summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cmake.conf5
-rw-r--r--.gitignore10
-rw-r--r--CMakeLists.txt4
-rw-r--r--LICENSE.GPL3-EXCEPT704
-rw-r--r--LICENSES/BSD-2-Clause.txt9
-rw-r--r--LICENSES/BSD-3-Clause.txt9
-rw-r--r--LICENSES/BSD-Source-Code.txt10
-rw-r--r--LICENSES/BSL-1.0.txt7
-rw-r--r--LICENSES/GFDL-1.3-no-invariants-only.txt (renamed from LICENSE.FDL)23
-rw-r--r--LICENSES/GPL-2.0-only.txt (renamed from LICENSE.GPL2)0
-rw-r--r--LICENSES/GPL-3.0-only.txt (renamed from LICENSE.GPL3)0
-rw-r--r--LICENSES/IJG.txt38
-rw-r--r--LICENSES/ISC.txt8
-rw-r--r--LICENSES/LGPL-2.1-or-later.txt175
-rw-r--r--LICENSES/LGPL-3.0-only.txt (renamed from LICENSE.LGPL3)0
-rw-r--r--LICENSES/LicenseRef-Qt-Commercial.txt8
-rw-r--r--LICENSES/MIT.txt9
-rw-r--r--LICENSES/MPL-2.0.txt373
-rw-r--r--LICENSES/Qt-GPL-exception-1.0.txt22
-rw-r--r--LICENSES/Zlib.txt11
-rw-r--r--cmake/FindAVFoundation.cmake3
-rw-r--r--cmake/FindFFmpeg.cmake274
-rw-r--r--cmake/FindGObject.cmake7
-rw-r--r--cmake/FindGStreamer.cmake9
-rw-r--r--cmake/FindMMRenderer.cmake3
-rw-r--r--cmake/FindMMRendererCore.cmake3
-rw-r--r--cmake/FindVAAPI.cmake3
-rw-r--r--cmake/FindWMF.cmake17
-rw-r--r--cmake/FindWrapPulseAudio.cmake5
-rw-r--r--coin/axivion/ci_config_linux.json45
-rw-r--r--coin/instructions/run_ffmpeg_backend_tests.yaml40
-rw-r--r--coin/module_config.yaml1
-rw-r--r--conanfile.py61
-rw-r--r--config.tests/alsa/CMakeLists.txt3
-rw-r--r--config.tests/alsa/alsatest.cpp29
-rw-r--r--config.tests/avfoundation/CMakeLists.txt3
-rw-r--r--config.tests/avfoundation/main.mm29
-rw-r--r--config.tests/evr/CMakeLists.txt3
-rw-r--r--config.tests/evr/main.cpp29
-rw-r--r--config.tests/gpu_vivante/CMakeLists.txt3
-rw-r--r--config.tests/gpu_vivante/main.cpp29
-rw-r--r--config.tests/gstreamer/CMakeLists.txt3
-rw-r--r--config.tests/gstreamer/main.cpp29
-rw-r--r--config.tests/gstreamer_appsrc/CMakeLists.txt3
-rw-r--r--config.tests/gstreamer_appsrc/main.cpp29
-rw-r--r--config.tests/gstreamer_photography/CMakeLists.txt3
-rw-r--r--config.tests/gstreamer_photography/main.cpp29
-rw-r--r--config.tests/linux_v4l/CMakeLists.txt3
-rw-r--r--config.tests/linux_v4l/main.cpp29
-rw-r--r--config.tests/mmrenderer/CMakeLists.txt3
-rw-r--r--config.tests/mmrenderer/mmrenderertest.cpp29
-rw-r--r--config.tests/pulseaudio/CMakeLists.txt3
-rw-r--r--config.tests/pulseaudio/pulseaudio.cpp29
-rw-r--r--config.tests/wmf/CMakeLists.txt3
-rw-r--r--config.tests/wmf/main.cpp29
-rw-r--r--config.tests/wmsdk/CMakeLists.txt3
-rw-r--r--config.tests/wmsdk/main.cpp29
-rw-r--r--configure.cmake3
-rw-r--r--dependencies.yaml9
-rw-r--r--examples/CMakeLists.txt7
-rw-r--r--examples/examples.pro2
-rw-r--r--examples/multimedia/CMakeLists.txt19
-rw-r--r--examples/multimedia/audiodecoder/audiodecoder.cpp213
-rw-r--r--examples/multimedia/audiodecoder/audiodecoder.h103
-rw-r--r--examples/multimedia/audiodecoder/main.cpp129
-rw-r--r--examples/multimedia/audiodevices/CMakeLists.txt3
-rw-r--r--examples/multimedia/audiodevices/audiodevices.cpp137
-rw-r--r--examples/multimedia/audiodevices/audiodevices.h62
-rw-r--r--examples/multimedia/audiodevices/doc/src/audiodevices.qdoc32
-rw-r--r--examples/multimedia/audiodevices/main.cpp55
-rw-r--r--examples/multimedia/audiooutput/CMakeLists.txt3
-rw-r--r--examples/multimedia/audiooutput/audiooutput.cpp92
-rw-r--r--examples/multimedia/audiooutput/audiooutput.h59
-rw-r--r--examples/multimedia/audiooutput/doc/src/audiooutput.qdoc32
-rw-r--r--examples/multimedia/audiooutput/main.cpp55
-rw-r--r--examples/multimedia/audiorecorder/CMakeLists.txt3
-rw-r--r--examples/multimedia/audiorecorder/audiolevel.cpp56
-rw-r--r--examples/multimedia/audiorecorder/audiolevel.h53
-rw-r--r--examples/multimedia/audiorecorder/audiorecorder.cpp218
-rw-r--r--examples/multimedia/audiorecorder/audiorecorder.h66
-rw-r--r--examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc32
-rw-r--r--examples/multimedia/audiorecorder/main.cpp53
-rw-r--r--examples/multimedia/audiosource/CMakeLists.txt4
-rw-r--r--examples/multimedia/audiosource/Info.plist.in44
-rw-r--r--examples/multimedia/audiosource/audioinput.pro13
-rw-r--r--examples/multimedia/audiosource/audiosource.cpp154
-rw-r--r--examples/multimedia/audiosource/audiosource.h55
-rw-r--r--examples/multimedia/audiosource/audiosource.pro13
-rw-r--r--examples/multimedia/audiosource/doc/src/audiosource.qdoc32
-rw-r--r--examples/multimedia/audiosource/main.cpp53
-rw-r--r--examples/multimedia/camera/CMakeLists.txt (renamed from examples/multimediawidgets/camera/CMakeLists.txt)5
-rw-r--r--examples/multimedia/camera/android/AndroidManifest.xml (renamed from examples/multimediawidgets/camera/android/AndroidManifest.xml)7
-rw-r--r--examples/multimedia/camera/camera.cpp (renamed from examples/multimediawidgets/camera/camera.cpp)220
-rw-r--r--examples/multimedia/camera/camera.h104
-rw-r--r--examples/multimedia/camera/camera.pro (renamed from examples/multimediawidgets/camera/camera.pro)2
-rw-r--r--examples/multimedia/camera/camera.qrc (renamed from examples/multimediawidgets/camera/camera.qrc)0
-rw-r--r--examples/multimedia/camera/camera.ui (renamed from examples/multimediawidgets/camera/camera.ui)0
-rw-r--r--examples/multimedia/camera/camera_mobile.ui (renamed from examples/multimediawidgets/camera/camera_mobile.ui)0
-rw-r--r--examples/multimedia/camera/doc/images/camera-example.pngbin0 -> 151290 bytes
-rw-r--r--examples/multimedia/camera/doc/src/camera.qdoc92
-rw-r--r--examples/multimedia/camera/images/shutter.svg (renamed from examples/multimediawidgets/camera/images/shutter.svg)0
-rw-r--r--examples/multimedia/camera/imagesettings.cpp84
-rw-r--r--examples/multimedia/camera/imagesettings.h41
-rw-r--r--examples/multimedia/camera/imagesettings.ui (renamed from examples/multimediawidgets/camera/imagesettings.ui)0
-rw-r--r--examples/multimedia/camera/ios/Info.plist.in (renamed from examples/multimediawidgets/camera/ios/Info.plist.in)0
-rw-r--r--examples/multimedia/camera/macos/Info.plist.in (renamed from examples/multimediawidgets/camera/macos/Info.plist.in)0
-rw-r--r--examples/multimedia/camera/main.cpp16
-rw-r--r--examples/multimedia/camera/metadatadialog.cpp105
-rw-r--r--examples/multimedia/camera/metadatadialog.h31
-rw-r--r--examples/multimedia/camera/videosettings.cpp196
-rw-r--r--examples/multimedia/camera/videosettings.h42
-rw-r--r--examples/multimedia/camera/videosettings.ui (renamed from examples/multimediawidgets/camera/videosettings.ui)0
-rw-r--r--examples/multimedia/camera/videosettings_mobile.ui (renamed from examples/multimediawidgets/camera/videosettings_mobile.ui)0
-rw-r--r--examples/multimedia/declarative-camera/CMakeLists.txt4
-rw-r--r--examples/multimedia/declarative-camera/CameraButton.qml51
-rw-r--r--examples/multimedia/declarative-camera/CameraListButton.qml68
-rw-r--r--examples/multimedia/declarative-camera/CameraListPopup.qml69
-rw-r--r--examples/multimedia/declarative-camera/CameraPropertyButton.qml69
-rw-r--r--examples/multimedia/declarative-camera/CameraPropertyPopup.qml69
-rw-r--r--examples/multimedia/declarative-camera/FlashControl.qml65
-rw-r--r--examples/multimedia/declarative-camera/Info.plist2
-rw-r--r--examples/multimedia/declarative-camera/PhotoCaptureControls.qml200
-rw-r--r--examples/multimedia/declarative-camera/PhotoPreview.qml52
-rw-r--r--examples/multimedia/declarative-camera/Popup.qml55
-rw-r--r--examples/multimedia/declarative-camera/VideoCaptureControls.qml220
-rw-r--r--examples/multimedia/declarative-camera/VideoPreview.qml53
-rw-r--r--examples/multimedia/declarative-camera/ZoomControl.qml54
-rw-r--r--examples/multimedia/declarative-camera/declarative-camera.qml114
-rw-r--r--examples/multimedia/declarative-camera/declarative-camera.qrc1
-rw-r--r--examples/multimedia/declarative-camera/doc/images/CaptureControls.pngbin0 -> 41278 bytes
-rw-r--r--examples/multimedia/declarative-camera/doc/images/FlashControls.pngbin0 -> 69725 bytes
-rw-r--r--examples/multimedia/declarative-camera/doc/images/VideoCaptureControls.pngbin0 -> 35913 bytes
-rw-r--r--examples/multimedia/declarative-camera/doc/images/ZoomControl.pngbin0 -> 28291 bytes
-rw-r--r--examples/multimedia/declarative-camera/doc/images/qml-camera.pngbin28409 -> 51620 bytes
-rw-r--r--examples/multimedia/declarative-camera/doc/images/qml-declarative-portrait.pngbin0 -> 49057 bytes
-rw-r--r--examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc183
-rw-r--r--examples/multimedia/declarative-camera/permission-denied.qml20
-rw-r--r--examples/multimedia/declarative-camera/qmlcamera.cpp89
-rw-r--r--examples/multimedia/devices/main.cpp183
-rw-r--r--examples/multimedia/multimedia.pro13
-rw-r--r--examples/multimedia/player/CMakeLists.txt (renamed from examples/multimediawidgets/player/CMakeLists.txt)10
-rw-r--r--examples/multimedia/player/doc/images/mediaplayerex.jpg (renamed from examples/multimediawidgets/player/doc/images/mediaplayerex.jpg)bin28825 -> 28825 bytes
-rw-r--r--examples/multimedia/player/doc/src/player.qdoc (renamed from examples/multimediawidgets/player/doc/src/player.qdoc)31
-rw-r--r--examples/multimedia/player/main.cpp37
-rw-r--r--examples/multimedia/player/player.cpp (renamed from examples/multimediawidgets/player/player.cpp)170
-rw-r--r--examples/multimedia/player/player.h99
-rw-r--r--examples/multimedia/player/player.pro (renamed from examples/multimediawidgets/player/player.pro)5
-rw-r--r--examples/multimedia/player/playercontrols.cpp (renamed from examples/multimediawidgets/player/playercontrols.cpp)85
-rw-r--r--examples/multimedia/player/playercontrols.h62
-rw-r--r--examples/multimedia/player/playlistmodel.cpp105
-rw-r--r--examples/multimedia/player/playlistmodel.h50
-rw-r--r--examples/multimedia/player/qmediaplaylist.cpp (renamed from examples/multimediawidgets/player/qmediaplaylist.cpp)88
-rw-r--r--examples/multimedia/player/qmediaplaylist.h (renamed from examples/multimediawidgets/player/qmediaplaylist.h)57
-rw-r--r--examples/multimedia/player/qmediaplaylist_p.cpp65
-rw-r--r--examples/multimedia/player/qmediaplaylist_p.h66
-rw-r--r--examples/multimedia/player/qplaylistfileparser.cpp (renamed from examples/multimediawidgets/player/qplaylistfileparser.cpp)250
-rw-r--r--examples/multimedia/player/qplaylistfileparser.h76
-rw-r--r--examples/multimedia/player/videowidget.cpp46
-rw-r--r--examples/multimedia/player/videowidget.h22
-rw-r--r--examples/multimedia/screencapture/CMakeLists.txt50
-rw-r--r--examples/multimedia/screencapture/Info.plist.in41
-rw-r--r--examples/multimedia/screencapture/doc/images/screencapture.jpgbin0 -> 42725 bytes
-rw-r--r--examples/multimedia/screencapture/doc/src/screencapture.qdoc48
-rw-r--r--examples/multimedia/screencapture/main.cpp14
-rw-r--r--examples/multimedia/screencapture/screencapture.pro18
-rw-r--r--examples/multimedia/screencapture/screencapturepreview.cpp179
-rw-r--r--examples/multimedia/screencapture/screencapturepreview.h67
-rw-r--r--examples/multimedia/screencapture/screenlistmodel.cpp54
-rw-r--r--examples/multimedia/screencapture/screenlistmodel.h30
-rw-r--r--examples/multimedia/screencapture/windowlistmodel.cpp42
-rw-r--r--examples/multimedia/screencapture/windowlistmodel.h31
-rw-r--r--examples/multimedia/shared/shared.pri4
-rw-r--r--examples/multimedia/spatialaudio/doc/src/spatialaudio.qdoc51
-rw-r--r--examples/multimedia/spatialaudio/main.cpp208
-rw-r--r--examples/multimedia/spectrum/CMakeLists.txt4
-rw-r--r--examples/multimedia/spectrum/Info.plist.in43
-rw-r--r--examples/multimedia/spectrum/app.pro3
-rw-r--r--examples/multimedia/spectrum/doc/src/spectrum.qdoc31
-rw-r--r--examples/multimedia/spectrum/engine.cpp329
-rw-r--r--examples/multimedia/spectrum/engine.h140
-rw-r--r--examples/multimedia/spectrum/frequencyspectrum.cpp59
-rw-r--r--examples/multimedia/spectrum/frequencyspectrum.h67
-rw-r--r--examples/multimedia/spectrum/levelmeter.cpp83
-rw-r--r--examples/multimedia/spectrum/levelmeter.h54
-rw-r--r--examples/multimedia/spectrum/main.cpp52
-rw-r--r--examples/multimedia/spectrum/mainwidget.cpp213
-rw-r--r--examples/multimedia/spectrum/mainwidget.h120
-rw-r--r--examples/multimedia/spectrum/progressbar.cpp66
-rw-r--r--examples/multimedia/spectrum/progressbar.h53
-rw-r--r--examples/multimedia/spectrum/settingsdialog.cpp99
-rw-r--r--examples/multimedia/spectrum/settingsdialog.h59
-rw-r--r--examples/multimedia/spectrum/spectrograph.cpp91
-rw-r--r--examples/multimedia/spectrum/spectrograph.h72
-rw-r--r--examples/multimedia/spectrum/spectrum.h102
-rw-r--r--examples/multimedia/spectrum/spectrumanalyser.cpp150
-rw-r--r--examples/multimedia/spectrum/spectrumanalyser.h113
-rw-r--r--examples/multimedia/spectrum/tonegenerator.cpp60
-rw-r--r--examples/multimedia/spectrum/tonegenerator.h55
-rw-r--r--examples/multimedia/spectrum/tonegeneratordialog.cpp94
-rw-r--r--examples/multimedia/spectrum/tonegeneratordialog.h55
-rw-r--r--examples/multimedia/spectrum/utils.cpp69
-rw-r--r--examples/multimedia/spectrum/utils.h94
-rw-r--r--examples/multimedia/spectrum/waveform.cpp179
-rw-r--r--examples/multimedia/spectrum/waveform.h97
-rw-r--r--examples/multimedia/video/CMakeLists.txt3
-rw-r--r--examples/multimedia/video/mediaplayer/AudioControl.qml92
-rw-r--r--examples/multimedia/video/mediaplayer/CMakeLists.txt60
-rw-r--r--examples/multimedia/video/mediaplayer/Main.qml178
-rw-r--r--examples/multimedia/video/mediaplayer/MetadataInfo.qml116
-rw-r--r--examples/multimedia/video/mediaplayer/PlaybackControl.qml197
-rw-r--r--examples/multimedia/video/mediaplayer/PlaybackRateControl.qml80
-rw-r--r--examples/multimedia/video/mediaplayer/PlaybackSeekControl.qml89
-rw-r--r--examples/multimedia/video/mediaplayer/PlayerMenuBar.qml171
-rw-r--r--examples/multimedia/video/mediaplayer/TracksInfo.qml122
-rw-r--r--examples/multimedia/video/mediaplayer/controls/AudioControl.qml50
-rw-r--r--examples/multimedia/video/mediaplayer/controls/MetadataInfo.qml30
-rw-r--r--examples/multimedia/video/mediaplayer/controls/PlaybackControl.qml318
-rw-r--r--examples/multimedia/video/mediaplayer/controls/PlaybackSeekControl.qml56
-rw-r--r--examples/multimedia/video/mediaplayer/controls/SettingsPopup.qml204
-rw-r--r--examples/multimedia/video/mediaplayer/controls/TracksInfo.qml25
-rw-r--r--examples/multimedia/video/mediaplayer/controls/UrlPopup.qml54
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/OqosZsDqvzQ.jpgbin181335 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/PlayerMenuBar.gifbin65177 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/architecture-overview.gifbin222405 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/audio-control.gifbin150039 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/mediaplayer.pngbin0 -> 49506 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/meta-data.pngbin66670 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/nHrBbW0H-pc.jpgbin84480 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/play-pause-stop.gifbin188432 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/playbackControlPanel.gifbin179494 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/qmlmediaplayer.jpgbin67156 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/settings.pngbin0 -> 33705 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/sf_yv01UtIw.jpgbin96819 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/images/url.pngbin68983 -> 0 bytes
-rw-r--r--examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf7
-rw-r--r--examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc325
-rw-r--r--examples/multimedia/video/mediaplayer/images/backward10.svg5
-rw-r--r--examples/multimedia/video/mediaplayer/images/ff.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/forward10.svg5
-rw-r--r--examples/multimedia/video/mediaplayer/images/link.svg3
-rw-r--r--examples/multimedia/video/mediaplayer/images/loop.svg3
-rw-r--r--examples/multimedia/video/mediaplayer/images/more.svg5
-rw-r--r--examples/multimedia/video/mediaplayer/images/mute.svg (renamed from examples/multimedia/video/mediaplayer/Mute_Icon.svg)0
-rw-r--r--examples/multimedia/video/mediaplayer/images/open_new.svg6
-rw-r--r--examples/multimedia/video/mediaplayer/images/pause_symbol.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/play_symbol.svg3
-rw-r--r--examples/multimedia/video/mediaplayer/images/rewind.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/settings.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/speaker.svg (renamed from examples/multimedia/video/mediaplayer/Speaker_Icon.svg)0
-rw-r--r--examples/multimedia/video/mediaplayer/images/stop_symbol.svg3
-rw-r--r--examples/multimedia/video/mediaplayer/images/url.svg6
-rw-r--r--examples/multimedia/video/mediaplayer/images/volume.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/volume_mute.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/zoom_maximize.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/images/zoom_minimize.svg4
-rw-r--r--examples/multimedia/video/mediaplayer/main.cpp68
-rw-r--r--examples/multimedia/video/mediaplayer/main.qml200
-rw-r--r--examples/multimedia/video/qmlvideo/CMakeLists.txt96
-rw-r--r--examples/multimedia/video/qmlvideo/doc/images/qmlvideo-menu.jpg (renamed from examples/multimedia/video/doc/images/qmlvideo-menu.jpg)bin21959 -> 21959 bytes
-rw-r--r--examples/multimedia/video/qmlvideo/doc/images/qmlvideo-overlay.jpg (renamed from examples/multimedia/video/doc/images/qmlvideo-overlay.jpg)bin23787 -> 23787 bytes
-rw-r--r--examples/multimedia/video/qmlvideo/doc/src/qmlvideo.qdoc (renamed from examples/multimedia/video/doc/src/qmlvideo.qdoc)50
-rw-r--r--examples/multimedia/video/qmlvideo/frequencymonitor.cpp108
-rw-r--r--examples/multimedia/video/qmlvideo/frequencymonitor.h62
-rw-r--r--examples/multimedia/video/qmlvideo/frequencymonitor/CMakeLists.txt30
-rw-r--r--examples/multimedia/video/qmlvideo/frequencymonitor/FrequencyItem.qml67
-rw-r--r--examples/multimedia/video/qmlvideo/frequencymonitor/qmldir3
-rw-r--r--examples/multimedia/video/qmlvideo/frequencymonitordeclarative.cpp53
-rw-r--r--examples/multimedia/video/qmlvideo/main.cpp126
-rw-r--r--examples/multimedia/video/qmlvideo/performancemonitor.cpp51
-rw-r--r--examples/multimedia/video/qmlvideo/performancemonitor.h60
-rw-r--r--examples/multimedia/video/qmlvideo/performancemonitor/CMakeLists.txt31
-rw-r--r--examples/multimedia/video/qmlvideo/performancemonitor/PerformanceItem.qml100
-rw-r--r--examples/multimedia/video/qmlvideo/performancemonitor/qmldir3
-rw-r--r--examples/multimedia/video/qmlvideo/performancemonitordeclarative.cpp60
-rw-r--r--examples/multimedia/video/qmlvideo/performancemonitordeclarative.h55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/frequencymonitor/FrequencyItem.qml114
-rw-r--r--examples/multimedia/video/qmlvideo/qml/performancemonitor/PerformanceItem.qml150
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/Button.qml90
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraBasic.qml56
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraDrag.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraDummy.qml78
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraFullScreen.qml56
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraFullScreenInverted.qml56
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraItem.qml94
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraMove.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraOverlay.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraResize.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraRotate.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraSpin.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/Content.qml165
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/ErrorDialog.qml118
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml419
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml83
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml94
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneDrag.qml81
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreen.qml112
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreenInverted.qml117
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMove.qml96
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneOverlay.qml130
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneResize.qml88
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneRotate.qml109
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml151
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSpin.qml81
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoDrag.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoDummy.qml84
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFillMode.qml95
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFullScreen.qml56
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFullScreenInverted.qml56
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoItem.qml89
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMetadata.qml137
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMove.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoOverlay.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoPlaybackRate.qml115
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoResize.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoRotate.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSeek.qml81
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSpin.qml55
-rw-r--r--examples/multimedia/video/qmlvideo/qml/qmlvideo/main.qml285
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo.pro119
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CMakeLists.txt81
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraBasic.qml7
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraDrag.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraDummy.qml31
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreen.qml7
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreenInverted.qml7
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraItem.qml47
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraMove.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraOverlay.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraResize.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraRotate.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/CameraSpin.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/Content.qml125
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/ErrorDialog.qml70
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/Main.qml188
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/Scene.qml31
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneBasic.qml47
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneDrag.qml34
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreen.qml67
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreenInverted.qml70
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneMove.qml49
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneMulti.qml (renamed from examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMulti.qml)106
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneOverlay.qml83
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneResize.qml41
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneRotate.qml57
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneSelectionPanel.qml88
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SceneSpin.qml34
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/SeekControl.qml (renamed from examples/multimedia/video/qmlvideo/qml/qmlvideo/SeekControl.qml)69
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoBasic.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoDrag.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoDummy.qml37
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoFillMode.qml49
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreen.qml7
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreenInverted.qml7
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoItem.qml42
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoMetadata.qml80
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoMove.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoOverlay.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoPlaybackRate.qml65
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoResize.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoRotate.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoSeek.qml36
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/VideoSpin.qml6
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/images/folder.png (renamed from examples/multimedia/video/qmlvideo/images/folder.png)bin1829 -> 1829 bytes
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/images/leaves.jpg (renamed from examples/multimedia/video/qmlvideo/images/leaves.jpg)bin257378 -> 257378 bytes
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/images/up.png (renamed from examples/multimedia/video/qmlvideo/images/up.png)bin1268 -> 1268 bytes
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/qmldir44
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/qmlvideo_global.h10
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.cpp54
-rw-r--r--examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.h48
-rw-r--r--examples/multimedia/video/qmlvideo/trace.h100
-rw-r--r--examples/multimedia/video/recorder/AudioInputSelect.qml81
-rw-r--r--examples/multimedia/video/recorder/CMakeLists.txt6
-rw-r--r--examples/multimedia/video/recorder/CameraSelect.qml85
-rw-r--r--examples/multimedia/video/recorder/Controls.qml66
-rw-r--r--examples/multimedia/video/recorder/MediaList.qml51
-rw-r--r--examples/multimedia/video/recorder/Playback.qml51
-rw-r--r--examples/multimedia/video/recorder/RecordButton.qml51
-rw-r--r--examples/multimedia/video/recorder/SettingsEncoder.qml56
-rw-r--r--examples/multimedia/video/recorder/SettingsMetaData.qml71
-rw-r--r--examples/multimedia/video/recorder/Style.qml51
-rw-r--r--examples/multimedia/video/recorder/StyleParameter.qml53
-rw-r--r--examples/multimedia/video/recorder/StyleRectangle.qml51
-rw-r--r--examples/multimedia/video/recorder/StyleSlider.qml53
-rw-r--r--examples/multimedia/video/recorder/VideoSourceSelect.qml165
-rw-r--r--examples/multimedia/video/recorder/doc/images/qml-recorder-control-bar-overview.gifbin0 -> 111693 bytes
-rw-r--r--examples/multimedia/video/recorder/doc/images/qml-recorder-overview.gifbin0 -> 138905 bytes
-rw-r--r--examples/multimedia/video/recorder/doc/src/recorder.qdoc118
-rw-r--r--examples/multimedia/video/recorder/main.cpp91
-rw-r--r--examples/multimedia/video/recorder/main.qml59
-rw-r--r--examples/multimedia/video/recorder/main_no_permissions.qml26
-rw-r--r--examples/multimedia/videographicsitem/CMakeLists.txt (renamed from examples/multimediawidgets/videographicsitem/CMakeLists.txt)5
-rw-r--r--examples/multimedia/videographicsitem/doc/images/video-videographicsitem.png (renamed from examples/multimediawidgets/videographicsitem/doc/images/video-videographicsitem.png)bin54436 -> 54436 bytes
-rw-r--r--examples/multimedia/videographicsitem/doc/src/videographicsitem.qdoc20
-rw-r--r--examples/multimedia/videographicsitem/main.cpp37
-rw-r--r--examples/multimedia/videographicsitem/videographicsitem.pro (renamed from examples/multimediawidgets/videographicsitem/videographicsitem.pro)2
-rw-r--r--examples/multimedia/videographicsitem/videoplayer.cpp (renamed from examples/multimediawidgets/videographicsitem/videoplayer.cpp)96
-rw-r--r--examples/multimedia/videographicsitem/videoplayer.h47
-rw-r--r--examples/multimedia/videowidget/CMakeLists.txt (renamed from examples/multimediawidgets/videowidget/CMakeLists.txt)5
-rw-r--r--examples/multimedia/videowidget/doc/images/video-videowidget.png (renamed from examples/multimediawidgets/videowidget/doc/images/video-videowidget.png)bin54199 -> 54199 bytes
-rw-r--r--examples/multimedia/videowidget/doc/src/videowidget.qdoc18
-rw-r--r--examples/multimedia/videowidget/main.cpp40
-rw-r--r--examples/multimedia/videowidget/videoplayer.cpp (renamed from examples/multimediawidgets/videowidget/videoplayer.cpp)90
-rw-r--r--examples/multimedia/videowidget/videoplayer.h44
-rw-r--r--examples/multimedia/videowidget/videowidget.pro (renamed from examples/multimediawidgets/videowidget/videowidget.pro)2
-rw-r--r--examples/multimediawidgets/CMakeLists.txt6
-rw-r--r--examples/multimediawidgets/camera/camera.h148
-rw-r--r--examples/multimediawidgets/camera/doc/images/camera-example.pngbin13647 -> 0 bytes
-rw-r--r--examples/multimediawidgets/camera/doc/src/camera.qdoc82
-rw-r--r--examples/multimediawidgets/camera/imagesettings.cpp130
-rw-r--r--examples/multimediawidgets/camera/imagesettings.h86
-rw-r--r--examples/multimediawidgets/camera/main.cpp63
-rw-r--r--examples/multimediawidgets/camera/metadatadialog.cpp127
-rw-r--r--examples/multimediawidgets/camera/metadatadialog.h78
-rw-r--r--examples/multimediawidgets/camera/videosettings.cpp248
-rw-r--r--examples/multimediawidgets/camera/videosettings.h85
-rw-r--r--examples/multimediawidgets/multimediawidgets.pro10
-rw-r--r--examples/multimediawidgets/player/main.cpp84
-rw-r--r--examples/multimediawidgets/player/player.h146
-rw-r--r--examples/multimediawidgets/player/playercontrols.h109
-rw-r--r--examples/multimediawidgets/player/playlistmodel.cpp149
-rw-r--r--examples/multimediawidgets/player/playlistmodel.h99
-rw-r--r--examples/multimediawidgets/player/qmediaplaylist_p.h148
-rw-r--r--examples/multimediawidgets/player/qplaylistfileparser_p.h116
-rw-r--r--examples/multimediawidgets/player/videowidget.cpp93
-rw-r--r--examples/multimediawidgets/player/videowidget.h69
-rw-r--r--examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc43
-rw-r--r--examples/multimediawidgets/videographicsitem/main.cpp86
-rw-r--r--examples/multimediawidgets/videographicsitem/videoplayer.h95
-rw-r--r--examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc41
-rw-r--r--examples/multimediawidgets/videowidget/main.cpp88
-rw-r--r--examples/multimediawidgets/videowidget/videoplayer.h91
-rw-r--r--examples/spatialaudio/CMakeLists.txt1
-rw-r--r--examples/spatialaudio/audiopanning/CMakeLists.txt (renamed from examples/multimedia/spatialaudio/CMakeLists.txt)17
-rw-r--r--examples/spatialaudio/audiopanning/audiopanning.pro9
-rw-r--r--examples/spatialaudio/audiopanning/doc/images/audiopanning-example.png (renamed from examples/multimedia/spatialaudio/doc/images/spatialaudio-example.png)bin171760 -> 171760 bytes
-rw-r--r--examples/spatialaudio/audiopanning/doc/src/audiopanning.qdoc28
-rw-r--r--examples/spatialaudio/audiopanning/main.cpp241
-rw-r--r--examples/spatialaudio/spatialaudio.pro3
-rw-r--r--licenseRule.json109
-rw-r--r--src/3rdparty/eigen/Eigen/src/Core/arch/NEON/GeneralBlockPanelKernel.h2
-rw-r--r--src/3rdparty/eigen/Eigen/src/Core/arch/NEON/PacketMath.h8
-rw-r--r--src/3rdparty/eigen/qt_attribution.json34
-rw-r--r--src/3rdparty/ffmpeg/qt_attribution.json55
-rw-r--r--src/3rdparty/pffft/fftpack.c173
-rw-r--r--src/3rdparty/pffft/pffft.c50
-rw-r--r--src/3rdparty/pffft/qt_attribution.json30
-rw-r--r--src/3rdparty/pffft/test_pffft.c4
-rw-r--r--src/3rdparty/resonance-audio/patches/0001-resonance_audio-fix-C-20-build.patch36
-rw-r--r--src/3rdparty/resonance-audio/qt_attribution.json32
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h6
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc7
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h9
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc6
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h5
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc5
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h2
-rw-r--r--src/CMakeLists.txt8
-rw-r--r--src/android/CMakeLists.txt3
-rw-r--r--src/android/jar/CMakeLists.txt10
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java65
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java142
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java526
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java133
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtExifDataHandler.java51
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java40
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java40
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java40
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java40
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java40
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java229
-rw-r--r--src/multimedia/CMakeLists.txt96
-rw-r--r--src/multimedia/alsa/qalsaaudiodevice.cpp40
-rw-r--r--src/multimedia/alsa/qalsaaudiodevice_p.h40
-rw-r--r--src/multimedia/alsa/qalsaaudiosink.cpp96
-rw-r--r--src/multimedia/alsa/qalsaaudiosink_p.h82
-rw-r--r--src/multimedia/alsa/qalsaaudiosource.cpp63
-rw-r--r--src/multimedia/alsa/qalsaaudiosource_p.h42
-rw-r--r--src/multimedia/alsa/qalsamediadevices.cpp150
-rw-r--r--src/multimedia/alsa/qalsamediadevices_p.h47
-rw-r--r--src/multimedia/android/qandroidaudiodevice.cpp40
-rw-r--r--src/multimedia/android/qandroidaudiodevice_p.h40
-rw-r--r--src/multimedia/android/qandroidaudiosink.cpp144
-rw-r--r--src/multimedia/android/qandroidaudiosink_p.h85
-rw-r--r--src/multimedia/android/qandroidaudiosource.cpp73
-rw-r--r--src/multimedia/android/qandroidaudiosource_p.h42
-rw-r--r--src/multimedia/android/qandroidmediadevices.cpp84
-rw-r--r--src/multimedia/android/qandroidmediadevices_p.h47
-rw-r--r--src/multimedia/android/qopenslesengine.cpp81
-rw-r--r--src/multimedia/android/qopenslesengine_p.h41
-rw-r--r--src/multimedia/audio/qaudio.h60
-rw-r--r--src/multimedia/audio/qaudiobuffer.cpp125
-rw-r--r--src/multimedia/audio/qaudiobuffer.h42
-rw-r--r--src/multimedia/audio/qaudiodecoder.cpp174
-rw-r--r--src/multimedia/audio/qaudiodecoder.h48
-rw-r--r--src/multimedia/audio/qaudiodecoder_p.h40
-rw-r--r--src/multimedia/audio/qaudiodevice.cpp92
-rw-r--r--src/multimedia/audio/qaudiodevice.h42
-rw-r--r--src/multimedia/audio/qaudiodevice_p.h56
-rw-r--r--src/multimedia/audio/qaudioformat.cpp103
-rw-r--r--src/multimedia/audio/qaudioformat.h42
-rw-r--r--src/multimedia/audio/qaudiohelpers.cpp42
-rw-r--r--src/multimedia/audio/qaudiohelpers_p.h40
-rw-r--r--src/multimedia/audio/qaudioinput.cpp73
-rw-r--r--src/multimedia/audio/qaudioinput.h42
-rw-r--r--src/multimedia/audio/qaudiooutput.cpp70
-rw-r--r--src/multimedia/audio/qaudiooutput.h42
-rw-r--r--src/multimedia/audio/qaudiosink.cpp106
-rw-r--r--src/multimedia/audio/qaudiosink.h51
-rw-r--r--src/multimedia/audio/qaudiosource.cpp98
-rw-r--r--src/multimedia/audio/qaudiosource.h51
-rw-r--r--src/multimedia/audio/qaudiostatemachine.cpp150
-rw-r--r--src/multimedia/audio/qaudiostatemachine_p.h149
-rw-r--r--src/multimedia/audio/qaudiostatemachineutils_p.h96
-rw-r--r--src/multimedia/audio/qaudiosystem.cpp254
-rw-r--r--src/multimedia/audio/qaudiosystem_p.h67
-rw-r--r--src/multimedia/audio/qsamplecache_p.cpp73
-rw-r--r--src/multimedia/audio/qsamplecache_p.h40
-rw-r--r--src/multimedia/audio/qsoundeffect.cpp206
-rw-r--r--src/multimedia/audio/qsoundeffect.h40
-rw-r--r--src/multimedia/audio/qtaudio.cpp (renamed from src/multimedia/audio/qaudio.cpp)60
-rw-r--r--src/multimedia/audio/qtaudio.h18
-rw-r--r--src/multimedia/audio/qwavedecoder.cpp75
-rw-r--r--src/multimedia/audio/qwavedecoder.h46
-rw-r--r--src/multimedia/camera/qcamera.cpp182
-rw-r--r--src/multimedia/camera/qcamera.h47
-rw-r--r--src/multimedia/camera/qcamera_p.h52
-rw-r--r--src/multimedia/camera/qcameradevice.cpp109
-rw-r--r--src/multimedia/camera/qcameradevice.h43
-rw-r--r--src/multimedia/camera/qcameradevice_p.h66
-rw-r--r--src/multimedia/camera/qimagecapture.cpp155
-rw-r--r--src/multimedia/camera/qimagecapture.h40
-rw-r--r--src/multimedia/compat/removed_api.cpp16
-rw-r--r--src/multimedia/configure.cmake26
-rw-r--r--src/multimedia/darwin/qcoreaudiosessionmanager.mm40
-rw-r--r--src/multimedia/darwin/qcoreaudiosessionmanager_p.h40
-rw-r--r--src/multimedia/darwin/qcoreaudioutils.mm72
-rw-r--r--src/multimedia/darwin/qcoreaudioutils_p.h42
-rw-r--r--src/multimedia/darwin/qdarwinaudiodevice.mm133
-rw-r--r--src/multimedia/darwin/qdarwinaudiodevice_p.h40
-rw-r--r--src/multimedia/darwin/qdarwinaudiosink.mm349
-rw-r--r--src/multimedia/darwin/qdarwinaudiosink_p.h102
-rw-r--r--src/multimedia/darwin/qdarwinaudiosource.mm61
-rw-r--r--src/multimedia/darwin/qdarwinaudiosource_p.h44
-rw-r--r--src/multimedia/darwin/qdarwinmediadevices.mm340
-rw-r--r--src/multimedia/darwin/qdarwinmediadevices_p.h59
-rw-r--r--src/multimedia/darwin/qmacosaudiodatautils_p.h112
-rw-r--r--src/multimedia/doc/QtMultimediaDoc7
-rw-r--r--src/multimedia/doc/qtmultimedia.qdocconf33
-rw-r--r--src/multimedia/doc/snippets/CMakeLists.txt3
-rw-r--r--src/multimedia/doc/snippets/doc_src_qtmultimedia.cpp53
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/audio.cpp76
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/camera.cpp42
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/devices.cpp38
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/media.cpp42
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/multiple-videooutputs.qml40
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/qsound.cpp51
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/qtvideosink.qml40
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/soundeffect.qml40
-rw-r--r--src/multimedia/doc/snippets/multimedia-snippets/video.cpp40
-rw-r--r--src/multimedia/doc/src/audiooverview.qdoc53
-rw-r--r--src/multimedia/doc/src/cameraoverview.qdoc41
-rw-r--r--src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc6
-rw-r--r--src/multimedia/doc/src/images/camera_correctionAngle_90.pngbin0 -> 48562 bytes
-rw-r--r--src/multimedia/doc/src/multimedia-overview.qdoc83
-rw-r--r--src/multimedia/doc/src/platform-notes-apple.qdoc40
-rw-r--r--src/multimedia/doc/src/platform-notes-wasm.qdoc35
-rw-r--r--src/multimedia/doc/src/qm-external-pages.qdoc42
-rw-r--r--src/multimedia/doc/src/qt6-changes.qdoc28
-rw-r--r--src/multimedia/doc/src/qtmultimedia-building-from-source.qdoc94
-rw-r--r--src/multimedia/doc/src/qtmultimedia-cpp.qdoc41
-rw-r--r--src/multimedia/doc/src/qtmultimedia-examples.qdoc29
-rw-r--r--src/multimedia/doc/src/qtmultimedia-index.qdoc200
-rw-r--r--src/multimedia/doc/src/qtmultimedia-qml-types.qdoc47
-rw-r--r--src/multimedia/doc/src/spatialaudiooverview.qdoc96
-rw-r--r--src/multimedia/doc/src/videooverview.qdoc29
-rw-r--r--src/multimedia/platform/qplatformaudiodecoder.cpp184
-rw-r--r--src/multimedia/platform/qplatformaudiodecoder_p.h49
-rw-r--r--src/multimedia/platform/qplatformaudioinput_p.h46
-rw-r--r--src/multimedia/platform/qplatformaudiooutput_p.h44
-rw-r--r--src/multimedia/platform/qplatformaudioresampler_p.h33
-rw-r--r--src/multimedia/platform/qplatformcamera.cpp119
-rw-r--r--src/multimedia/platform/qplatformcamera_p.h68
-rw-r--r--src/multimedia/platform/qplatformcapturablewindows_p.h44
-rw-r--r--src/multimedia/platform/qplatformimagecapture.cpp164
-rw-r--r--src/multimedia/platform/qplatformimagecapture_p.h40
-rw-r--r--src/multimedia/platform/qplatformmediacapture.cpp80
-rw-r--r--src/multimedia/platform/qplatformmediacapture_p.h60
-rw-r--r--src/multimedia/platform/qplatformmediadevices.cpp156
-rw-r--r--src/multimedia/platform/qplatformmediadevices_p.h89
-rw-r--r--src/multimedia/platform/qplatformmediaformatinfo.cpp40
-rw-r--r--src/multimedia/platform/qplatformmediaformatinfo_p.h40
-rw-r--r--src/multimedia/platform/qplatformmediaintegration.cpp266
-rw-r--r--src/multimedia/platform/qplatformmediaintegration_p.h124
-rw-r--r--src/multimedia/platform/qplatformmediaplayer.cpp354
-rw-r--r--src/multimedia/platform/qplatformmediaplayer_p.h73
-rw-r--r--src/multimedia/platform/qplatformmediaplugin.cpp14
-rw-r--r--src/multimedia/platform/qplatformmediaplugin_p.h46
-rw-r--r--src/multimedia/platform/qplatformmediarecorder.cpp177
-rw-r--r--src/multimedia/platform/qplatformmediarecorder_p.h60
-rw-r--r--src/multimedia/platform/qplatformsurfacecapture.cpp83
-rw-r--r--src/multimedia/platform/qplatformsurfacecapture_p.h88
-rw-r--r--src/multimedia/platform/qplatformvideodevices.cpp53
-rw-r--r--src/multimedia/platform/qplatformvideodevices_p.h51
-rw-r--r--src/multimedia/platform/qplatformvideosink.cpp141
-rw-r--r--src/multimedia/platform/qplatformvideosink_p.h99
-rw-r--r--src/multimedia/platform/qplatformvideosource.cpp15
-rw-r--r--src/multimedia/platform/qplatformvideosource_p.h53
-rw-r--r--src/multimedia/playback/qmediaplayer.cpp301
-rw-r--r--src/multimedia/playback/qmediaplayer.h49
-rw-r--r--src/multimedia/playback/qmediaplayer_p.h55
-rw-r--r--src/multimedia/pulseaudio/qaudioengine_pulse.cpp413
-rw-r--r--src/multimedia/pulseaudio/qaudioengine_pulse_p.h40
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiodevice.cpp40
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiodevice_p.h42
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiomediadevices.cpp62
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiomediadevices_p.h47
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiosink.cpp518
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiosink_p.h71
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiosource.cpp448
-rw-r--r--src/multimedia/pulseaudio/qpulseaudiosource_p.h60
-rw-r--r--src/multimedia/pulseaudio/qpulsehelpers.cpp122
-rw-r--r--src/multimedia/pulseaudio/qpulsehelpers_p.h111
-rw-r--r--src/multimedia/qerrorinfo_p.h57
-rw-r--r--src/multimedia/qmaybe_p.h96
-rw-r--r--src/multimedia/qmediadevices.cpp90
-rw-r--r--src/multimedia/qmediadevices.h43
-rw-r--r--src/multimedia/qmediaenumdebug.h40
-rw-r--r--src/multimedia/qmediaformat.cpp91
-rw-r--r--src/multimedia/qmediaformat.h40
-rw-r--r--src/multimedia/qmediametadata.cpp86
-rw-r--r--src/multimedia/qmediametadata.h44
-rw-r--r--src/multimedia/qmediastoragelocation.cpp66
-rw-r--r--src/multimedia/qmediastoragelocation_p.h40
-rw-r--r--src/multimedia/qmediatimerange.cpp60
-rw-r--r--src/multimedia/qmediatimerange.h40
-rw-r--r--src/multimedia/qmultimediautils.cpp135
-rw-r--r--src/multimedia/qmultimediautils_p.h81
-rw-r--r--src/multimedia/qnx/qqnxaudiodevice.cpp42
-rw-r--r--src/multimedia/qnx/qqnxaudiodevice_p.h42
-rw-r--r--src/multimedia/qnx/qqnxaudiosink.cpp66
-rw-r--r--src/multimedia/qnx/qqnxaudiosink_p.h45
-rw-r--r--src/multimedia/qnx/qqnxaudiosource.cpp47
-rw-r--r--src/multimedia/qnx/qqnxaudiosource_p.h42
-rw-r--r--src/multimedia/qnx/qqnxaudioutils.cpp40
-rw-r--r--src/multimedia/qnx/qqnxaudioutils_p.h40
-rw-r--r--src/multimedia/qnx/qqnxmediadevices.cpp50
-rw-r--r--src/multimedia/qnx/qqnxmediadevices_p.h46
-rw-r--r--src/multimedia/qt_cmdline.cmake3
-rw-r--r--src/multimedia/qtmultimediaglobal.h40
-rw-r--r--src/multimedia/qtmultimediaglobal_p.h40
-rw-r--r--src/multimedia/recording/qcapturablewindow.cpp156
-rw-r--r--src/multimedia/recording/qcapturablewindow.h66
-rw-r--r--src/multimedia/recording/qcapturablewindow_p.h41
-rw-r--r--src/multimedia/recording/qmediacapturesession.cpp277
-rw-r--r--src/multimedia/recording/qmediacapturesession.h56
-rw-r--r--src/multimedia/recording/qmediacapturesession_p.h44
-rw-r--r--src/multimedia/recording/qmediarecorder.cpp231
-rw-r--r--src/multimedia/recording/qmediarecorder.h52
-rw-r--r--src/multimedia/recording/qmediarecorder_p.h41
-rw-r--r--src/multimedia/recording/qscreencapture-limitations.qdocinc22
-rw-r--r--src/multimedia/recording/qscreencapture.cpp261
-rw-r--r--src/multimedia/recording/qscreencapture.h72
-rw-r--r--src/multimedia/recording/qwindowcapture.cpp268
-rw-r--r--src/multimedia/recording/qwindowcapture.h71
-rw-r--r--src/multimedia/shaders/ayuv.frag4
-rw-r--r--src/multimedia/shaders/colortransfer.glsl18
-rwxr-xr-xsrc/multimedia/shaders/compile.bat38
-rw-r--r--src/multimedia/shaders/imc2.frag8
-rw-r--r--src/multimedia/shaders/imc4.frag4
-rw-r--r--src/multimedia/shaders/nv12.frag4
-rw-r--r--src/multimedia/shaders/nv21.frag4
-rw-r--r--src/multimedia/shaders/uyvy.frag4
-rw-r--r--src/multimedia/shaders/y.frag4
-rw-r--r--src/multimedia/shaders/yuv_triplanar.frag4
-rw-r--r--src/multimedia/shaders/yuv_triplanar_p10.frag4
-rw-r--r--src/multimedia/shaders/yuyv.frag4
-rw-r--r--src/multimedia/shaders/yvu_triplanar.frag4
-rw-r--r--src/multimedia/spatial/qambisonicdecoder.cpp229
-rw-r--r--src/multimedia/spatial/qambisonicdecoder_p.h99
-rw-r--r--src/multimedia/spatial/qambisonicdecoderdata_p.h423
-rw-r--r--src/multimedia/spatial/qspatialaudioengine.cpp627
-rw-r--r--src/multimedia/spatial/qspatialaudioengine.h111
-rw-r--r--src/multimedia/spatial/qspatialaudioengine_p.h179
-rw-r--r--src/multimedia/spatial/qspatialaudiolistener.cpp161
-rw-r--r--src/multimedia/spatial/qspatialaudiolistener.h73
-rw-r--r--src/multimedia/spatial/qspatialaudioroom_p.h83
-rw-r--r--src/multimedia/spatial/qspatialaudiosoundsource_p.h95
-rw-r--r--src/multimedia/spatial/qspatialaudiostereosource.cpp215
-rw-r--r--src/multimedia/spatial/qspatialaudiostereosource.h102
-rw-r--r--src/multimedia/video/qabstractvideobuffer.cpp47
-rw-r--r--src/multimedia/video/qabstractvideobuffer_p.h54
-rw-r--r--src/multimedia/video/qimagevideobuffer.cpp90
-rw-r--r--src/multimedia/video/qimagevideobuffer_p.h43
-rw-r--r--src/multimedia/video/qmemoryvideobuffer.cpp69
-rw-r--r--src/multimedia/video/qmemoryvideobuffer_p.h49
-rw-r--r--src/multimedia/video/qtvideo.cpp30
-rw-r--r--src/multimedia/video/qtvideo.h23
-rw-r--r--src/multimedia/video/qvideoframe.cpp242
-rw-r--r--src/multimedia/video/qvideoframe.h71
-rw-r--r--src/multimedia/video/qvideoframe_p.h61
-rw-r--r--src/multimedia/video/qvideoframeconversionhelper.cpp141
-rw-r--r--src/multimedia/video/qvideoframeconversionhelper_avx2.cpp79
-rw-r--r--src/multimedia/video/qvideoframeconversionhelper_p.h53
-rw-r--r--src/multimedia/video/qvideoframeconversionhelper_sse2.cpp80
-rw-r--r--src/multimedia/video/qvideoframeconversionhelper_ssse3.cpp42
-rw-r--r--src/multimedia/video/qvideoframeconverter.cpp186
-rw-r--r--src/multimedia/video/qvideoframeconverter_p.h49
-rw-r--r--src/multimedia/video/qvideoframeformat.cpp105
-rw-r--r--src/multimedia/video/qvideoframeformat.h61
-rw-r--r--src/multimedia/video/qvideooutputorientationhandler.cpp46
-rw-r--r--src/multimedia/video/qvideooutputorientationhandler_p.h40
-rw-r--r--src/multimedia/video/qvideosink.cpp71
-rw-r--r--src/multimedia/video/qvideosink.h47
-rw-r--r--src/multimedia/video/qvideotexturehelper.cpp300
-rw-r--r--src/multimedia/video/qvideotexturehelper_p.h45
-rw-r--r--src/multimedia/video/qvideowindow.cpp78
-rw-r--r--src/multimedia/video/qvideowindow_p.h60
-rw-r--r--src/multimedia/wasm/qwasmaudiodevice.cpp69
-rw-r--r--src/multimedia/wasm/qwasmaudiodevice_p.h40
-rw-r--r--src/multimedia/wasm/qwasmaudiosink.cpp83
-rw-r--r--src/multimedia/wasm/qwasmaudiosink_p.h51
-rw-r--r--src/multimedia/wasm/qwasmaudiosource.cpp76
-rw-r--r--src/multimedia/wasm/qwasmaudiosource_p.h52
-rw-r--r--src/multimedia/wasm/qwasmmediadevices.cpp298
-rw-r--r--src/multimedia/wasm/qwasmmediadevices_p.h96
-rw-r--r--src/multimedia/windows/qcomptr_p.h33
-rw-r--r--src/multimedia/windows/qcomtaskresource_p.h152
-rw-r--r--src/multimedia/windows/qwindowsaudiodevice.cpp91
-rw-r--r--src/multimedia/windows/qwindowsaudiodevice_p.h48
-rw-r--r--src/multimedia/windows/qwindowsaudiosink.cpp186
-rw-r--r--src/multimedia/windows/qwindowsaudiosink_p.h72
-rw-r--r--src/multimedia/windows/qwindowsaudiosource.cpp795
-rw-r--r--src/multimedia/windows/qwindowsaudiosource_p.h136
-rw-r--r--src/multimedia/windows/qwindowsaudioutils.cpp67
-rw-r--r--src/multimedia/windows/qwindowsaudioutils_p.h53
-rw-r--r--src/multimedia/windows/qwindowsiupointer_p.h93
-rw-r--r--src/multimedia/windows/qwindowsmediadevices.cpp288
-rw-r--r--src/multimedia/windows/qwindowsmediadevices_p.h63
-rw-r--r--src/multimedia/windows/qwindowsmediafoundation.cpp70
-rw-r--r--src/multimedia/windows/qwindowsmediafoundation_p.h59
-rw-r--r--src/multimedia/windows/qwindowsmfdefs.cpp42
-rw-r--r--src/multimedia/windows/qwindowsmfdefs_p.h42
-rw-r--r--src/multimedia/windows/qwindowsmultimediautils.cpp51
-rw-r--r--src/multimedia/windows/qwindowsmultimediautils_p.h43
-rw-r--r--src/multimedia/windows/qwindowsresampler.cpp100
-rw-r--r--src/multimedia/windows/qwindowsresampler_p.h51
-rw-r--r--src/multimediaquick/CMakeLists.txt10
-rw-r--r--src/multimediaquick/Video.qml53
-rw-r--r--src/multimediaquick/multimedia_plugin.cpp40
-rw-r--r--src/multimediaquick/qquickimagecapture.cpp69
-rw-r--r--src/multimediaquick/qquickimagecapture_p.h40
-rw-r--r--src/multimediaquick/qquickimagepreviewprovider.cpp40
-rw-r--r--src/multimediaquick/qquickimagepreviewprovider_p.h40
-rw-r--r--src/multimediaquick/qquickmediaplayer.cpp95
-rw-r--r--src/multimediaquick/qquickmediaplayer_p.h86
-rw-r--r--src/multimediaquick/qquickplaylist.cpp40
-rw-r--r--src/multimediaquick/qquickplaylist_p.h40
-rw-r--r--src/multimediaquick/qquickscreencapture.cpp27
-rw-r--r--src/multimediaquick/qquickscreencapture_p.h44
-rw-r--r--src/multimediaquick/qquicksoundeffect.cpp29
-rw-r--r--src/multimediaquick/qquicksoundeffect_p.h57
-rw-r--r--src/multimediaquick/qquickvideooutput.cpp135
-rw-r--r--src/multimediaquick/qquickvideooutput_p.h53
-rw-r--r--src/multimediaquick/qsgvideonode_p.cpp195
-rw-r--r--src/multimediaquick/qsgvideonode_p.h60
-rw-r--r--src/multimediaquick/qsgvideotexture.cpp112
-rw-r--r--src/multimediaquick/qsgvideotexture_p.h45
-rw-r--r--src/multimediaquick/qtmultimediaquickglobal_p.h40
-rw-r--r--src/multimediaquick/qtmultimediaquicktypes.cpp12
-rw-r--r--src/multimediaquick/qtmultimediaquicktypes_p.h113
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudio-qml-types.qdoc56
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudioengine.cpp153
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudioengine_p.h86
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudiolistener.cpp82
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudiolistener_p.h72
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudioroom.cpp303
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudioroom_p.h152
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudiosoundsource.cpp346
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudiostereosource.cpp172
-rw-r--r--src/multimediaquick3d/qquick3dspatialaudiostereosource_p.h97
-rw-r--r--src/multimediaquick3d/qtquick3dsoundglobal_p.h62
-rw-r--r--src/multimediaquick3d/qtquick3dsoundtypes_p.h61
-rw-r--r--src/multimediaquick3d/quick3dspatialaudio_plugin.cpp71
-rw-r--r--src/multimediawidgets/CMakeLists.txt3
-rw-r--r--src/multimediawidgets/doc/snippets/doc_src_qtmultimediawidgets.cpp53
-rw-r--r--src/multimediawidgets/doc/snippets/multimedia-snippets/camera.cpp40
-rw-r--r--src/multimediawidgets/doc/snippets/multimedia-snippets/video.cpp40
-rw-r--r--src/multimediawidgets/doc/src/qtmultimediawidgets-index.qdoc34
-rw-r--r--src/multimediawidgets/doc/src/qtmultimediawidgets.qdoc28
-rw-r--r--src/multimediawidgets/qgraphicsvideoitem.cpp64
-rw-r--r--src/multimediawidgets/qgraphicsvideoitem.h40
-rw-r--r--src/multimediawidgets/qtmultimediawidgetsglobal.h40
-rw-r--r--src/multimediawidgets/qvideowidget.cpp69
-rw-r--r--src/multimediawidgets/qvideowidget.h40
-rw-r--r--src/multimediawidgets/qvideowidget_p.h40
-rw-r--r--src/plugins/multimedia/CMakeLists.txt30
-rw-r--r--src/plugins/multimedia/android/CMakeLists.txt12
-rw-r--r--src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp92
-rw-r--r--src/plugins/multimedia/android/audio/qandroidaudiodecoder_p.h42
-rw-r--r--src/plugins/multimedia/android/common/qandroidaudioinput.cpp42
-rw-r--r--src/plugins/multimedia/android/common/qandroidaudioinput_p.h40
-rw-r--r--src/plugins/multimedia/android/common/qandroidaudiooutput_p.h40
-rw-r--r--src/plugins/multimedia/android/common/qandroidglobal_p.h40
-rw-r--r--src/plugins/multimedia/android/common/qandroidmultimediautils.cpp81
-rw-r--r--src/plugins/multimedia/android/common/qandroidmultimediautils_p.h42
-rw-r--r--src/plugins/multimedia/android/common/qandroidvideooutput.cpp817
-rw-r--r--src/plugins/multimedia/android/common/qandroidvideooutput_p.h157
-rw-r--r--src/plugins/multimedia/android/common/qandroidvideosink.cpp47
-rw-r--r--src/plugins/multimedia/android/common/qandroidvideosink_p.h40
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp58
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamera_p.h42
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp128
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h50
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp144
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h47
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp42
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidimagecapture_p.h40
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp48
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession_p.h42
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp40
-rw-r--r--src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h40
-rw-r--r--src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp124
-rw-r--r--src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h44
-rw-r--r--src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp52
-rw-r--r--src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h40
-rw-r--r--src/plugins/multimedia/android/qandroidformatsinfo.cpp52
-rw-r--r--src/plugins/multimedia/android/qandroidformatsinfo_p.h40
-rw-r--r--src/plugins/multimedia/android/qandroidintegration.cpp78
-rw-r--r--src/plugins/multimedia/android/qandroidintegration_p.h64
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp61
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h44
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp43
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h40
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp89
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediaplayer_p.h42
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp46
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmediarecorder_p.h40
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp40
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_p.h40
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp45
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture_p.h42
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp44
-rw-r--r--src/plugins/multimedia/android/wrappers/jni/androidsurfaceview_p.h40
-rw-r--r--src/plugins/multimedia/darwin/CMakeLists.txt3
-rw-r--r--src/plugins/multimedia/darwin/avfaudiodecoder.mm441
-rw-r--r--src/plugins/multimedia/darwin/avfaudiodecoder_p.h83
-rw-r--r--src/plugins/multimedia/darwin/avfvideobuffer.mm55
-rw-r--r--src/plugins/multimedia/darwin/avfvideobuffer_p.h45
-rw-r--r--src/plugins/multimedia/darwin/avfvideosink.mm107
-rw-r--r--src/plugins/multimedia/darwin/avfvideosink_p.h46
-rw-r--r--src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_p.h40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamera.mm125
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamera_p.h40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcameradebug_p.h40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm91
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h45
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcameraservice.mm45
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcameraservice_p.h40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerasession.mm157
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerasession_p.h51
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerautility.mm105
-rw-r--r--src/plugins/multimedia/darwin/camera/avfcamerautility_p.h45
-rw-r--r--src/plugins/multimedia/darwin/camera/avfimagecapture.mm40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfimagecapture_p.h40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h40
-rw-r--r--src/plugins/multimedia/darwin/camera/avfmediaencoder.mm105
-rw-r--r--src/plugins/multimedia/darwin/camera/avfmediaencoder_p.h40
-rw-r--r--src/plugins/multimedia/darwin/camera/qavfcamerabase.mm219
-rw-r--r--src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h40
-rw-r--r--src/plugins/multimedia/darwin/common/avfmetadata.mm44
-rw-r--r--src/plugins/multimedia/darwin/common/avfmetadata_p.h40
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm42
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink_p.h40
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm130
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer_p.h45
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm66
-rw-r--r--src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h44
-rw-r--r--src/plugins/multimedia/darwin/qavfhelpers.mm252
-rw-r--r--src/plugins/multimedia/darwin/qavfhelpers_p.h56
-rw-r--r--src/plugins/multimedia/darwin/qdarwinformatsinfo.mm40
-rw-r--r--src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h40
-rw-r--r--src/plugins/multimedia/darwin/qdarwinintegration.mm70
-rw-r--r--src/plugins/multimedia/darwin/qdarwinintegration_p.h61
-rw-r--r--src/plugins/multimedia/ffmpeg/CMakeLists.txt226
-rw-r--r--src/plugins/multimedia/ffmpeg/cmake/QtDeployFFmpeg.cmake43
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer.cpp355
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer_p.h120
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec.cpp82
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec_p.h62
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegdemuxer.cpp228
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegdemuxer_p.h87
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegframe_p.h109
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder.cpp365
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder_p.h107
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegpacket_p.h61
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackenginedefs_p.h46
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackengineobject.cpp109
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackengineobject_p.h84
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegpositionwithoffset_p.h40
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer.cpp216
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer_p.h125
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegstreamdecoder.cpp240
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegstreamdecoder_p.h87
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegsubtitlerenderer.cpp44
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegsubtitlerenderer_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegtimecontroller.cpp165
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegtimecontroller_p.h94
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer.cpp78
-rw-r--r--src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer_p.h47
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidcamera.cpp697
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidcamera_p.h91
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidcameraframe.cpp198
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidcameraframe_p.h75
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp46
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidimagecapture_p.h38
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidvideodevices.cpp150
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidvideodevices_p.h35
-rw-r--r--src/plugins/multimedia/ffmpeg/qavfcamera.mm392
-rw-r--r--src/plugins/multimedia/ffmpeg/qavfcamera_p.h53
-rw-r--r--src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate.mm222
-rw-r--r--src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate_p.h51
-rw-r--r--src/plugins/multimedia/ffmpeg/qavfscreencapture.mm201
-rw-r--r--src/plugins/multimedia/ffmpeg/qavfscreencapture_p.h60
-rw-r--r--src/plugins/multimedia/ffmpeg/qcgcapturablewindows.mm48
-rw-r--r--src/plugins/multimedia/ffmpeg/qcgcapturablewindows_p.h32
-rw-r--r--src/plugins/multimedia/ffmpeg/qcgwindowcapture.mm200
-rw-r--r--src/plugins/multimedia/ffmpeg/qcgwindowcapture_p.h43
-rw-r--r--src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp178
-rw-r--r--src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeg.cpp612
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeg_p.h270
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp290
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp132
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegavaudioformat.cpp78
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegavaudioformat_p.h68
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegclock.cpp214
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegclock_p.h157
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegconverter.cpp272
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegconverter_p.h30
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp1362
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h548
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h41
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp567
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h233
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegencoderoptions_p.h68
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegencodingformatcontext.cpp116
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegencodingformatcontext_p.h60
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp575
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp353
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h103
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp120
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec_p.h36
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h88
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp108
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h40
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm76
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h44
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp134
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h59
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegioutils.cpp55
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegioutils_p.h40
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp247
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h86
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp87
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h40
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp311
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h73
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp61
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h40
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp369
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h85
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp161
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h57
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegopensslsymbols.cpp185
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp636
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegplaybackengine_p.h229
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp160
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h70
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp473
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h44
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp202
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber_p.h92
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolve_p.h37
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolveutils.cpp103
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolveutils_p.h142
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegthread.cpp96
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegthread_p.h116
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvaapisymbols.cpp127
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp214
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h63
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp407
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder_p.h112
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp42
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h43
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp509
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp_p.h46
-rw-r--r--src/plugins/multimedia/ffmpeg/qgdiwindowcapture.cpp195
-rw-r--r--src/plugins/multimedia/ffmpeg/qgdiwindowcapture_p.h43
-rw-r--r--src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp220
-rw-r--r--src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/qopenglvideobuffer.cpp101
-rw-r--r--src/plugins/multimedia/ffmpeg/qopenglvideobuffer_p.h45
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2camera.cpp624
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2camera_p.h169
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2cameradevices.cpp182
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2cameradevices_p.h46
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2filedescriptor.cpp71
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2filedescriptor_p.h50
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2memorytransfer.cpp223
-rw-r--r--src/plugins/multimedia/ffmpeg/qv4l2memorytransfer_p.h66
-rw-r--r--src/plugins/multimedia/ffmpeg/qwincapturablewindows.cpp74
-rw-r--r--src/plugins/multimedia/ffmpeg/qwincapturablewindows_p.h32
-rw-r--r--src/plugins/multimedia/ffmpeg/qwindowscamera.cpp255
-rw-r--r--src/plugins/multimedia/ffmpeg/qwindowscamera_p.h42
-rw-r--r--src/plugins/multimedia/ffmpeg/qx11capturablewindows.cpp74
-rw-r--r--src/plugins/multimedia/ffmpeg/qx11capturablewindows_p.h45
-rw-r--r--src/plugins/multimedia/ffmpeg/qx11surfacecapture.cpp342
-rw-r--r--src/plugins/multimedia/ffmpeg/qx11surfacecapture_p.h48
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp220
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h58
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoderutils.cpp97
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoderutils_p.h28
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions.cpp (renamed from src/plugins/multimedia/ffmpeg/qffmpegencoderoptions.cpp)176
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions_p.h32
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp20
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h29
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp63
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h41
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp182
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h113
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp191
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h59
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils.cpp213
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils_p.h64
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder.cpp423
-rw-r--r--src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder_p.h89
-rw-r--r--src/plugins/multimedia/gstreamer/CMakeLists.txt55
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp530
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h91
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp69
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h44
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp121
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h59
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp111
-rw-r--r--src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource_p.h61
-rw-r--r--src/plugins/multimedia/gstreamer/common/qglist_helper_p.h82
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst.cpp1238
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst_debug.cpp453
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst_debug_p.h68
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h267
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgst_p.h1062
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstappsource.cpp (renamed from src/plugins/multimedia/gstreamer/common/qgstappsrc.cpp)175
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstappsource_p.h96
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstappsrc_p.h132
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp271
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstpipeline_p.h103
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp164
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h73
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp166
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h72
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp59
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h42
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp645
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h119
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermessage.cpp94
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h76
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp614
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h55
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp193
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h63
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp91
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay_p.h56
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp223
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideosink_p.h65
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp111
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h46
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstutils.cpp417
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstutils_p.h74
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp387
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstvideobuffer_p.h68
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp279
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstvideorenderersink_p.h70
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp339
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h89
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp303
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h73
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp350
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h55
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp227
-rw-r--r--src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h56
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp123
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerformatinfo_p.h49
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp171
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h74
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamerplugin.cpp28
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamervideodevices.cpp239
-rw-r--r--src/plugins/multimedia/gstreamer/qgstreamervideodevices_p.h62
-rw-r--r--src/plugins/multimedia/qnx/CMakeLists.txt5
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcamera.cpp764
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcamera_p.h260
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer.cpp104
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h48
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxcamerahandle_p.h102
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnximagecapture.cpp339
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnximagecapture_p.h63
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxplatformcamera.cpp427
-rw-r--r--src/plugins/multimedia/qnx/camera/qqnxplatformcamera_p.h113
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxaudiorecorder.cpp46
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxaudiorecorder_p.h42
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp62
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxmediacapturesession_p.h44
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp55
-rw-r--r--src/plugins/multimedia/qnx/capture/qqnxmediarecorder_p.h40
-rw-r--r--src/plugins/multimedia/qnx/common/mmrenderertypes.h40
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp40
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxaudioinput_p.h40
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp42
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h40
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp48
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxmediaeventthread_p.h42
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp53
-rw-r--r--src/plugins/multimedia/qnx/common/qqnxwindowgrabber_p.h40
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp40
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h40
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp50
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer_p.h40
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp40
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h40
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp44
-rw-r--r--src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink_p.h42
-rw-r--r--src/plugins/multimedia/qnx/qqnxformatinfo.cpp40
-rw-r--r--src/plugins/multimedia/qnx/qqnxformatinfo_p.h40
-rw-r--r--src/plugins/multimedia/qnx/qqnxmediaintegration.cpp77
-rw-r--r--src/plugins/multimedia/qnx/qqnxmediaintegration_p.h58
-rw-r--r--src/plugins/multimedia/qnx/qqnxvideodevices.cpp162
-rw-r--r--src/plugins/multimedia/qnx/qqnxvideodevices_p.h47
-rw-r--r--src/plugins/multimedia/wasm/CMakeLists.txt25
-rw-r--r--src/plugins/multimedia/wasm/common/qwasmaudioinput.cpp107
-rw-r--r--src/plugins/multimedia/wasm/common/qwasmaudioinput_p.h57
-rw-r--r--src/plugins/multimedia/wasm/common/qwasmaudiooutput.cpp378
-rw-r--r--src/plugins/multimedia/wasm/common/qwasmaudiooutput_p.h97
-rw-r--r--src/plugins/multimedia/wasm/common/qwasmvideooutput.cpp1069
-rw-r--r--src/plugins/multimedia/wasm/common/qwasmvideooutput_p.h152
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmcamera.cpp479
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmcamera_p.h99
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmimagecapture.cpp130
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmimagecapture_p.h58
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmmediacapturesession.cpp111
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmmediacapturesession_p.h71
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder.cpp520
-rw-r--r--src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder_p.h89
-rw-r--r--src/plugins/multimedia/wasm/mediaplayer/qwasmmediaplayer.cpp475
-rw-r--r--src/plugins/multimedia/wasm/mediaplayer/qwasmmediaplayer_p.h124
-rw-r--r--src/plugins/multimedia/wasm/mediaplayer/qwasmvideosink.cpp26
-rw-r--r--src/plugins/multimedia/wasm/mediaplayer/qwasmvideosink_p.h40
-rw-r--r--src/plugins/multimedia/wasm/qwasmmediaintegration.cpp109
-rw-r--r--src/plugins/multimedia/wasm/qwasmmediaintegration_p.h50
-rw-r--r--src/plugins/multimedia/wasm/wasm.json5
-rw-r--r--src/plugins/multimedia/windows/CMakeLists.txt5
-rw-r--r--src/plugins/multimedia/windows/common/mfmetadata.cpp43
-rw-r--r--src/plugins/multimedia/windows/common/mfmetadata_p.h42
-rw-r--r--src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp90
-rw-r--r--src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h50
-rw-r--r--src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp71
-rw-r--r--src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h55
-rw-r--r--src/plugins/multimedia/windows/evr/evrcustompresenter.cpp612
-rw-r--r--src/plugins/multimedia/windows/evr/evrcustompresenter_p.h100
-rw-r--r--src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp434
-rw-r--r--src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h57
-rw-r--r--src/plugins/multimedia/windows/evr/evrhelpers.cpp40
-rw-r--r--src/plugins/multimedia/windows/evr/evrhelpers_p.h51
-rw-r--r--src/plugins/multimedia/windows/evr/evrvideowindowcontrol.cpp46
-rw-r--r--src/plugins/multimedia/windows/evr/evrvideowindowcontrol_p.h40
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp42
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowscamera_p.h40
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp62
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture_p.h46
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.cpp42
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture_p.h40
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp58
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h44
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp80
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession_p.h40
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp71
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder_p.h40
-rw-r--r--src/plugins/multimedia/windows/mfstream.cpp52
-rw-r--r--src/plugins/multimedia/windows/mfstream_p.h47
-rw-r--r--src/plugins/multimedia/windows/player/mfactivate.cpp74
-rw-r--r--src/plugins/multimedia/windows/player/mfactivate_p.h73
-rw-r--r--src/plugins/multimedia/windows/player/mfevrvideowindowcontrol.cpp40
-rw-r--r--src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h40
-rw-r--r--src/plugins/multimedia/windows/player/mfplayercontrol.cpp61
-rw-r--r--src/plugins/multimedia/windows/player/mfplayercontrol_p.h46
-rw-r--r--src/plugins/multimedia/windows/player/mfplayersession.cpp808
-rw-r--r--src/plugins/multimedia/windows/player/mfplayersession_p.h87
-rw-r--r--src/plugins/multimedia/windows/player/mftvideo.cpp728
-rw-r--r--src/plugins/multimedia/windows/player/mftvideo_p.h131
-rw-r--r--src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp2200
-rw-r--r--src/plugins/multimedia/windows/player/mfvideorenderercontrol_p.h70
-rw-r--r--src/plugins/multimedia/windows/player/samplegrabber.cpp174
-rw-r--r--src/plugins/multimedia/windows/player/samplegrabber_p.h103
-rw-r--r--src/plugins/multimedia/windows/qwindowsformatinfo.cpp165
-rw-r--r--src/plugins/multimedia/windows/qwindowsformatinfo_p.h40
-rw-r--r--src/plugins/multimedia/windows/qwindowsintegration.cpp72
-rw-r--r--src/plugins/multimedia/windows/qwindowsintegration_p.h60
-rw-r--r--src/plugins/multimedia/windows/qwindowsvideodevices.cpp282
-rw-r--r--src/plugins/multimedia/windows/qwindowsvideodevices_p.h40
-rw-r--r--src/plugins/multimedia/windows/sourceresolver.cpp45
-rw-r--r--src/plugins/multimedia/windows/sourceresolver_p.h44
-rw-r--r--src/plugins/videonode/CMakeLists.txt3
-rw-r--r--src/plugins/videonode/imx6/CMakeLists.txt3
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp42
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideomaterial.h40
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp40
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h40
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideonode.cpp40
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideonode.h40
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp40
-rw-r--r--src/plugins/videonode/imx6/qsgvivantevideonodefactory.h40
-rwxr-xr-xsrc/plugins/videonode/imx6/shaders/compile.bat38
-rw-r--r--src/resonance-audio/CMakeLists.txt28
-rw-r--r--src/resonance-audio/resonance_audio.cpp42
-rw-r--r--src/resonance-audio/resonance_audio.h33
-rw-r--r--src/resonance-audio/resonance_audio_api_extensions.cpp58
-rw-r--r--src/resonance-audio/resonance_audio_api_extensions.h49
-rw-r--r--src/spatialaudio/CMakeLists.txt29
-rw-r--r--src/spatialaudio/doc/qtspatialaudio.qdocconf62
-rw-r--r--src/spatialaudio/doc/src/qtspatialaudio-cpp.qdoc33
-rw-r--r--src/spatialaudio/doc/src/qtspatialaudio-examples.qdoc15
-rw-r--r--src/spatialaudio/doc/src/qtspatialaudio-index.qdoc104
-rw-r--r--src/spatialaudio/doc/src/qtspatialaudio-qml-types.qdoc26
-rw-r--r--src/spatialaudio/doc/src/spatialaudiooverview.qdoc64
-rw-r--r--src/spatialaudio/qambientsound.cpp283
-rw-r--r--src/spatialaudio/qambientsound.h68
-rw-r--r--src/spatialaudio/qambientsound_p.h84
-rw-r--r--src/spatialaudio/qambisonicdecoder.cpp314
-rw-r--r--src/spatialaudio/qambisonicdecoder_p.h69
-rw-r--r--src/spatialaudio/qambisonicdecoderdata_p.h279
-rw-r--r--src/spatialaudio/qaudioengine.cpp602
-rw-r--r--src/spatialaudio/qaudioengine.h80
-rw-r--r--src/spatialaudio/qaudioengine_p.h92
-rw-r--r--src/spatialaudio/qaudiolistener.cpp134
-rw-r--r--src/spatialaudio/qaudiolistener.h37
-rw-r--r--src/spatialaudio/qaudioroom.cpp (renamed from src/multimedia/spatial/qspatialaudioroom.cpp)165
-rw-r--r--src/spatialaudio/qaudioroom.h (renamed from src/multimedia/spatial/qspatialaudioroom.h)64
-rw-r--r--src/spatialaudio/qaudioroom_p.h50
-rw-r--r--src/spatialaudio/qspatialsound.cpp (renamed from src/multimedia/spatial/qspatialaudiosoundsource.cpp)280
-rw-r--r--src/spatialaudio/qspatialsound.h (renamed from src/multimedia/spatial/qspatialaudiosoundsource.h)76
-rw-r--r--src/spatialaudio/qspatialsound_p.h62
-rw-r--r--src/spatialaudio/qtspatialaudioglobal.h10
-rw-r--r--src/spatialaudio/qtspatialaudioglobal_p.h21
-rw-r--r--src/spatialaudioquick3d/CMakeLists.txt (renamed from src/multimediaquick3d/CMakeLists.txt)23
-rw-r--r--src/spatialaudioquick3d/qquick3dambientsound.cpp146
-rw-r--r--src/spatialaudioquick3d/qquick3dambientsound_p.h74
-rw-r--r--src/spatialaudioquick3d/qquick3daudio-qml-types.qdoc32
-rw-r--r--src/spatialaudioquick3d/qquick3daudioengine.cpp128
-rw-r--r--src/spatialaudioquick3d/qquick3daudioengine_p.h64
-rw-r--r--src/spatialaudioquick3d/qquick3daudiolistener.cpp49
-rw-r--r--src/spatialaudioquick3d/qquick3daudiolistener_p.h47
-rw-r--r--src/spatialaudioquick3d/qquick3daudioroom.cpp270
-rw-r--r--src/spatialaudioquick3d/qquick3daudioroom_p.h129
-rw-r--r--src/spatialaudioquick3d/qquick3dspatialaudio_plugin.cpp35
-rw-r--r--src/spatialaudioquick3d/qquick3dspatialsound.cpp323
-rw-r--r--src/spatialaudioquick3d/qquick3dspatialsound_p.h (renamed from src/multimediaquick3d/qquick3dspatialaudiosoundsource_p.h)67
-rw-r--r--src/spatialaudioquick3d/qtquick3daudioglobal_p.h28
-rw-r--r--src/spatialaudioquick3d/qtquick3daudiotypes_p.h27
-rw-r--r--sync.profile23
-rw-r--r--tests/CMakeLists.txt3
-rw-r--r--tests/auto/CMakeLists.txt7
-rw-r--r--tests/auto/cmake/CMakeLists.txt14
-rw-r--r--tests/auto/integration/CMakeLists.txt8
-rw-r--r--tests/auto/integration/backends/CMakeLists.txt9
-rw-r--r--tests/auto/integration/backends/tst_backends.cpp60
-rw-r--r--tests/auto/integration/multiapp/CMakeLists.txt21
-rw-r--r--tests/auto/integration/multiapp/double-drop.wavbin0 -> 20626 bytes
-rw-r--r--tests/auto/integration/multiapp/tst_multiapp.cpp161
-rw-r--r--tests/auto/integration/qaudiodecoderbackend/CMakeLists.txt9
-rw-r--r--tests/auto/integration/qaudiodecoderbackend/testdata/test-no-audio-track.mp4bin0 -> 1589 bytes
-rw-r--r--tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp503
-rw-r--r--tests/auto/integration/qaudiodevice/CMakeLists.txt5
-rw-r--r--tests/auto/integration/qaudiodevice/tst_qaudiodevice.cpp49
-rw-r--r--tests/auto/integration/qaudiosink/BLACKLIST12
-rw-r--r--tests/auto/integration/qaudiosink/CMakeLists.txt5
-rw-r--r--tests/auto/integration/qaudiosink/tst_qaudiosink.cpp587
-rw-r--r--tests/auto/integration/qaudiosource/CMakeLists.txt5
-rw-r--r--tests/auto/integration/qaudiosource/tst_qaudiosource.cpp238
-rw-r--r--tests/auto/integration/qcamerabackend/BLACKLIST1
-rw-r--r--tests/auto/integration/qcamerabackend/CMakeLists.txt10
-rw-r--r--tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp256
-rw-r--r--tests/auto/integration/qmediacapturesession/BLACKLIST1
-rw-r--r--tests/auto/integration/qmediacapturesession/CMakeLists.txt8
-rw-r--r--tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp549
-rw-r--r--tests/auto/integration/qmediaplayerbackend/BLACKLIST1
-rw-r--r--tests/auto/integration/qmediaplayerbackend/CMakeLists.txt18
-rw-r--r--tests/auto/integration/qmediaplayerbackend/LazyLoad.qml52
-rw-r--r--tests/auto/integration/qmediaplayerbackend/fake.h29
-rw-r--r--tests/auto/integration/qmediaplayerbackend/fixture.h79
-rw-r--r--tests/auto/integration/qmediaplayerbackend/mediaplayerstate.h167
-rw-r--r--tests/auto/integration/qmediaplayerbackend/server.h48
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/3colors_with_sound_1s.mp4bin0 -> 37261 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/audio_video_with_jpg_thumbnail.mp4bin0 -> 37930 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/audio_video_with_png_thumbnail.mp4bin0 -> 37436 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/busAv1.webmbin0 -> 16108 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/color_matrix.mp4bin0 -> 21412 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_180_deg_clockwise.mp4bin0 -> 21412 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_270_deg_clockwise.mp4bin0 -> 21412 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_90_deg_clockwise.mp4bin0 -> 21412 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/duration_issues.webmbin0 -> 34940 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3bin62715 -> 33988 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/one_red_frame.mp4bin0 -> 1589 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/par_2_3.mp4bin0 -> 1656 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/par_3_2.mp4bin0 -> 1652 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp2977
-rw-r--r--tests/auto/integration/qml/CMakeLists.txt5
-rw-r--r--tests/auto/integration/qml/soundeffect/tst_soundeffect.qml29
-rw-r--r--tests/auto/integration/qml/tst_qml.cpp29
-rw-r--r--tests/auto/integration/qquickvideooutput/CMakeLists.txt5
-rw-r--r--tests/auto/integration/qquickvideooutput/tst_qquickvideooutput.cpp59
-rw-r--r--tests/auto/integration/qquickvideooutput_window/CMakeLists.txt5
-rw-r--r--tests/auto/integration/qquickvideooutput_window/tst_qquickvideooutput_window.cpp31
-rw-r--r--tests/auto/integration/qscreencapturebackend/BLACKLIST11
-rw-r--r--tests/auto/integration/qscreencapturebackend/CMakeLists.txt17
-rw-r--r--tests/auto/integration/qscreencapturebackend/tst_qscreencapturebackend.cpp505
-rw-r--r--tests/auto/integration/qsoundeffect/CMakeLists.txt5
-rw-r--r--tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp85
-rw-r--r--tests/auto/integration/qvideoframebackend/CMakeLists.txt32
-rw-r--r--tests/auto/integration/qvideoframebackend/testdata/colors.mp4bin0 -> 26042 bytes
-rw-r--r--tests/auto/integration/qvideoframebackend/testdata/one_red_frame.mp4bin0 -> 1589 bytes
-rw-r--r--tests/auto/integration/qvideoframebackend/tst_qvideoframebackend.cpp258
-rw-r--r--tests/auto/integration/qwindowcapturebackend/BLACKLIST13
-rw-r--r--tests/auto/integration/qwindowcapturebackend/CMakeLists.txt20
-rw-r--r--tests/auto/integration/qwindowcapturebackend/fixture.cpp234
-rw-r--r--tests/auto/integration/qwindowcapturebackend/fixture.h147
-rw-r--r--tests/auto/integration/qwindowcapturebackend/grabber.cpp62
-rw-r--r--tests/auto/integration/qwindowcapturebackend/grabber.h43
-rw-r--r--tests/auto/integration/qwindowcapturebackend/tst_qwindowcapturebackend.cpp278
-rw-r--r--tests/auto/integration/qwindowcapturebackend/widget.cpp125
-rw-r--r--tests/auto/integration/qwindowcapturebackend/widget.h43
-rw-r--r--tests/auto/integration/shared/mediabackendutils.h53
-rw-r--r--tests/auto/integration/shared/mediafileselector.h191
-rw-r--r--tests/auto/integration/shared/testvideosink.h62
-rwxr-xr-xtests/auto/runautotests.py29
-rw-r--r--tests/auto/unit/CMakeLists.txt3
-rw-r--r--tests/auto/unit/mockbackend/CMakeLists.txt47
-rw-r--r--tests/auto/unit/mockbackend/mock.json3
-rw-r--r--tests/auto/unit/mockbackend/qmockaudiodecoder.cpp139
-rw-r--r--tests/auto/unit/mockbackend/qmockaudiodecoder.h178
-rw-r--r--tests/auto/unit/mockbackend/qmockaudiooutput.h40
-rw-r--r--tests/auto/unit/mockbackend/qmockcamera.cpp164
-rw-r--r--tests/auto/unit/mockbackend/qmockcamera.h204
-rw-r--r--tests/auto/unit/mockbackend/qmockimagecapture.cpp39
-rw-r--r--tests/auto/unit/mockbackend/qmockimagecapture.h29
-rw-r--r--tests/auto/unit/mockbackend/qmockintegration.cpp182
-rw-r--r--tests/auto/unit/mockbackend/qmockintegration.h103
-rw-r--r--tests/auto/unit/mockbackend/qmockintegration_p.h113
-rw-r--r--tests/auto/unit/mockbackend/qmockmediacapturesession.h35
-rw-r--r--tests/auto/unit/mockbackend/qmockmediadevices.cpp91
-rw-r--r--tests/auto/unit/mockbackend/qmockmediadevices.h45
-rw-r--r--tests/auto/unit/mockbackend/qmockmediadevices_p.h83
-rw-r--r--tests/auto/unit/mockbackend/qmockmediaencoder.h31
-rw-r--r--tests/auto/unit/mockbackend/qmockmediaplayer.h29
-rw-r--r--tests/auto/unit/mockbackend/qmocksurfacecapture.h86
-rw-r--r--tests/auto/unit/mockbackend/qmockvideobuffer.h40
-rw-r--r--tests/auto/unit/mockbackend/qmockvideosink.h42
-rw-r--r--tests/auto/unit/multimedia/CMakeLists.txt21
-rw-r--r--tests/auto/unit/multimedia/gstreamer_backend/CMakeLists.txt15
-rw-r--r--tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.cpp156
-rw-r--r--tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.h39
-rw-r--r--tests/auto/unit/multimedia/qabstractvideobuffer/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qabstractvideobuffer/tst_qabstractvideobuffer.cpp33
-rw-r--r--tests/auto/unit/multimedia/qaudiobuffer/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qaudiobuffer/tst_qaudiobuffer.cpp29
-rw-r--r--tests/auto/unit/multimedia/qaudiodecoder/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp95
-rw-r--r--tests/auto/unit/multimedia/qaudioformat/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qaudioformat/tst_qaudioformat.cpp31
-rw-r--r--tests/auto/unit/multimedia/qaudionamespace/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qaudionamespace/tst_qaudionamespace.cpp33
-rw-r--r--tests/auto/unit/multimedia/qaudiorecorder/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimedia/qaudiorecorder/tst_qaudiorecorder.cpp39
-rw-r--r--tests/auto/unit/multimedia/qaudiostatemachine/CMakeLists.txt11
-rw-r--r--tests/auto/unit/multimedia/qaudiostatemachine/tst_qaudiostatemachine.cpp661
-rw-r--r--tests/auto/unit/multimedia/qcamera/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp163
-rw-r--r--tests/auto/unit/multimedia/qcameradevice/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimedia/qcameradevice/tst_qcameradevice.cpp40
-rw-r--r--tests/auto/unit/multimedia/qerrorinfo/CMakeLists.txt10
-rw-r--r--tests/auto/unit/multimedia/qerrorinfo/tst_qerrorinfo.cpp117
-rw-r--r--tests/auto/unit/multimedia/qimagecapture/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp86
-rw-r--r--tests/auto/unit/multimedia/qmaybe/CMakeLists.txt9
-rw-r--r--tests/auto/unit/multimedia/qmaybe/tst_qmaybe.cpp124
-rw-r--r--tests/auto/unit/multimedia/qmediacapture_gstreamer/CMakeLists.txt14
-rw-r--r--tests/auto/unit/multimedia/qmediacapture_gstreamer/tst_qmediacapture_gstreamer.cpp75
-rw-r--r--tests/auto/unit/multimedia/qmediadevices/CMakeLists.txt13
-rw-r--r--tests/auto/unit/multimedia/qmediadevices/tst_qmediadevices.cpp58
-rw-r--r--tests/auto/unit/multimedia/qmediaformat/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qmediaformat/tst_qmediaformat.cpp29
-rw-r--r--tests/auto/unit/multimedia/qmediaplayer/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp216
-rw-r--r--tests/auto/unit/multimedia/qmediaplayer_gstreamer/CMakeLists.txt14
-rw-r--r--tests/auto/unit/multimedia/qmediaplayer_gstreamer/tst_qmediaplayer_gstreamer.cpp78
-rw-r--r--tests/auto/unit/multimedia/qmediaplaylist/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qmediaplaylist/tst_qmediaplaylist.cpp31
-rw-r--r--tests/auto/unit/multimedia/qmediarecorder/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp138
-rw-r--r--tests/auto/unit/multimedia/qmediatimerange/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qmediatimerange/tst_qmediatimerange.cpp45
-rw-r--r--tests/auto/unit/multimedia/qmultimediautils/CMakeLists.txt9
-rw-r--r--tests/auto/unit/multimedia/qmultimediautils/tst_qmultimediautils.cpp184
-rw-r--r--tests/auto/unit/multimedia/qsamplecache/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qsamplecache/tst_qsamplecache.cpp31
-rw-r--r--tests/auto/unit/multimedia/qscreencapture/CMakeLists.txt18
-rw-r--r--tests/auto/unit/multimedia/qscreencapture/tst_qscreencapture.cpp64
-rw-r--r--tests/auto/unit/multimedia/qvideobuffers/CMakeLists.txt10
-rw-r--r--tests/auto/unit/multimedia/qvideobuffers/tst_qvideobuffers.cpp296
-rw-r--r--tests/auto/unit/multimedia/qvideoframe/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp450
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/CMakeLists.txt24
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpgbin0 -> 12786 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_AdobeRgb_Full.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_AdobeRgb_Video.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT2020_Full.pngbin0 -> 40908 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT2020_Video.pngbin0 -> 40978 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT601_Full.pngbin0 -> 40854 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT601_Video.pngbin0 -> 40904 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT709_Full.pngbin0 -> 40945 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT709_Video.pngbin0 -> 40959 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_AdobeRgb_Full.pngbin0 -> 41163 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_AdobeRgb_Video.pngbin0 -> 41163 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT2020_Full.pngbin0 -> 41160 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT2020_Video.pngbin0 -> 41199 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT601_Full.pngbin0 -> 41000 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT601_Video.pngbin0 -> 41177 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT709_Full.pngbin0 -> 41183 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT709_Video.pngbin0 -> 41209 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_AdobeRgb_Full.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_AdobeRgb_Video.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT2020_Full.pngbin0 -> 40908 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT2020_Video.pngbin0 -> 40978 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT601_Full.pngbin0 -> 40854 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT601_Video.pngbin0 -> 40904 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT709_Full.pngbin0 -> 40945 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT709_Video.pngbin0 -> 40959 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_AdobeRgb_Full.pngbin0 -> 40934 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_AdobeRgb_Video.pngbin0 -> 40934 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT2020_Full.pngbin0 -> 40912 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT2020_Video.pngbin0 -> 41004 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT601_Full.pngbin0 -> 40870 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT601_Video.pngbin0 -> 40924 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT709_Full.pngbin0 -> 40966 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT709_Video.pngbin0 -> 40975 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_AdobeRgb_Full.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_AdobeRgb_Video.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT2020_Full.pngbin0 -> 40908 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT2020_Video.pngbin0 -> 40978 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT601_Full.pngbin0 -> 40854 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT601_Video.pngbin0 -> 40904 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT709_Full.pngbin0 -> 40945 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT709_Video.pngbin0 -> 40959 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_AdobeRgb_Full.pngbin0 -> 40908 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_AdobeRgb_Video.pngbin0 -> 40908 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT2020_Full.pngbin0 -> 40912 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT2020_Video.pngbin0 -> 40987 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT601_Full.pngbin0 -> 40877 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT601_Video.pngbin0 -> 40918 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT709_Full.pngbin0 -> 40967 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT709_Video.pngbin0 -> 40961 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_AdobeRgb_Full.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_AdobeRgb_Video.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT2020_Full.pngbin0 -> 40908 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT2020_Video.pngbin0 -> 40978 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT601_Full.pngbin0 -> 40854 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT601_Video.pngbin0 -> 40904 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT709_Full.pngbin0 -> 40945 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT709_Video.pngbin0 -> 40959 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_AdobeRgb_Full.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_AdobeRgb_Video.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT2020_Full.pngbin0 -> 40908 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT2020_Video.pngbin0 -> 40978 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT601_Full.pngbin0 -> 40854 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT601_Video.pngbin0 -> 40904 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT709_Full.pngbin0 -> 40945 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT709_Video.pngbin0 -> 40959 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_AdobeRgb_Full.pngbin0 -> 40927 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_AdobeRgb_Video.pngbin0 -> 40927 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT2020_Full.pngbin0 -> 40923 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT2020_Video.pngbin0 -> 40964 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT601_Full.pngbin0 -> 40897 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT601_Video.pngbin0 -> 40914 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT709_Full.pngbin0 -> 40936 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT709_Video.pngbin0 -> 40991 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_AdobeRgb_Full.pngbin0 -> 40913 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_AdobeRgb_Video.pngbin0 -> 40913 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT2020_Full.pngbin0 -> 40969 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT2020_Video.pngbin0 -> 40951 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT601_Full.pngbin0 -> 40860 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT601_Video.pngbin0 -> 40946 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT709_Full.pngbin0 -> 40942 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT709_Video.pngbin0 -> 40966 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Full.pngbin0 -> 39859 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Video.pngbin0 -> 39859 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Full.pngbin0 -> 39864 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Video.pngbin0 -> 39919 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Full.pngbin0 -> 39789 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Video.pngbin0 -> 39890 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Full.pngbin0 -> 39879 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Video.pngbin0 -> 39951 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_AdobeRgb_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_AdobeRgb_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT2020_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT2020_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT601_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT601_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT709_Full.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT709_Video.pngbin0 -> 41243 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_AdobeRgb_Full.pngbin0 -> 25240 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_AdobeRgb_Video.pngbin0 -> 25240 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT2020_Full.pngbin0 -> 25544 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT2020_Video.pngbin0 -> 24030 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT601_Full.pngbin0 -> 25557 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT601_Video.pngbin0 -> 23884 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT709_Full.pngbin0 -> 25557 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT709_Video.pngbin0 -> 24021 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_AdobeRgb_Full.pngbin0 -> 25364 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_AdobeRgb_Video.pngbin0 -> 25364 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT2020_Full.pngbin0 -> 25430 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT2020_Video.pngbin0 -> 23981 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT601_Full.pngbin0 -> 25497 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT601_Video.pngbin0 -> 23904 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT709_Full.pngbin0 -> 25497 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT709_Video.pngbin0 -> 23979 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_AdobeRgb_Full.pngbin0 -> 40919 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_AdobeRgb_Video.pngbin0 -> 40919 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT2020_Full.pngbin0 -> 40952 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT2020_Video.pngbin0 -> 40967 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT601_Full.pngbin0 -> 40881 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT601_Video.pngbin0 -> 40962 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT709_Full.pngbin0 -> 40954 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT709_Video.pngbin0 -> 40987 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Full.pngbin0 -> 39859 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Video.pngbin0 -> 39859 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Full.pngbin0 -> 39864 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Video.pngbin0 -> 39919 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Full.pngbin0 -> 39789 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Video.pngbin0 -> 39890 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Full.pngbin0 -> 39879 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Video.pngbin0 -> 39951 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_AdobeRgb_Full.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_AdobeRgb_Video.pngbin0 -> 40898 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT2020_Full.pngbin0 -> 40908 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT2020_Video.pngbin0 -> 40978 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT601_Full.pngbin0 -> 40854 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT601_Video.pngbin0 -> 40904 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT709_Full.pngbin0 -> 40945 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT709_Video.pngbin0 -> 40959 bytes
-rw-r--r--tests/auto/unit/multimedia/qvideoframecolormanagement/tst_qvideoframecolormanagement.cpp448
-rw-r--r--tests/auto/unit/multimedia/qvideoframeformat/CMakeLists.txt5
-rw-r--r--tests/auto/unit/multimedia/qvideoframeformat/tst_qvideoframeformat.cpp108
-rw-r--r--tests/auto/unit/multimedia/qvideotexturehelper/CMakeLists.txt10
-rw-r--r--tests/auto/unit/multimedia/qvideotexturehelper/tst_qvideotexturehelper.cpp260
-rw-r--r--tests/auto/unit/multimedia/qwavedecoder/CMakeLists.txt5
-rwxr-xr-xtests/auto/unit/multimedia/qwavedecoder/data/gendata.sh29
-rw-r--r--tests/auto/unit/multimedia/qwavedecoder/data/isawav_1_8_8000_even_bext.wavbin0 -> 2064 bytes
-rw-r--r--tests/auto/unit/multimedia/qwavedecoder/data/isawav_1_8_8000_odd_bext.wavbin0 -> 2064 bytes
-rw-r--r--tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp117
-rw-r--r--tests/auto/unit/multimediawidgets/CMakeLists.txt3
-rw-r--r--tests/auto/unit/multimediawidgets/qcamerawidgets/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp41
-rw-r--r--tests/auto/unit/multimediawidgets/qgraphicsvideoitem/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp43
-rw-r--r--tests/auto/unit/multimediawidgets/qmediaplayerwidgets/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimediawidgets/qmediaplayerwidgets/tst_qmediaplayerwidgets.cpp66
-rw-r--r--tests/auto/unit/multimediawidgets/qvideowidget/BLACKLIST3
-rw-r--r--tests/auto/unit/multimediawidgets/qvideowidget/CMakeLists.txt7
-rw-r--r--tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp78
-rw-r--r--tests/manual/CMakeLists.txt16
-rw-r--r--tests/manual/audiodecoder/CMakeLists.txt (renamed from examples/multimedia/audiodecoder/CMakeLists.txt)3
-rw-r--r--tests/manual/audiodecoder/audiodecoder.cpp162
-rw-r--r--tests/manual/audiodecoder/audiodecoder.h55
-rw-r--r--tests/manual/audiodecoder/audiodecoder.pro (renamed from examples/multimedia/audiodecoder/audiodecoder.pro)0
-rw-r--r--tests/manual/audiodecoder/main.cpp83
-rw-r--r--tests/manual/devices/CMakeLists.txt (renamed from examples/multimedia/devices/CMakeLists.txt)9
-rw-r--r--tests/manual/devices/devices.pro12
-rw-r--r--tests/manual/devices/main.cpp136
-rw-r--r--tests/manual/minimal-player/CMakeLists.txt35
-rw-r--r--tests/manual/minimal-player/Info.plist.in46
-rw-r--r--tests/manual/minimal-player/minimal-player.cpp94
-rw-r--r--tests/manual/qml-gstreamer-rtp/CMakeLists.txt42
-rw-r--r--tests/manual/qml-gstreamer-rtp/Info.plist.in46
-rw-r--r--tests/manual/qml-gstreamer-rtp/qml-gstreamer-rtp.cpp32
-rw-r--r--tests/manual/qml-gstreamer-rtp/qml-gstreamer-rtp.qml29
-rw-r--r--tests/manual/qml-minimal-camera/CMakeLists.txt42
-rw-r--r--tests/manual/qml-minimal-camera/Info.plist.in46
-rw-r--r--tests/manual/qml-minimal-camera/qml-minimal-camera.cpp17
-rw-r--r--tests/manual/qml-minimal-camera/qml-minimal-camera.qml34
-rw-r--r--tests/manual/qml-minimal-player/CMakeLists.txt42
-rw-r--r--tests/manual/qml-minimal-player/Info.plist.in46
-rw-r--r--tests/manual/qml-minimal-player/qml-minimal-player.cpp17
-rw-r--r--tests/manual/qml-minimal-player/qml-minimal-player.qml42
-rw-r--r--tests/manual/wasm/CMakeLists.txt6
-rw-r--r--tests/manual/wasm/camera/CMakeLists.txt42
-rw-r--r--tests/manual/wasm/camera/camera-test.pro69
-rw-r--r--tests/manual/wasm/camera/main.cpp17
-rw-r--r--tests/manual/wasm/camera/mainwindow.cpp261
-rw-r--r--tests/manual/wasm/camera/mainwindow.h53
-rw-r--r--tests/manual/wasm/camera/mainwindow.ui107
-rw-r--r--tests/systemtests/audio_playback/sys_audio.qtt374
-rw-r--r--tests/systemtests/audio_recording/sys_audio_recording.qtt64
-rw-r--r--tests/systemtests/radio/sys_radio.qtt286
-rw-r--r--tests/systemtests/still_camera/sys_camera.qtt162
-rw-r--r--tests/systemtests/video_playback/sys_video.qtt180
-rw-r--r--util/adt_generate_qt.m123
-rw-r--r--util/macos_test_audio_config/CMakeLists.txt13
-rw-r--r--util/macos_test_audio_config/README25
-rwxr-xr-xutil/macos_test_audio_config/compile_and_run.sh19
-rw-r--r--util/macos_test_audio_config/main.cpp498
1694 files changed, 70596 insertions, 66421 deletions
diff --git a/.cmake.conf b/.cmake.conf
index 280060834..dc1d7a924 100644
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -1,2 +1,5 @@
-set(QT_REPO_MODULE_VERSION "6.4.0")
+set(QT_REPO_MODULE_VERSION "6.8.0")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
+set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1")
+list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_FOREACH=1")
+list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_CONTEXTLESS_CONNECT=1")
diff --git a/.gitignore b/.gitignore
index 9cf9ac3df..bcd62917b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@ tmp
*-config.*
*-config_p.h
*.version*
+.DS_Store
#
# Includes
@@ -44,6 +45,7 @@ src/multimediawidgets/qtmultimediawidgetsversion.h
# Tests
#
tst_*
+!tst_*.h
!tst_*.cpp
!tst_*.qml
tests/auto/cmake/build
@@ -51,7 +53,13 @@ tests/auto/cmake/build
# Generated static plugin import sources
*_plugin_import.cpp
-# Build artifacts
+# IDE artifacts
CMakeLists.txt.user
+CMakeLists.txt.user.*
+.vscode
+.xdp_CMakeLists.txt.*
+CMakeUserPresets.json
+
+# Build artifacts
build-*-Debug
build-*-Release
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 033d02ec5..70710b1b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qtmultimedia.pro.
cmake_minimum_required(VERSION 3.16)
@@ -12,6 +15,7 @@ project(QtMultimedia
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core)
find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Gui Network Svg Widgets Quick Qml QuickTest QuickControls2 Quick3D)
+qt_internal_project_setup()
if(NOT TARGET Qt::Gui)
message(NOTICE "Skipping the build as the condition \"TARGET Qt::Gui\" is not met.")
diff --git a/LICENSE.GPL3-EXCEPT b/LICENSE.GPL3-EXCEPT
deleted file mode 100644
index b1cb1bec7..000000000
--- a/LICENSE.GPL3-EXCEPT
+++ /dev/null
@@ -1,704 +0,0 @@
-This is the GNU General Public License version 3, annotated with The
-Qt Company GPL Exception 1.0:
-
--------------------------------------------------------------------------
-
-The Qt Company GPL Exception 1.0
-
-Exception 1:
-
-As a special exception you may create a larger work which contains the
-output of this application and distribute that work under terms of your
-choice, so long as the work is not otherwise derived from or based on
-this application and so long as the work does not in itself generate
-output that contains the output from this application in its original
-or modified form.
-
-Exception 2:
-
-As a special exception, you have permission to combine this application
-with Plugins licensed under the terms of your choice, to produce an
-executable, and to copy and distribute the resulting executable under
-the terms of your choice. However, the executable must be accompanied
-by a prominent notice offering all users of the executable the entire
-source code to this application, excluding the source code of the
-independent modules, but including any changes you have made to this
-application, under the terms of this license.
-
-
--------------------------------------------------------------------------
-
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- <program> Copyright (C) <year> <name of author>
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt
new file mode 100644
index 000000000..5f662b354
--- /dev/null
+++ b/LICENSES/BSD-2-Clause.txt
@@ -0,0 +1,9 @@
+Copyright (c) <year> <owner>
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt
new file mode 100644
index 000000000..b91bbd894
--- /dev/null
+++ b/LICENSES/BSD-3-Clause.txt
@@ -0,0 +1,9 @@
+Copyright (c) <year> <owner>.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSES/BSD-Source-Code.txt b/LICENSES/BSD-Source-Code.txt
new file mode 100644
index 000000000..c41fc4273
--- /dev/null
+++ b/LICENSES/BSD-Source-Code.txt
@@ -0,0 +1,10 @@
+Copyright (c) 2011, Deusty, LLC
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+ * Neither the name of Deusty nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of Deusty, LLC.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSES/BSL-1.0.txt b/LICENSES/BSL-1.0.txt
new file mode 100644
index 000000000..2d87ab1a9
--- /dev/null
+++ b/LICENSES/BSL-1.0.txt
@@ -0,0 +1,7 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/LICENSE.FDL b/LICENSES/GFDL-1.3-no-invariants-only.txt
index 938bb8da9..857214dd8 100644
--- a/LICENSE.FDL
+++ b/LICENSES/GFDL-1.3-no-invariants-only.txt
@@ -1,9 +1,10 @@
+
GNU Free Documentation License
Version 1.3, 3 November 2008
Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
- <http://fsf.org/>
+ <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -376,7 +377,7 @@ The Free Software Foundation may publish new, revised versions of the
GNU Free Documentation License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in
detail to address new problems or concerns. See
-http://www.gnu.org/copyleft/.
+https://www.gnu.org/licenses/.
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
@@ -400,19 +401,19 @@ public wiki that anybody can edit is an example of such a server. A
"Massive Multiauthor Collaboration" (or "MMC") contained in the site
means any set of copyrightable works thus published on the MMC site.
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
-license published by Creative Commons Corporation, a not-for-profit
-corporation with a principal place of business in San Francisco,
-California, as well as future copyleft versions of that license
+"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
published by that same organization.
-"Incorporate" means to publish or republish a Document, in whole or in
+"Incorporate" means to publish or republish a Document, in whole or in
part, as part of another Document.
-An MMC is "eligible for relicensing" if it is licensed under this
-License, and if all works that were first published under this License
-somewhere other than this MMC, and subsequently incorporated in whole or
-in part into the MMC, (1) had no cover texts or invariant sections, and
+An MMC is "eligible for relicensing" if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole or
+in part into the MMC, (1) had no cover texts or invariant sections, and
(2) were thus incorporated prior to November 1, 2008.
The operator of an MMC Site may republish an MMC contained in the site
diff --git a/LICENSE.GPL2 b/LICENSES/GPL-2.0-only.txt
index d159169d1..d159169d1 100644
--- a/LICENSE.GPL2
+++ b/LICENSES/GPL-2.0-only.txt
diff --git a/LICENSE.GPL3 b/LICENSES/GPL-3.0-only.txt
index 94a9ed024..94a9ed024 100644
--- a/LICENSE.GPL3
+++ b/LICENSES/GPL-3.0-only.txt
diff --git a/LICENSES/IJG.txt b/LICENSES/IJG.txt
new file mode 100644
index 000000000..761071caa
--- /dev/null
+++ b/LICENSES/IJG.txt
@@ -0,0 +1,38 @@
+Independent JPEG Group License
+
+LEGAL ISSUES
+
+In plain English:
+
+1. We don't promise that this software works. (But if you find any bugs, please let us know!)
+2. You can use this software for whatever you want. You don't have to pay us.
+3. You may not pretend that you wrote this software. If you use it in a program, you must acknowledge somewhere in your documentation that you've used the IJG code.
+
+In legalese:
+
+The authors make NO WARRANTY or representation, either express or implied, with respect to this software, its quality, accuracy, merchantability, or fitness for a particular purpose. This software is provided "AS IS", and you, its user, assume the entire risk as to its quality and accuracy.
+
+This software is copyright (C) 1991-1998, Thomas G. Lane. All Rights Reserved except as specified below.
+
+Permission is hereby granted to use, copy, modify, and distribute this software (or portions thereof) for any purpose, without fee, subject to these conditions:
+
+ (1) If any part of the source code for this software is distributed, then this README file must be included, with this copyright and no-warranty notice unaltered; and any additions, deletions, or changes to the original files must be clearly indicated in accompanying documentation.
+ (2) If only executable code is distributed, then the accompanying documentation must state that "this software is based in part on the work of the Independent JPEG Group".
+ (3) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind.
+
+These conditions apply to any software derived from or based on the IJG code, not just to the unmodified library. If you use our work, you ought to acknowledge us.
+
+Permission is NOT granted for the use of any IJG author's name or company name in advertising or publicity relating to this software or products derived from it. This software may be referred to only as "the Independent JPEG Group's software".
+
+We specifically permit and encourage the use of this software as the basis of commercial products, provided that all warranty or liability claims are assumed by the product vendor.
+
+ansi2knr.c is included in this distribution by permission of L. Peter Deutsch, sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA. ansi2knr.c is NOT covered by the above copyright and conditions, but instead by the usual distribution terms of the Free Software Foundation; principally, that you must include source code if you redistribute it. (See the file ansi2knr.c for full details.) However, since ansi2knr.c is not needed as part of any program generated from the IJG code, this does not limit you more than the foregoing paragraphs do.
+
+The Unix configuration script "configure" was produced with GNU Autoconf. It is copyright by the Free Software Foundation but is freely distributable. The same holds for its supporting scripts (config.guess, config.sub, ltconfig, ltmain.sh). Another support script, install-sh, is copyright by M.I.T. but is also freely distributable.
+
+It appears that the arithmetic coding option of the JPEG spec is covered by patents owned by IBM, AT&T, and Mitsubishi. Hence arithmetic coding cannot legally be used without obtaining one or more licenses. For this reason, support for arithmetic coding has been removed from the free JPEG software. (Since arithmetic coding provides only a marginal gain over the unpatented Huffman mode, it is unlikely that very many implementations will support it.) So far as we are aware, there are no patent restrictions on the remaining code.
+
+The IJG distribution formerly included code to read and write GIF files. To avoid entanglement with the Unisys LZW patent, GIF reading support has been removed altogether, and the GIF writer has been simplified to produce "uncompressed GIFs". This technique does not use the LZW algorithm; the resulting GIF files are larger than usual, but are readable by all standard GIF decoders.
+
+We are required to state that
+ "The Graphics Interchange Format(c) is the Copyright property of CompuServe Incorporated. GIF(sm) is a Service Mark property of CompuServe Incorporated."
diff --git a/LICENSES/ISC.txt b/LICENSES/ISC.txt
new file mode 100644
index 000000000..b9c199c98
--- /dev/null
+++ b/LICENSES/ISC.txt
@@ -0,0 +1,8 @@
+ISC License:
+
+Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
+Copyright (c) 1995-2003 by Internet Software Consortium
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/LICENSES/LGPL-2.1-or-later.txt b/LICENSES/LGPL-2.1-or-later.txt
new file mode 100644
index 000000000..c9aa53018
--- /dev/null
+++ b/LICENSES/LGPL-2.1-or-later.txt
@@ -0,0 +1,175 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+
+Version 2.1, February 1999
+
+Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.]
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.
+
+This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.
+
+When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.
+
+To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.
+
+For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
+
+We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.
+
+To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.
+
+Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.
+
+Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.
+
+When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.
+
+We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.
+
+For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.
+
+In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.
+
+Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.
+
+The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".
+
+A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
+
+The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
+
+"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
+
+1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
+
+(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.
+
+Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
+
+This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
+
+4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.
+
+If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
+
+5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.
+
+However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
+
+When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
+
+If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
+
+Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
+
+6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
+
+You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
+
+ a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
+
+For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
+
+7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
+
+ b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
+
+8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
+
+10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.
+
+11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
+
+14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).
+
+To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+ one line to give the library's name and an idea of what it does.
+ Copyright (C) year name of author
+
+ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the library `Frob' (a library for tweaking knobs) written
+by James Random Hacker.
+
+signature of Ty Coon, 1 April 1990
+Ty Coon, President of Vice
+That's all there is to it!
diff --git a/LICENSE.LGPL3 b/LICENSES/LGPL-3.0-only.txt
index 65c5ca88a..65c5ca88a 100644
--- a/LICENSE.LGPL3
+++ b/LICENSES/LGPL-3.0-only.txt
diff --git a/LICENSES/LicenseRef-Qt-Commercial.txt b/LICENSES/LicenseRef-Qt-Commercial.txt
new file mode 100644
index 000000000..825b1f358
--- /dev/null
+++ b/LICENSES/LicenseRef-Qt-Commercial.txt
@@ -0,0 +1,8 @@
+Licensees holding valid commercial Qt licenses may use this software in
+accordance with the the terms contained in a written agreement between
+you and The Qt Company. Alternatively, the terms and conditions that were
+accepted by the licensee when buying and/or downloading the
+software do apply.
+
+For the latest licensing terms and conditions, see https://www.qt.io/terms-conditions.
+For further information use the contact form at https://www.qt.io/contact-us.
diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt
new file mode 100644
index 000000000..2071b23b0
--- /dev/null
+++ b/LICENSES/MIT.txt
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/LICENSES/MPL-2.0.txt b/LICENSES/MPL-2.0.txt
new file mode 100644
index 000000000..ee6256cdb
--- /dev/null
+++ b/LICENSES/MPL-2.0.txt
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/LICENSES/Qt-GPL-exception-1.0.txt b/LICENSES/Qt-GPL-exception-1.0.txt
new file mode 100644
index 000000000..d0322bf0e
--- /dev/null
+++ b/LICENSES/Qt-GPL-exception-1.0.txt
@@ -0,0 +1,22 @@
+The Qt Company GPL Exception 1.0
+
+Exception 1:
+
+As a special exception you may create a larger work which contains the
+output of this application and distribute that work under terms of your
+choice, so long as the work is not otherwise derived from or based on
+this application and so long as the work does not in itself generate
+output that contains the output from this application in its original
+or modified form.
+
+Exception 2:
+
+As a special exception, you have permission to combine this application
+with Plugins licensed under the terms of your choice, to produce an
+executable, and to copy and distribute the resulting executable under
+the terms of your choice. However, the executable must be accompanied
+by a prominent notice offering all users of the executable the entire
+source code to this application, excluding the source code of the
+independent modules, but including any changes you have made to this
+application, under the terms of this license.
+
diff --git a/LICENSES/Zlib.txt b/LICENSES/Zlib.txt
new file mode 100644
index 000000000..e0e3605ba
--- /dev/null
+++ b/LICENSES/Zlib.txt
@@ -0,0 +1,11 @@
+zlib License
+
+This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
diff --git a/cmake/FindAVFoundation.cmake b/cmake/FindAVFoundation.cmake
index feefe3d77..3dad5d677 100644
--- a/cmake/FindAVFoundation.cmake
+++ b/cmake/FindAVFoundation.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
find_library(AVFoundation_LIBRARY NAMES AVFoundation)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(AVFoundation DEFAULT_MSG AVFoundation_LIBRARY)
diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake
index 1ee312578..86be24dd7 100644
--- a/cmake/FindFFmpeg.cmake
+++ b/cmake/FindFFmpeg.cmake
@@ -1,3 +1,5 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#.rst:
# FindFFmpeg
# ----------
@@ -18,10 +20,7 @@
# ::
#
# FFMPEG_FOUND - System has the all required components.
-# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers.
-# FFMPEG_LIBRARIES - Link these to use the required ffmpeg components.
-# FFMPEG_LIBRARY_DIRS - Link directories
-# FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components.
+# FFMPEG_SHARED_LIBRARIES - Found FFmpeg shared libraries.
#
# For each of the components it will additionally set.
#
@@ -59,8 +58,6 @@
# Copyright (c) 2011, Michael Jansen, <kde@michael-jansen.biz>
# Copyright (c) 2017, Alexander Drozdov, <adrozdoff@gmail.com>
#
-# Redistribution and use is allowed according to the terms of the BSD license.
-# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
include(FindPackageHandleStandardArgs)
@@ -69,14 +66,42 @@ if (NOT FFmpeg_FIND_COMPONENTS)
set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL)
endif ()
+if (QT_DEPLOY_FFMPEG AND BUILD_SHARED_LIBS)
+ set(shared_libs_desired TRUE)
+endif()
+
+# finds FFmpeg libs, including symlinks, for the specified component.
+macro(find_shared_libs_for_component _component)
+ # the searching pattern is pretty rough but it seems to be sufficient to gather dynamic libs
+ get_filename_component(name_we ${${_component}_LIBRARY} NAME_WE)
+
+ if (WIN32)
+ get_filename_component(dir_name ${${_component}_LIBRARY_DIR} NAME)
+ if (${dir_name} STREQUAL "lib" AND EXISTS "${${_component}_LIBRARY_DIR}/../bin")
+ # llvm-mingv builds aux ffmpeg static libs like lib/libavutil.dll.a and cmake finds
+ # only them even though the folder bin/ contains proper *.dll and *.lib.
+
+ string(REGEX REPLACE "^lib" "" name_we "${name_we}")
+ set(shared_lib_pattern "../bin/${name_we}*${CMAKE_SHARED_LIBRARY_SUFFIX}")
+ else()
+ set(shared_lib_pattern "${name_we}*${CMAKE_SHARED_LIBRARY_SUFFIX}")
+ endif()
+
+ else()
+ set(shared_lib_pattern "${name_we}*${CMAKE_SHARED_LIBRARY_SUFFIX}*")
+ endif()
+
+ file(GLOB ${_component}_SHARED_LIBRARIES "${${_component}_LIBRARY_DIR}/${shared_lib_pattern}")
+endmacro()
+
#
### Macro: set_component_found
#
-# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present.
+# Marks the given component as found if both *_LIBRARY_NAME AND *_INCLUDE_DIRS is present.
#
-macro(set_component_found _component )
- if (${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS)
- # message(STATUS " - ${_component} found.")
+macro(set_component_found _component)
+ if (${_component}_LIBRARY_NAME AND ${_component}_INCLUDE_DIR)
+ # message(STATUS " - ${_component} found.")
set(${_component}_FOUND TRUE)
set(${CMAKE_FIND_PACKAGE_NAME}_${_component}_FOUND TRUE)
else ()
@@ -114,7 +139,12 @@ macro(find_component _component _pkgconfig _library _header)
list(APPEND CMAKE_FIND_ROOT_PATH "${FFMPEG_ROOT}")
endif()
- find_path(${_component}_INCLUDE_DIRS ${_header}
+ if (${_component}_INCLUDE_DIR AND NOT EXISTS ${${_component}_INCLUDE_DIR})
+ message(STATUS "Cached include dir ${${_component}_INCLUDE_DIR} doesn't exist")
+ unset(${_component}_INCLUDE_DIR CACHE)
+ endif()
+
+ find_path(${_component}_INCLUDE_DIR ${_header}
HINTS
${PC_${_component}_INCLUDEDIR}
${PC_${_component}_INCLUDE_DIRS}
@@ -125,7 +155,17 @@ macro(find_component _component _pkgconfig _library _header)
ffmpeg include
)
- find_library(${_component}_LIBRARY NAMES ${PC_${_component}_LIBRARIES} ${_library}
+ if (shared_libs_desired AND NOT WIN32)
+ set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_SHARED_LIBRARY_SUFFIX};${CMAKE_STATIC_LIBRARY_SUFFIX}")
+ endif()
+
+ if (${_component}_LIBRARY AND NOT EXISTS ${${_component}_LIBRARY})
+ message(STATUS "Cached library ${${_component}_LIBRARY} doesn't exist")
+ unset(${_component}_LIBRARY CACHE)
+ endif()
+
+ find_library(${_component}_LIBRARY
+ NAMES ${PC_${_component}_LIBRARIES} ${_library}
HINTS
${PC_${_component}_LIBDIR}
${PC_${_component}_LIBRARY_DIRS}
@@ -133,46 +173,80 @@ macro(find_component _component _pkgconfig _library _header)
PATHS
${FFMPEG_DIR}
PATH_SUFFIXES
- lib
+ lib bin
)
if(FFMPEG_DIR OR FFMPEG_ROOT)
set(CMAKE_FIND_ROOT_PATH "${__find_ffmpeg_backup_root_dir}")
endif()
- get_filename_component(${_component}_LIBRARY_DIR_FROM_FIND ${${_component}_LIBRARY} DIRECTORY)
- get_filename_component(${_component}_LIBRARY_FROM_FIND ${${_component}_LIBRARY} NAME)
-
- set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.")
- set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.")
- set(${_component}_LIBRARY_DIRS ${${_component}_LIBRARY_DIR_FROM_FIND} CACHE STRING "The ${_component} library dirs.")
- set(${_component}_LIBRARIES ${${_component}_LIBRARY_FROM_FIND} CACHE STRING "The ${_component} libraries.")
+ if (${_component}_LIBRARY)
+ get_filename_component(${_component}_LIBRARY_DIR ${${_component}_LIBRARY} DIRECTORY)
+ get_filename_component(${_component}_LIBRARY_NAME ${${_component}_LIBRARY} NAME)
-# message("Libs" ${FFMPEG_DIR} ${${_component}_LIBRARIES} ${${_component}_LIBRARY_DIRS})
+ # On Windows, shared linking goes through 'integration' static libs, so we should look for shared ones anyway
+ # On Unix, we gather symlinks as well so that we could install them.
+ if (WIN32 OR ${${_component}_LIBRARY_NAME} MATCHES "\\${CMAKE_SHARED_LIBRARY_SUFFIX}$")
+ find_shared_libs_for_component(${_component})
+ endif()
-# message(STATUS "L0: ${${_component}_LIBRARIES}")
-# message(STATUS "L1: ${PC_${_component}_LIBRARIES}")
-# message(STATUS "L2: ${_library}")
-# message(STATUS "L3: ${${_component}_LIBRARY}")
-# message(STATUS "L4: ${${_component}_LIBRARY_DIRS}")
+ endif()
+ set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER})
set_component_found(${_component})
- mark_as_advanced(
- ${_component}_LIBRARY
- ${_component}_INCLUDE_DIRS
- ${_component}_LIBRARY_DIRS
- ${_component}_LIBRARIES
- ${_component}_DEFINITIONS
- ${_component}_VERSION)
+ mark_as_advanced(${_component}_LIBRARY)
endmacro()
# Clear the previously cached variables, because they are recomputed every time
# the Find script is included.
-set(FFMPEG_INCLUDE_DIRS "")
-set(FFMPEG_LIBRARIES "")
-set(FFMPEG_DEFINITIONS "")
-set(FFMPEG_LIBRARY_DIRS "")
+unset(FFMPEG_SHARED_LIBRARIES CACHE)
+
+# Check for components.
+foreach (_component ${FFmpeg_FIND_COMPONENTS})
+ string(TOLOWER ${_component} library)
+ find_component(${_component} "lib${library}" ${library} "lib${library}/${library}.h")
+
+ if (${_component}_FOUND)
+ list(APPEND FFMPEG_LIBRARIES ${${_component}_LIBRARY_NAME})
+ list(APPEND FFMPEG_DEFINITIONS ${${_component}_DEFINITIONS})
+ list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIR})
+ list(APPEND FFMPEG_LIBRARY_DIRS ${${_component}_LIBRARY_DIR})
+
+ if (${_component}_SHARED_LIBRARIES)
+ list(APPEND FFMPEG_SHARED_LIBRARIES ${${_component}_SHARED_LIBRARIES})
+ list(APPEND FFMPEG_SHARED_COMPONENTS ${_component})
+ else()
+ list(APPEND FFMPEG_STATIC_COMPONENTS ${_component})
+ endif()
+
+ mark_as_advanced(${_component}_LIBRARY_NAME ${_component}_DEFINITIONS ${_component}_INCLUDE_DIR
+ ${_component}_LIBRARY_DIR ${_component}_SHARED_LIBRARIES)
+ endif()
+endforeach()
+
+if (NOT FFMPEG_SHARED_COMPONENTS AND (ANDROID OR LINUX))
+ set(ENABLE_DYNAMIC_RESOLVE_OPENSSL_SYMBOLS TRUE CACHE INTERNAL "")
+endif()
+
+set(ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS ${LINUX} CACHE INTERNAL "")
+
+function(__try_add_dynamic_resolve_dependency dep added)
+ set(added TRUE PARENT_SCOPE)
+
+ if(ENABLE_DYNAMIC_RESOLVE_OPENSSL_SYMBOLS AND
+ (${dep} STREQUAL "ssl" OR ${dep} STREQUAL "crypto"))
+ set(DYNAMIC_RESOLVE_OPENSSL_SYMBOLS TRUE CACHE INTERNAL "")
+ elseif(ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS AND ${dep} STREQUAL "va")
+ set(DYNAMIC_RESOLVE_VAAPI_SYMBOLS TRUE CACHE INTERNAL "")
+ elseif(ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS AND ${dep} STREQUAL "va-drm")
+ set(DYNAMIC_RESOLVE_VA_DRM_SYMBOLS TRUE CACHE INTERNAL "")
+ elseif(ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS AND ${dep} STREQUAL "va-x11")
+ set(DYNAMIC_RESOLVE_VA_X11_SYMBOLS TRUE CACHE INTERNAL "")
+ else()
+ set(added FALSE PARENT_SCOPE)
+ endif()
+endfunction()
# Function parses package config file to find the static library dependencies
# and adds them to the target library.
@@ -181,26 +255,32 @@ function(__ffmpeg_internal_set_dependencies lib)
if(EXISTS ${PC_FILE})
file(READ ${PC_FILE} pcfile)
- string(REGEX REPLACE ".*Libs:([A-Za-z0-9_. \${}-]+).*" "\\1" out "${pcfile}")
- string(REGEX MATCHALL "\\-l[a-z0-9_-]+" libs_dependency ${out})
- string(REGEX MATCHALL "[A-Za-z0-9_-]+\\.lib" libs_dependency_lib ${out})
-
- string(REGEX REPLACE ".*Libs.private:([A-Za-z0-9_. \${}-]+).*" "\\1" out "${pcfile}")
- string(REGEX MATCHALL "\\-l[a-z0-9_-]+" libs_private_dependency ${out})
- string(REGEX MATCHALL "[A-Za-z0-9_-]+\\.lib" libs_private_dependency_lib ${out})
-
- list(APPEND no_sufix ${libs_dependency} ${libs_private_dependency})
- list(APPEND lib_sufix ${libs_dependency_lib} ${libs_private_dependency_lib})
-
- foreach(d ${no_sufix})
- string(REGEX REPLACE "\\-l" "" d ${d})
- if(NOT ${lib} MATCHES ${d})
- target_link_libraries(FFmpeg::${lib} INTERFACE ${d})
+ set(prefix_l "(^| )\\-l")
+ set(suffix_lib "\\.lib($| )")
+
+ string(REGEX REPLACE ".*Libs:([^\n\r]+).*" "\\1" out "${pcfile}")
+ string(REGEX MATCHALL "${prefix_l}[^ ]+" libs_dependency ${out})
+ string(REGEX MATCHALL "[^ ]+${suffix_lib}" libs_dependency_lib ${out})
+
+ string(REGEX REPLACE ".*Libs.private:([^\n\r]+).*" "\\1" out "${pcfile}")
+ string(REGEX MATCHALL "${prefix_l}[^ ]+" libs_private_dependency ${out})
+ string(REGEX MATCHALL "[^ ]+${suffix_lib}" libs_private_dependency_lib ${out})
+
+ list(APPEND deps_no_suffix ${libs_dependency} ${libs_private_dependency})
+ foreach(dependency ${deps_no_suffix})
+ string(REGEX REPLACE ${prefix_l} "" dependency ${dependency})
+ if(NOT ${lib} STREQUAL ${dependency})
+ __try_add_dynamic_resolve_dependency(${dependency} added)
+ if(NOT added)
+ target_link_libraries(FFmpeg::${lib} INTERFACE ${dependency})
+ endif()
endif()
endforeach()
- foreach(d ${lib_sufix})
- string(REGEX REPLACE "\\.lib" "" d ${d})
- target_link_libraries(FFmpeg::${lib} INTERFACE ${d})
+
+ list(APPEND deps_lib_suffix ${libs_dependency_lib} ${libs_private_dependency_lib})
+ foreach(dependency ${deps_lib_suffix})
+ string(REGEX REPLACE ${suffix_lib} "" dependency ${dependency})
+ target_link_libraries(FFmpeg::${lib} INTERFACE ${dependency})
endforeach()
endif()
endfunction()
@@ -208,65 +288,61 @@ endfunction()
# Check for cached results. If there are skip the costly part.
#if (NOT FFMPEG_LIBRARIES)
- # Check for all possible component.
- find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h)
- find_component(AVFORMAT libavformat avformat libavformat/avformat.h)
- find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h)
- find_component(AVUTIL libavutil avutil libavutil/avutil.h)
- find_component(AVFILTER libavfilter avfilter libavfilter/avfilter.h)
- find_component(SWSCALE libswscale swscale libswscale/swscale.h)
- find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h)
- find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h)
-
# Check if the required components were found and add their stuff to the FFMPEG_* vars.
- foreach (_component ${FFmpeg_FIND_COMPONENTS})
- if (${_component}_FOUND)
- # message(STATUS "Libs: ${${_component}_LIBRARIES} | ${PC_${_component}_LIBRARIES}")
-
- # message(STATUS "Required component ${_component} present.")
- set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARY} ${${_component}_LIBRARIES})
- set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS})
- list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS})
- list(APPEND FFMPEG_LIBRARY_DIRS ${${_component}_LIBRARY_DIRS})
+ foreach (_component ${FFmpeg_FIND_COMPONENTS})
+ if (${_component}_FOUND)
string(TOLOWER ${_component} _lowerComponent)
if (NOT TARGET FFmpeg::${_lowerComponent})
add_library(FFmpeg::${_lowerComponent} INTERFACE IMPORTED)
set_target_properties(FFmpeg::${_lowerComponent} PROPERTIES
INTERFACE_COMPILE_OPTIONS "${${_component}_DEFINITIONS}"
- INTERFACE_INCLUDE_DIRECTORIES ${${_component}_INCLUDE_DIRS}
- INTERFACE_LINK_LIBRARIES "${${_component}_LIBRARIES}"
- INTERFACE_LINK_DIRECTORIES "${${_component}_LIBRARY_DIRS}"
+ INTERFACE_INCLUDE_DIRECTORIES ${${_component}_INCLUDE_DIR}
+ INTERFACE_LINK_LIBRARIES "${${_component}_LIBRARY_NAME}"
+ INTERFACE_LINK_DIRECTORIES "${${_component}_LIBRARY_DIR}"
)
- __ffmpeg_internal_set_dependencies(${_lowerComponent})
- target_link_libraries(FFmpeg::${_lowerComponent} INTERFACE "${${_component}_LIBRARY}")
+ if(NOT ${_component}_SHARED_LIBRARIES)
+ __ffmpeg_internal_set_dependencies(${_lowerComponent})
+ endif()
+ target_link_libraries(FFmpeg::${_lowerComponent} INTERFACE "${${_component}_LIBRARY_NAME}")
if (UNIX AND NOT APPLE)
target_link_options(FFmpeg::${_lowerComponent} INTERFACE "-Wl,--exclude-libs=lib${_lowerComponent}")
endif ()
- endif()
- else()
- # message(STATUS "Required component ${_component} missing.")
+ endif()
endif()
endforeach ()
# Build the include path with duplicates removed.
- if (FFMPEG_INCLUDE_DIRS)
- list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
- endif ()
+ list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
+ list(REMOVE_DUPLICATES FFMPEG_LIBRARY_DIRS)
+ list(REMOVE_DUPLICATES FFMPEG_SHARED_LIBRARIES)
+
+ message(STATUS "FFmpeg shared libs: ${FFMPEG_SHARED_LIBRARIES}")
# cache the vars.
- set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE)
- set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE)
- set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE)
- set(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBRARY_DIRS} CACHE STRING "The FFmpeg library dirs." FORCE)
+ set(FFMPEG_SHARED_LIBRARIES ${FFMPEG_SHARED_LIBRARIES} CACHE STRING "The FFmpeg dynamic libraries." FORCE)
- mark_as_advanced(FFMPEG_INCLUDE_DIRS
- FFMPEG_LIBRARIES
- FFMPEG_DEFINITIONS
- FFMPEG_LIBRARY_DIRS)
+ mark_as_advanced(FFMPEG_SHARED_LIBRARIES)
+# endif ()
-#endif ()
+list(LENGTH FFMPEG_LIBRARY_DIRS DIRS_COUNT)
+if (${DIRS_COUNT} GREATER 1)
+ message(WARNING "One ffmpeg library dir is expected, found dirs: ${FFMPEG_LIBRARY_DIRS}")
+endif()
+
+if(FFMPEG_SHARED_COMPONENTS AND FFMPEG_STATIC_COMPONENTS)
+ message(WARNING
+ "Only static or shared components are expected\n"
+ " static components: ${FFMPEG_STATIC_COMPONENTS}\n"
+ " shared components: ${FFMPEG_SHARED_COMPONENTS}")
+endif()
+
+if (shared_libs_desired AND NOT FFMPEG_SHARED_COMPONENTS)
+ message(WARNING
+ "Shared FFmpeg libs are desired as QT_DEPLOY_FFMPEG=TRUE, but haven't been found!\n"
+ "Remove QT_DEPLOY_FFMPEG or set the proper path to shared FFmpeg via FFMPEG_DIR.")
+endif()
if (NOT TARGET FFmpeg::FFmpeg)
add_library(FFmpeg INTERFACE)
@@ -274,19 +350,15 @@ if (NOT TARGET FFmpeg::FFmpeg)
INTERFACE_COMPILE_OPTIONS "${FFMPEG_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${FFMPEG_LIBRARIES}"
- INTERFACE_LINK_DIRECTORIES "${FFMPEG_LIBRARY_DIRS}")
+ INTERFACE_LINK_DIRECTORIES "${FFMPEG_LIBRARY_DIRS}"
+ )
add_library(FFmpeg::FFmpeg ALIAS FFmpeg)
endif()
-# Now set the noncached _FOUND vars for the components.
-foreach (_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWSCALE)
- set_component_found(${_component})
-endforeach ()
-
# Compile the list of required vars
set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS)
foreach (_component ${FFmpeg_FIND_COMPONENTS})
- list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS)
+ list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARY ${_component}_INCLUDE_DIR)
endforeach ()
# Give a nice error message if some of the required vars are missing.
diff --git a/cmake/FindGObject.cmake b/cmake/FindGObject.cmake
index 19a8a678d..89a02435a 100644
--- a/cmake/FindGObject.cmake
+++ b/cmake/FindGObject.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# FindGObject
# ---------
#
@@ -20,7 +23,7 @@ qt_internal_disable_find_package_global_promotion(GLIB2::GLIB2)
if(NOT TARGET GObject::GObject)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_GOBJECT gobject-2.0 IMPORTED_TARGET)
- if (TARGET PkgConfig::PC_GOBJECT)
+ if(TARGET PkgConfig::PC_GOBJECT)
add_library(GObject::GObject INTERFACE IMPORTED)
target_link_libraries(GObject::GObject INTERFACE
PkgConfig::PC_GOBJECT
@@ -32,7 +35,7 @@ if(NOT TARGET GObject::GObject)
PATH_SUFFIXES glib-2.0/gobject/
)
find_library(GObject_LIBRARY NAMES gobject-2.0)
- if (GObject_LIBRARY AND GObject_INCLUDE_DIR)
+ if(GObject_LIBRARY AND GObject_INCLUDE_DIR)
add_library(GObject::GObject INTERFACE IMPORTED)
target_include_directories(GObject::GObject INTERFACE
${GObject_INCLUDE_DIR}
diff --git a/cmake/FindGStreamer.cmake b/cmake/FindGStreamer.cmake
index c3becd731..b8891e7ed 100644
--- a/cmake/FindGStreamer.cmake
+++ b/cmake/FindGStreamer.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# FindGStreamer
# ---------
#
@@ -94,17 +97,17 @@ endif()
# GStreamer optional components
foreach(component ${GStreamer_FIND_COMPONENTS})
- if (${component} STREQUAL "App")
+ if(${component} STREQUAL "App")
find_gstreamer_component(App gstreamer-app-1.0 gst/app/gstappsink.h gstapp-1.0)
if(TARGET GStreamer::App AND TARGET GStreamer::Base)
target_link_libraries(GStreamer::App INTERFACE GStreamer::Base)
endif()
- elseif (${component} STREQUAL "Photography")
+ elseif(${component} STREQUAL "Photography")
find_gstreamer_component(Photography gstreamer-photography-1.0 gst/interfaces/photography.h gstphotography-1.0)
if(TARGET GStreamer::Photography AND TARGET GStreamer::Core)
target_link_libraries(GStreamer::Photography INTERFACE GStreamer::Core)
endif()
- elseif (${component} STREQUAL "Gl")
+ elseif(${component} STREQUAL "Gl")
find_gstreamer_component(Gl gstreamer-gl-1.0 gst/gl/gl.h gstgl-1.0)
if(TARGET GStreamer::Gl AND TARGET GStreamer::Video AND TARGET GStreamer::Allocators)
target_link_libraries(GStreamer::Gl INTERFACE GStreamer::Video GStreamer::Allocators)
diff --git a/cmake/FindMMRenderer.cmake b/cmake/FindMMRenderer.cmake
index 5031b8ceb..43dcf3f00 100644
--- a/cmake/FindMMRenderer.cmake
+++ b/cmake/FindMMRenderer.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# FindMMRenderer
# ---------
#
diff --git a/cmake/FindMMRendererCore.cmake b/cmake/FindMMRendererCore.cmake
index b27df3892..3f07e3720 100644
--- a/cmake/FindMMRendererCore.cmake
+++ b/cmake/FindMMRendererCore.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
find_library(MMRendererCore_LIBRARY NAMES mmrndcore)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MMRendererCore DEFAULT_MSG MMRendererCore_LIBRARY)
diff --git a/cmake/FindVAAPI.cmake b/cmake/FindVAAPI.cmake
index e696b6fe4..a3ea6cd58 100644
--- a/cmake/FindVAAPI.cmake
+++ b/cmake/FindVAAPI.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
find_package(PkgConfig QUIET)
diff --git a/cmake/FindWMF.cmake b/cmake/FindWMF.cmake
index 7c6923c1e..79306a350 100644
--- a/cmake/FindWMF.cmake
+++ b/cmake/FindWMF.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# FindWMF
# ---------
#
@@ -22,11 +25,11 @@ find_library(WMF_UUID_LIBRARY uuid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
find_library(WMF_MSDMO_LIBRARY msdmo HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
find_library(WMF_OLE32_LIBRARY ole32 HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
find_library(WMF_OLEAUT32_LIBRARY oleaut32 HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
-find_library(WMF_MF_LIBRARY Mf HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
-find_library(WMF_MFUUID_LIBRARY Mfuuid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
-find_library(WMF_MFPLAT_LIBRARY Mfplat HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
-find_library(WMF_MFCORE_LIBRARY Mfcore HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
-find_library(WMF_PROPSYS_LIBRARY Propsys HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
+find_library(WMF_MF_LIBRARY mf HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
+find_library(WMF_MFUUID_LIBRARY mfuuid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
+find_library(WMF_MFPLAT_LIBRARY mfplat HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
+find_library(WMF_MFCORE_LIBRARY mfcore HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
+find_library(WMF_PROPSYS_LIBRARY propsys HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
set(WMF_LIBRARIES ${WMF_STRMIIDS_LIBRARY} ${WMF_AMSTRMID_LIBRARY} ${WMF_DMOGUIDS_LIBRARY} ${WMF_UUID_LIBRARY}
@@ -44,4 +47,6 @@ if(WMF_FOUND AND NOT TARGET WMF::WMF)
INTERFACE_LINK_LIBRARIES "${WMF_LIBRARIES}")
endif()
-mark_as_advanced(WMF_LIBRARIES)
+mark_as_advanced(WMF_LIBRARIES WMF_STRMIIDS_LIBRARY WMF_AMSTRMID_LIBRARY WMF_DMOGUIDS_LIBRARY WMF_UUID_LIBRARY
+ WMF_MSDMO_LIBRARY WMF_OLE32_LIBRARY WMF_OLEAUT32_LIBRARY WMF_MF_LIBRARY WMF_MFUUID_LIBRARY WMF_MFPLAT_LIBRARY
+ WMF_MFCORE_LIBRARY WMF_PROPSYS_LIBRARY)
diff --git a/cmake/FindWrapPulseAudio.cmake b/cmake/FindWrapPulseAudio.cmake
index 67fb7401d..ce170da63 100644
--- a/cmake/FindWrapPulseAudio.cmake
+++ b/cmake/FindWrapPulseAudio.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# FindWrapPulseAudio
# ---------
#
@@ -30,3 +33,5 @@ endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WrapPulseAudio REQUIRED_VARS
PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR WrapPulseAudio_FOUND)
+
+mark_as_advanced(PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR)
diff --git a/coin/axivion/ci_config_linux.json b/coin/axivion/ci_config_linux.json
new file mode 100644
index 000000000..1741f34e2
--- /dev/null
+++ b/coin/axivion/ci_config_linux.json
@@ -0,0 +1,45 @@
+{
+ "Project": {
+ "BuildSystemIntegration": {
+ "child_order": [
+ "GCCSetup",
+ "CMake",
+ "LinkLibraries"
+ ]
+ },
+ "CMake": {
+ "_active": true,
+ "_copy_from": "CMakeIntegration",
+ "build_environment": {},
+ "build_options": "-j4",
+ "generate_options": "--fresh",
+ "generator": "Ninja"
+ },
+ "GCCSetup": {
+ "_active": true,
+ "_copy_from": "Command",
+ "build_command": "gccsetup --cc gcc --cxx g++ --config ../../../axivion/"
+ },
+ "LinkLibraries": {
+ "_active": true,
+ "_copy_from": "AxivionLinker",
+ "input_files": [
+ "build/lib/lib*.so*.ir",
+ "build/qml/*/lib*.so*.ir",
+ "build/qml/*/*/lib*.so*.ir"
+ ],
+ "ir": "build/$(env:TESTED_MODULE_COIN).ir",
+ "plugin_files": [
+ "build/plugins/*/lib*.so*.ir"
+ ]
+ }
+ },
+ "_Format": "1.0",
+ "_Version": "7.6.2",
+ "_VersionNum": [
+ 7,
+ 6,
+ 2,
+ 12725
+ ]
+}
diff --git a/coin/instructions/run_ffmpeg_backend_tests.yaml b/coin/instructions/run_ffmpeg_backend_tests.yaml
new file mode 100644
index 000000000..92096bd33
--- /dev/null
+++ b/coin/instructions/run_ffmpeg_backend_tests.yaml
@@ -0,0 +1,40 @@
+type: Group
+enable_if:
+ condition: or
+ conditions:
+ - condition: runtime
+ env_var: TARGET_OS_COIN
+ equals_value: macos
+ - condition: runtime
+ env_var: TARGET_OSVERSION_COIN
+ contains_value: ubuntu
+ - condition: runtime
+ env_var: TARGET_OSVERSION_COIN
+ contains_value: rhel
+ - condition: runtime
+ env_var: TARGET_OSVERSION_COIN
+ contains_value: opensuse
+ - condition: runtime
+ env_var: TARGET_OSVERSION_COIN
+ contains_value: android_any
+ - condition: runtime
+ env_var: NON_QTBASE_CMAKE_ARGS
+ contains_value: "-DFFMPEG_DIR=/"
+ - condition: and
+ conditions:
+ - condition: runtime
+ env_var: TARGET_OS_COIN
+ equals_value: windows
+ - condition: or
+ conditions:
+ - condition: property
+ property: target.arch
+ not_equals_value: arm64
+ - condition: runtime
+ env_var: NON_QTBASE_CMAKE_ARGS
+ contains_value: "-DFFMPEG_DIR=C:"
+instructions:
+ - type: EnvironmentVariable
+ variableName: QT_MEDIA_BACKEND
+ variableValue: ffmpeg
+ - !include "{{qt/qtbase}}/coin_module_test_template_v3.yaml"
diff --git a/coin/module_config.yaml b/coin/module_config.yaml
index 7733000cb..baafc4ca4 100644
--- a/coin/module_config.yaml
+++ b/coin/module_config.yaml
@@ -9,4 +9,5 @@ instructions:
- !include "{{qt/qtbase}}/coin_module_build_template_v2.yaml"
Test:
+ - !include "{{qt/qtmultimedia}}/run_ffmpeg_backend_tests.yaml"
- !include "{{qt/qtbase}}/coin_module_test_docs.yaml"
diff --git a/conanfile.py b/conanfile.py
deleted file mode 100644
index 32b9eb1ca..000000000
--- a/conanfile.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#############################################################################
-##
-## Copyright (C) 2021 The Qt Company Ltd.
-## Contact: https://www.qt.io/licensing/
-##
-## This file is part of the release tools of the Qt Toolkit.
-##
-## $QT_BEGIN_LICENSE:GPL-EXCEPT$
-## Commercial License Usage
-## Licensees holding valid commercial Qt licenses may use this file in
-## accordance with the commercial license agreement provided with the
-## Software or, alternatively, in accordance with the terms contained in
-## a written agreement between you and The Qt Company. For licensing terms
-## and conditions see https://www.qt.io/terms-conditions. For further
-## information use the contact form at https://www.qt.io/contact-us.
-##
-## GNU General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU
-## General Public License version 3 as published by the Free Software
-## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-## included in the packaging of this file. Please review the following
-## information to ensure the GNU General Public License requirements will
-## be met: https://www.gnu.org/licenses/gpl-3.0.html.
-##
-## $QT_END_LICENSE$
-##
-#############################################################################
-
-from conans import ConanFile
-import re
-from pathlib import Path
-
-
-def _parse_qt_version_by_key(key: str) -> str:
- with open(Path(__file__).parent.resolve() / ".cmake.conf") as f:
- m = re.search(fr'{key} .*"(.*)"', f.read())
- return m.group(1) if m else ""
-
-
-def _get_qt_minor_version() -> str:
- return ".".join(_parse_qt_version_by_key("QT_REPO_MODULE_VERSION").split(".")[:2])
-
-
-class QtMultimedia(ConanFile):
- name = "qtmultimedia"
- license = "LGPL-3.0, GPL-2.0+, Commercial Qt License Agreement"
- author = "The Qt Company <https://www.qt.io/contact-us>"
- url = "https://code.qt.io/cgit/qt/qtmultimedia.git"
- description = (
- "Qt Multimedia is an essential module that provides a rich set of QML types and C++ "
- "classes to handle multimedia content. It also provides necessary APIs to access "
- "camera and radio functionality. The included Qt Audio Engine provides types for 3D "
- "positional audio playback and content management."
- )
- topics = "qt", "qt6", "qtmultimedia", "multimedia"
- settings = "os", "compiler", "arch", "build_type"
- # for referencing the version number and prerelease tag and dependencies info
- exports = ".cmake.conf", "dependencies.yaml"
- exports_sources = "*", "!conan*.*"
- python_requires = f"qt-conan-common/{_get_qt_minor_version()}@qt/everywhere"
- python_requires_extend = "qt-conan-common.QtLeafModule"
diff --git a/config.tests/alsa/CMakeLists.txt b/config.tests/alsa/CMakeLists.txt
index 08d643fca..8b434e11a 100644
--- a/config.tests/alsa/CMakeLists.txt
+++ b/config.tests/alsa/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from alsa.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/alsa/alsatest.cpp b/config.tests/alsa/alsatest.cpp
index ea511cd21..f57ff2a0e 100644
--- a/config.tests/alsa/alsatest.cpp
+++ b/config.tests/alsa/alsatest.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#include <alsa/asoundlib.h>
#if SND_LIB_VERSION < 0x1000a // 1.0.10
diff --git a/config.tests/avfoundation/CMakeLists.txt b/config.tests/avfoundation/CMakeLists.txt
index 64a88ab4d..4d71200f1 100644
--- a/config.tests/avfoundation/CMakeLists.txt
+++ b/config.tests/avfoundation/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from avfoundation.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/avfoundation/main.mm b/config.tests/avfoundation/main.mm
index 56cb48e5f..850c95487 100644
--- a/config.tests/avfoundation/main.mm
+++ b/config.tests/avfoundation/main.mm
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: BSD-3-Clause
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
diff --git a/config.tests/evr/CMakeLists.txt b/config.tests/evr/CMakeLists.txt
index a8dfd25da..20f4d2cff 100644
--- a/config.tests/evr/CMakeLists.txt
+++ b/config.tests/evr/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from evr.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/evr/main.cpp b/config.tests/evr/main.cpp
index c6a020522..eb72c5968 100644
--- a/config.tests/evr/main.cpp
+++ b/config.tests/evr/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#include <evr.h>
diff --git a/config.tests/gpu_vivante/CMakeLists.txt b/config.tests/gpu_vivante/CMakeLists.txt
index 08560ae7f..84f5be9ec 100644
--- a/config.tests/gpu_vivante/CMakeLists.txt
+++ b/config.tests/gpu_vivante/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from gpu_vivante.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/gpu_vivante/main.cpp b/config.tests/gpu_vivante/main.cpp
index 589fca084..4786cf54d 100644
--- a/config.tests/gpu_vivante/main.cpp
+++ b/config.tests/gpu_vivante/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
diff --git a/config.tests/gstreamer/CMakeLists.txt b/config.tests/gstreamer/CMakeLists.txt
index 8bfda9847..314362293 100644
--- a/config.tests/gstreamer/CMakeLists.txt
+++ b/config.tests/gstreamer/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from gstreamer.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/gstreamer/main.cpp b/config.tests/gstreamer/main.cpp
index 72f79611b..5b29e8a48 100644
--- a/config.tests/gstreamer/main.cpp
+++ b/config.tests/gstreamer/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#define GST_USE_UNSTABLE_API
diff --git a/config.tests/gstreamer_appsrc/CMakeLists.txt b/config.tests/gstreamer_appsrc/CMakeLists.txt
index da5730ef2..3cff11eb9 100644
--- a/config.tests/gstreamer_appsrc/CMakeLists.txt
+++ b/config.tests/gstreamer_appsrc/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from gstreamer_appsrc.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/gstreamer_appsrc/main.cpp b/config.tests/gstreamer_appsrc/main.cpp
index 2cf22944b..c50de3936 100644
--- a/config.tests/gstreamer_appsrc/main.cpp
+++ b/config.tests/gstreamer_appsrc/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#define GST_USE_UNSTABLE_API
diff --git a/config.tests/gstreamer_photography/CMakeLists.txt b/config.tests/gstreamer_photography/CMakeLists.txt
index 3d34e06cd..235182c33 100644
--- a/config.tests/gstreamer_photography/CMakeLists.txt
+++ b/config.tests/gstreamer_photography/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from gstreamer_photography.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/gstreamer_photography/main.cpp b/config.tests/gstreamer_photography/main.cpp
index 89151d23a..1ca0dbb74 100644
--- a/config.tests/gstreamer_photography/main.cpp
+++ b/config.tests/gstreamer_photography/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#define GST_USE_UNSTABLE_API
diff --git a/config.tests/linux_v4l/CMakeLists.txt b/config.tests/linux_v4l/CMakeLists.txt
index b5c098a13..1ebaf3c0e 100644
--- a/config.tests/linux_v4l/CMakeLists.txt
+++ b/config.tests/linux_v4l/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from linux_v4l.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/linux_v4l/main.cpp b/config.tests/linux_v4l/main.cpp
index 196d86147..0c468d59d 100644
--- a/config.tests/linux_v4l/main.cpp
+++ b/config.tests/linux_v4l/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: BSD-3-Clause
#include <linux/videodev2.h>
diff --git a/config.tests/mmrenderer/CMakeLists.txt b/config.tests/mmrenderer/CMakeLists.txt
index 1ecc587b0..062a7eca5 100644
--- a/config.tests/mmrenderer/CMakeLists.txt
+++ b/config.tests/mmrenderer/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from mmrenderer.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/mmrenderer/mmrenderertest.cpp b/config.tests/mmrenderer/mmrenderertest.cpp
index 5b7f4b528..949bc4aca 100644
--- a/config.tests/mmrenderer/mmrenderertest.cpp
+++ b/config.tests/mmrenderer/mmrenderertest.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: BSD-3-Clause
#include <mm/renderer.h>
diff --git a/config.tests/pulseaudio/CMakeLists.txt b/config.tests/pulseaudio/CMakeLists.txt
index 0f29ac8b9..bf47d800b 100644
--- a/config.tests/pulseaudio/CMakeLists.txt
+++ b/config.tests/pulseaudio/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from pulseaudio.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/pulseaudio/pulseaudio.cpp b/config.tests/pulseaudio/pulseaudio.cpp
index 230664232..f5392119d 100644
--- a/config.tests/pulseaudio/pulseaudio.cpp
+++ b/config.tests/pulseaudio/pulseaudio.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h>
diff --git a/config.tests/wmf/CMakeLists.txt b/config.tests/wmf/CMakeLists.txt
index 6a903cf8f..907c7f600 100644
--- a/config.tests/wmf/CMakeLists.txt
+++ b/config.tests/wmf/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from wmf.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/wmf/main.cpp b/config.tests/wmf/main.cpp
index 5d00d5c04..edd71aeca 100644
--- a/config.tests/wmf/main.cpp
+++ b/config.tests/wmf/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#include <mfapi.h>
#include <mfidl.h>
diff --git a/config.tests/wmsdk/CMakeLists.txt b/config.tests/wmsdk/CMakeLists.txt
index d0bc6c616..7f957ad74 100644
--- a/config.tests/wmsdk/CMakeLists.txt
+++ b/config.tests/wmsdk/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from wmsdk.pro.
cmake_minimum_required(VERSION 3.16)
diff --git a/config.tests/wmsdk/main.cpp b/config.tests/wmsdk/main.cpp
index 4aeeb5e57..24300ee30 100644
--- a/config.tests/wmsdk/main.cpp
+++ b/config.tests/wmsdk/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
#include <wmsdk.h>
diff --git a/configure.cmake b/configure.cmake
index 53e3f1109..68f54ce72 100644
--- a/configure.cmake
+++ b/configure.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#### Inputs
diff --git a/dependencies.yaml b/dependencies.yaml
index 55971bbcb..bc05ecd3e 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,10 +1,13 @@
dependencies:
../qtbase:
- ref: c93d279222e253cf6d525ba80a37dbd20339e012
+ ref: 4641945e45206508b44678011bb83da7722bad62
required: true
../qtdeclarative:
- ref: 1818d3dfcf92279770aa41908061d720dbcbfe9b
+ ref: 828b823938395d4d43f9b7a1b7f53f10a4a6b99b
+ required: false
+ ../qtquick3d:
+ ref: 7a03b8412f81b3f152d12e71d107ed7c5288f880
required: false
../qtshadertools:
- ref: c13dd4cb9d0dadb33c2d1e02f77cee84f7e5828d
+ ref: 2eced24fc6550f2845f5d6de33100e7c27207008
required: true
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 573b1c7c3..76d01581a 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,6 +1,11 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
qt_examples_build_begin(EXTERNAL_BUILD)
add_subdirectory(multimedia)
-add_subdirectory(multimediawidgets)
+if(QT_FEATURE_spatialaudio)
+ add_subdirectory(spatialaudio)
+endif()
qt_examples_build_end()
diff --git a/examples/examples.pro b/examples/examples.pro
index 979671672..3cda36046 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -1,3 +1,3 @@
TEMPLATE = subdirs
-SUBDIRS += multimedia multimediawidgets
+SUBDIRS += multimedia spatialaudio
diff --git a/examples/multimedia/CMakeLists.txt b/examples/multimedia/CMakeLists.txt
index 44b701120..07bf4debd 100644
--- a/examples/multimedia/CMakeLists.txt
+++ b/examples/multimedia/CMakeLists.txt
@@ -1,16 +1,19 @@
-qt_internal_add_example(audiodecoder)
-if(NOT ANDROID AND NOT IOS)
- qt_internal_add_example(devices)
-endif()
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
if(TARGET Qt::Widgets)
- qt_internal_add_example(spectrum)
- qt_internal_add_example(audiorecorder)
if(NOT ANDROID AND NOT IOS)
qt_internal_add_example(audiodevices)
endif()
- qt_internal_add_example(audiosource)
qt_internal_add_example(audiooutput)
- qt_internal_add_example(spatialaudio)
+ qt_internal_add_example(audiorecorder)
+ qt_internal_add_example(audiosource)
+ qt_internal_add_example(camera)
+ qt_internal_add_example(player)
+ qt_internal_add_example(spectrum)
+ qt_internal_add_example(videographicsitem)
+ qt_internal_add_example(videowidget)
+ qt_internal_add_example(screencapture)
endif()
if(TARGET Qt::Quick)
qt_internal_add_example(declarative-camera)
diff --git a/examples/multimedia/audiodecoder/audiodecoder.cpp b/examples/multimedia/audiodecoder/audiodecoder.cpp
deleted file mode 100644
index c65a3838c..000000000
--- a/examples/multimedia/audiodecoder/audiodecoder.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/examples/multimedia/audiodecoder/audiodecoder.h b/examples/multimedia/audiodecoder/audiodecoder.h
deleted file mode 100644
index d75eecb8d..000000000
--- a/examples/multimedia/audiodecoder/audiodecoder.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef AUDIODECODER_H
-#define AUDIODECODER_H
-
-#include <QAudioDecoder>
-#include <QSoundEffect>
-#include <QTextStream>
-#include <QAudioDecoder>
-#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/examples/multimedia/audiodecoder/main.cpp b/examples/multimedia/audiodecoder/main.cpp
deleted file mode 100644
index 2f6637b22..000000000
--- a/examples/multimedia/audiodecoder/main.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "audiodecoder.h"
-
-#include <QCoreApplication>
-#include <QDir>
-#include <QFileInfo>
-#include <QTextStream>
-#ifdef Q_OS_ANDROID
-#include <QApplication>
-#include <QFileDialog>
-#include <QMessageBox>
-#include <qstandardpaths.h>
-#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/examples/multimedia/audiodevices/CMakeLists.txt b/examples/multimedia/audiodevices/CMakeLists.txt
index 6bf07e6e5..a3003c4c5 100644
--- a/examples/multimedia/audiodevices/CMakeLists.txt
+++ b/examples/multimedia/audiodevices/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(audiodevices LANGUAGES CXX)
diff --git a/examples/multimedia/audiodevices/audiodevices.cpp b/examples/multimedia/audiodevices/audiodevices.cpp
index 812613df1..30f52e0c9 100644
--- a/examples/multimedia/audiodevices/audiodevices.cpp
+++ b/examples/multimedia/audiodevices/audiodevices.cpp
@@ -1,57 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiodevices.h"
-#include <qmediadevices.h>
-#include <qcameradevice.h>
-#include <qmediaformat.h>
+
+#include <QCameraDevice>
+#include <QMediaDevices>
+#include <QMediaFormat>
+
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
// Utility functions for converting QAudioFormat fields into text
@@ -71,19 +29,46 @@ static QString toString(QAudioFormat::SampleFormat sampleFormat)
}
}
-AudioDevicesBase::AudioDevicesBase(QWidget *parent)
- : QMainWindow(parent)
+AudioDevicesBase::AudioDevicesBase(QWidget *parent) : QMainWindow(parent)
{
setupUi(this);
}
AudioDevicesBase::~AudioDevicesBase() = default;
+AudioTest::AudioTest(QWidget *parent) : AudioDevicesBase(parent), m_devices(new QMediaDevices(this))
+{
+ init();
+}
-AudioTest::AudioTest(QWidget *parent)
- : AudioDevicesBase(parent),
- m_devices(new QMediaDevices(this))
+void AudioTest::init()
{
+#if QT_CONFIG(permissions)
+ // camera
+ QCameraPermission cameraPermission;
+ switch (qApp->checkPermission(cameraPermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(cameraPermission, this, &AudioTest::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, &AudioTest::init);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Microphone permission is not granted!");
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+#endif
m_devices->videoInputs();
qDebug() << "<<<<<<<<<<<<<<<<<<";
QMediaFormat().supportedFileFormats(QMediaFormat::Encode);
@@ -92,7 +77,8 @@ AudioTest::AudioTest(QWidget *parent)
connect(deviceBox, QOverload<int>::of(&QComboBox::activated), this, &AudioTest::deviceChanged);
connect(sampleRateSpinBox, &QSpinBox::valueChanged, this, &AudioTest::sampleRateChanged);
connect(channelsSpinBox, &QSpinBox::valueChanged, this, &AudioTest::channelChanged);
- connect(sampleFormatBox, QOverload<int>::of(&QComboBox::activated), this, &AudioTest::sampleFormatChanged);
+ connect(sampleFormatBox, QOverload<int>::of(&QComboBox::activated), this,
+ &AudioTest::sampleFormatChanged);
connect(populateTableButton, &QPushButton::clicked, this, &AudioTest::populateTable);
connect(m_devices, &QMediaDevices::audioInputsChanged, this, &AudioTest::updateAudioDevices);
connect(m_devices, &QMediaDevices::audioOutputsChanged, this, &AudioTest::updateAudioDevices);
@@ -117,24 +103,23 @@ void AudioTest::test()
} else {
QAudioFormat nearest = m_deviceInfo.preferredFormat();
testResult->setText(tr("Failed"));
- nearestSampleRate->setText(QString("%1").arg(nearest.sampleRate()));
- nearestChannel->setText(QString("%1").arg(nearest.channelCount()));
+ nearestSampleRate->setText(QStringLiteral("%1").arg(nearest.sampleRate()));
+ nearestChannel->setText(QStringLiteral("%1").arg(nearest.channelCount()));
nearestSampleFormat->setText(toString(nearest.sampleFormat()));
}
- }
- else
+ } else
testResult->setText(tr("No Device"));
}
void AudioTest::updateAudioDevices()
{
deviceBox->clear();
- const auto devices = m_mode == QAudioDevice::Input ? m_devices->audioInputs() : m_devices->audioOutputs();
- for (auto &deviceInfo: devices)
+ const auto devices =
+ m_mode == QAudioDevice::Input ? m_devices->audioInputs() : m_devices->audioOutputs();
+ for (auto &deviceInfo : devices)
deviceBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));
}
-
void AudioTest::modeChanged(int idx)
{
testResult->clear();
@@ -157,16 +142,16 @@ void AudioTest::deviceChanged(int idx)
sampleRateSpinBox->clear();
sampleRateSpinBox->setMinimum(m_deviceInfo.minimumSampleRate());
sampleRateSpinBox->setMaximum(m_deviceInfo.maximumSampleRate());
- int sampleValue = qBound(m_deviceInfo.minimumSampleRate(), 48000,
- m_deviceInfo.maximumSampleRate());
+ int sampleValue =
+ qBound(m_deviceInfo.minimumSampleRate(), 48000, m_deviceInfo.maximumSampleRate());
sampleRateSpinBox->setValue(sampleValue);
m_settings.setSampleRate(sampleValue);
channelsSpinBox->clear();
channelsSpinBox->setMinimum(m_deviceInfo.minimumChannelCount());
channelsSpinBox->setMaximum(m_deviceInfo.maximumChannelCount());
- int channelValue = qBound(m_deviceInfo.minimumChannelCount(), 2,
- m_deviceInfo.maximumChannelCount());
+ int channelValue =
+ qBound(m_deviceInfo.minimumChannelCount(), 2, m_deviceInfo.maximumChannelCount());
channelsSpinBox->setValue(channelValue);
sampleFormatBox->clear();
@@ -189,12 +174,16 @@ void AudioTest::populateTable()
QTableWidgetItem *sampleTypeItem = new QTableWidgetItem(toString(sampleFormat));
allFormatsTable->setItem(row, 2, sampleTypeItem);
- QTableWidgetItem *sampleRateItem = new QTableWidgetItem(QString("%1 - %2")
- .arg(m_deviceInfo.minimumSampleRate()).arg(m_deviceInfo.maximumSampleRate()));
+ QTableWidgetItem *sampleRateItem =
+ new QTableWidgetItem(QStringLiteral("%1 - %2")
+ .arg(m_deviceInfo.minimumSampleRate())
+ .arg(m_deviceInfo.maximumSampleRate()));
allFormatsTable->setItem(row, 0, sampleRateItem);
- QTableWidgetItem *channelsItem = new QTableWidgetItem(QString("%1 - %2")
- .arg(m_deviceInfo.minimumChannelCount()).arg(m_deviceInfo.maximumChannelCount()));
+ QTableWidgetItem *channelsItem =
+ new QTableWidgetItem(QStringLiteral("%1 - %2")
+ .arg(m_deviceInfo.minimumChannelCount())
+ .arg(m_deviceInfo.maximumChannelCount()));
allFormatsTable->setItem(row, 1, channelsItem);
++row;
@@ -217,3 +206,5 @@ void AudioTest::sampleFormatChanged(int idx)
auto formats = m_deviceInfo.supportedSampleFormats();
m_settings.setSampleFormat(formats.at(idx));
}
+
+#include "moc_audiodevices.cpp"
diff --git a/examples/multimedia/audiodevices/audiodevices.h b/examples/multimedia/audiodevices/audiodevices.h
index a2c5325bc..efe4e373c 100644
--- a/examples/multimedia/audiodevices/audiodevices.h
+++ b/examples/multimedia/audiodevices/audiodevices.h
@@ -1,67 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef AUDIODEVICES_H
#define AUDIODEVICES_H
+#include "ui_audiodevicesbase.h"
+
#include <QAudioDevice>
-#include <QMediaDevices>
#include <QMainWindow>
+#include <QMediaDevices>
#include <QObject>
-#include "ui_audiodevicesbase.h"
-
class AudioDevicesBase : public QMainWindow, public Ui::AudioDevicesBase
{
public:
- AudioDevicesBase(QWidget *parent = 0);
+ AudioDevicesBase(QWidget *parent = nullptr);
virtual ~AudioDevicesBase();
};
@@ -79,6 +32,7 @@ private:
QMediaDevices *m_devices = nullptr;
private slots:
+ void init();
void updateAudioDevices();
void modeChanged(int idx);
void deviceChanged(int idx);
@@ -87,8 +41,6 @@ private slots:
void sampleFormatChanged(int idx);
void test();
void populateTable();
-
};
#endif
-
diff --git a/examples/multimedia/audiodevices/doc/src/audiodevices.qdoc b/examples/multimedia/audiodevices/doc/src/audiodevices.qdoc
index 4ff9d7f80..1aac6abb0 100644
--- a/examples/multimedia/audiodevices/doc/src/audiodevices.qdoc
+++ b/examples/multimedia/audiodevices/doc/src/audiodevices.qdoc
@@ -1,34 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example multimedia/audiodevices
+ \example audiodevices
\title Audio Devices Example
\ingroup multimedia_examples
+ \ingroup audio_examples
+ \examplecategory {Multimedia}
\brief Testing the available audio devices and their configuration.
\meta {tag} {widgets}
diff --git a/examples/multimedia/audiodevices/main.cpp b/examples/multimedia/audiodevices/main.cpp
index 9042d391a..411f67092 100644
--- a/examples/multimedia/audiodevices/main.cpp
+++ b/examples/multimedia/audiodevices/main.cpp
@@ -1,57 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtWidgets>
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiodevices.h"
+#include <QApplication>
+
int main(int argv, char **args)
{
QApplication app(argv, args);
diff --git a/examples/multimedia/audiooutput/CMakeLists.txt b/examples/multimedia/audiooutput/CMakeLists.txt
index 09e6d13ac..9308463d2 100644
--- a/examples/multimedia/audiooutput/CMakeLists.txt
+++ b/examples/multimedia/audiooutput/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(audiooutput LANGUAGES CXX)
diff --git a/examples/multimedia/audiooutput/audiooutput.cpp b/examples/multimedia/audiooutput/audiooutput.cpp
index 381ccca1d..809b79293 100644
--- a/examples/multimedia/audiooutput/audiooutput.cpp
+++ b/examples/multimedia/audiooutput/audiooutput.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiooutput.h"
@@ -54,8 +7,8 @@
#include <QAudioSink>
#include <QDebug>
#include <QVBoxLayout>
-#include <qmath.h>
-#include <qendian.h>
+#include <QtEndian>
+#include <QtMath>
Generator::Generator(const QAudioFormat &format, qint64 durationUs, int sampleRate)
{
@@ -88,9 +41,10 @@ void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int
while (length) {
// Produces value (-1..1)
- const qreal x = qSin(2 * M_PI * sampleRate * qreal(sampleIndex++ % format.sampleRate()) / format.sampleRate());
- for (int i=0; i<format.channelCount(); ++i) {
- switch(format.sampleFormat()) {
+ const qreal x = qSin(2 * M_PI * sampleRate * qreal(sampleIndex++ % format.sampleRate())
+ / format.sampleRate());
+ for (int i = 0; i < format.channelCount(); ++i) {
+ switch (format.sampleFormat()) {
case QAudioFormat::UInt8:
*reinterpret_cast<quint8 *>(ptr) = static_cast<quint8>((1.0 + x) / 2 * 255);
break;
@@ -98,7 +52,8 @@ void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int
*reinterpret_cast<qint16 *>(ptr) = static_cast<qint16>(x * 32767);
break;
case QAudioFormat::Int32:
- *reinterpret_cast<qint32 *>(ptr) = static_cast<qint32>(x * std::numeric_limits<qint32>::max());
+ *reinterpret_cast<qint32 *>(ptr) =
+ static_cast<qint32>(x * std::numeric_limits<qint32>::max());
break;
case QAudioFormat::Float:
*reinterpret_cast<float *>(ptr) = x;
@@ -140,9 +95,7 @@ qint64 Generator::bytesAvailable() const
return m_buffer.size() + QIODevice::bytesAvailable();
}
-AudioTest::AudioTest()
- : m_devices(new QMediaDevices(this)),
- m_pushTimer(new QTimer(this))
+AudioTest::AudioTest() : m_devices(new QMediaDevices(this)), m_pushTimer(new QTimer(this))
{
initializeWindow();
initializeAudio(m_devices->defaultAudioOutput());
@@ -161,11 +114,12 @@ void AudioTest::initializeWindow()
m_deviceBox = new QComboBox(this);
const QAudioDevice &defaultDeviceInfo = m_devices->defaultAudioOutput();
m_deviceBox->addItem(defaultDeviceInfo.description(), QVariant::fromValue(defaultDeviceInfo));
- for (auto &deviceInfo: m_devices->audioOutputs()) {
+ for (auto &deviceInfo : m_devices->audioOutputs()) {
if (deviceInfo != defaultDeviceInfo)
m_deviceBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));
}
- connect(m_deviceBox, QOverload<int>::of(&QComboBox::activated), this, &AudioTest::deviceChanged);
+ connect(m_deviceBox, QOverload<int>::of(&QComboBox::activated), this,
+ &AudioTest::deviceChanged);
connect(m_devices, &QMediaDevices::audioOutputsChanged, this, &AudioTest::updateAudioDevices);
layout->addWidget(m_deviceBox);
@@ -205,8 +159,7 @@ void AudioTest::initializeAudio(const QAudioDevice &deviceInfo)
m_audioOutput.reset(new QAudioSink(deviceInfo, format));
m_generator->start();
- qreal initialVolume = QAudio::convertVolume(m_audioOutput->volume(),
- QAudio::LinearVolumeScale,
+ qreal initialVolume = QAudio::convertVolume(m_audioOutput->volume(), QAudio::LinearVolumeScale,
QAudio::LogarithmicVolumeScale);
m_volumeSlider->setValue(qRound(initialVolume * 100));
toggleMode();
@@ -222,8 +175,7 @@ void AudioTest::deviceChanged(int index)
void AudioTest::volumeChanged(int value)
{
- qreal linearVolume = QAudio::convertVolume(value / qreal(100),
- QAudio::LogarithmicVolumeScale,
+ qreal linearVolume = QAudio::convertVolume(value / qreal(100), QAudio::LogarithmicVolumeScale,
QAudio::LinearVolumeScale);
m_audioOutput->setVolume(linearVolume);
@@ -233,7 +185,7 @@ void AudioTest::updateAudioDevices()
{
m_deviceBox->clear();
const QList<QAudioDevice> devices = m_devices->audioOutputs();
- for (const QAudioDevice &deviceInfo: devices)
+ for (const QAudioDevice &deviceInfo : devices)
m_deviceBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));
}
@@ -244,11 +196,11 @@ void AudioTest::toggleMode()
toggleSuspendResume();
if (m_pullMode) {
- //switch to pull mode (QAudioSink pulls from Generator as needed)
+ // switch to pull mode (QAudioSink pulls from Generator as needed)
m_modeButton->setText(tr("Enable push mode"));
m_audioOutput->start(m_generator.data());
} else {
- //switch to push mode (periodically push to QAudioSink using a timer)
+ // switch to push mode (periodically push to QAudioSink using a timer)
m_modeButton->setText(tr("Enable pull mode"));
auto io = m_audioOutput->start();
m_pushTimer->disconnect();
@@ -261,7 +213,7 @@ void AudioTest::toggleMode()
QByteArray buffer(len, 0);
len = m_generator->read(buffer.data(), len);
if (len)
- io->write(buffer.data(), len);
+ io->write(buffer.data(), len);
});
m_pushTimer->start(10);
@@ -272,7 +224,8 @@ void AudioTest::toggleMode()
void AudioTest::toggleSuspendResume()
{
- if (m_audioOutput->state() == QAudio::SuspendedState || m_audioOutput->state() == QAudio::StoppedState) {
+ if (m_audioOutput->state() == QAudio::SuspendedState
+ || m_audioOutput->state() == QAudio::StoppedState) {
m_audioOutput->resume();
m_suspendResumeButton->setText(tr("Suspend playback"));
} else if (m_audioOutput->state() == QAudio::ActiveState) {
@@ -283,3 +236,4 @@ void AudioTest::toggleSuspendResume()
}
}
+#include "moc_audiooutput.cpp"
diff --git a/examples/multimedia/audiooutput/audiooutput.h b/examples/multimedia/audiooutput/audiooutput.h
index 1d3792aff..3a82c57c4 100644
--- a/examples/multimedia/audiooutput/audiooutput.h
+++ b/examples/multimedia/audiooutput/audiooutput.h
@@ -1,70 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef AUDIOOUTPUT_H
#define AUDIOOUTPUT_H
-#include <math.h>
-
#include <QAudioSink>
#include <QByteArray>
#include <QComboBox>
#include <QIODevice>
#include <QLabel>
#include <QMainWindow>
+#include <QMediaDevices>
#include <QObject>
#include <QPushButton>
+#include <QScopedPointer>
#include <QSlider>
#include <QTimer>
-#include <QScopedPointer>
-#include <QMediaDevices>
+
+#include <math.h>
class Generator : public QIODevice
{
diff --git a/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc b/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc
index 6aaf0639d..93636c49d 100644
--- a/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc
+++ b/examples/multimedia/audiooutput/doc/src/audiooutput.qdoc
@@ -1,34 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example multimedia/audiooutput
+ \example audiooutput
\title Audio Output Example
\ingroup multimedia_examples
+ \ingroup audio_examples
+ \examplecategory {Multimedia}
\brief Enabling audio playback using the QAudioSink class.
\meta {tag} {widgets}
diff --git a/examples/multimedia/audiooutput/main.cpp b/examples/multimedia/audiooutput/main.cpp
index 9ed586095..8c652f0ff 100644
--- a/examples/multimedia/audiooutput/main.cpp
+++ b/examples/multimedia/audiooutput/main.cpp
@@ -1,57 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QApplication>
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiooutput.h"
+#include <QApplication>
+
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
diff --git a/examples/multimedia/audiorecorder/CMakeLists.txt b/examples/multimedia/audiorecorder/CMakeLists.txt
index af0e23e41..6ff9306fe 100644
--- a/examples/multimedia/audiorecorder/CMakeLists.txt
+++ b/examples/multimedia/audiorecorder/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(audiorecorder LANGUAGES CXX)
diff --git a/examples/multimedia/audiorecorder/audiolevel.cpp b/examples/multimedia/audiorecorder/audiolevel.cpp
index b13e7fbc8..444dca9a4 100644
--- a/examples/multimedia/audiorecorder/audiolevel.cpp
+++ b/examples/multimedia/audiorecorder/audiolevel.cpp
@@ -1,58 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiolevel.h"
+
#include <QPainter>
-AudioLevel::AudioLevel(QWidget *parent)
- : QWidget(parent)
+AudioLevel::AudioLevel(QWidget *parent) : QWidget(parent)
{
setMinimumHeight(15);
setMaximumHeight(50);
@@ -77,3 +30,4 @@ void AudioLevel::paintEvent(QPaintEvent *event)
// clear the rest of the control
painter.fillRect(widthLevel, 0, width(), height(), Qt::black);
}
+
diff --git a/examples/multimedia/audiorecorder/audiolevel.h b/examples/multimedia/audiorecorder/audiolevel.h
index 3c71264c5..7a42a8d01 100644
--- a/examples/multimedia/audiorecorder/audiolevel.h
+++ b/examples/multimedia/audiorecorder/audiolevel.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef AUDIOLEVEL_H
#define AUDIOLEVEL_H
@@ -57,7 +10,7 @@ class AudioLevel : public QWidget
{
Q_OBJECT
public:
- explicit AudioLevel(QWidget *parent = 0);
+ explicit AudioLevel(QWidget *parent = nullptr);
// Using [0; 1.0] range
void setLevel(qreal level);
diff --git a/examples/multimedia/audiorecorder/audiorecorder.cpp b/examples/multimedia/audiorecorder/audiorecorder.cpp
index 992e0a486..4131d5d51 100644
--- a/examples/multimedia/audiorecorder/audiorecorder.cpp
+++ b/examples/multimedia/audiorecorder/audiorecorder.cpp
@@ -1,125 +1,103 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiorecorder.h"
#include "audiolevel.h"
-
#include "ui_audiorecorder.h"
-#include <QMediaRecorder>
+#include <QAudioBuffer>
+#include <QAudioDevice>
+#include <QAudioInput>
#include <QDir>
#include <QFileDialog>
+#include <QImageCapture>
+#include <QMediaDevices>
+#include <QMediaFormat>
#include <QMediaRecorder>
-#include <QStandardPaths>
-#include <qmediadevices.h>
-#include <qmediaformat.h>
-#include <qaudiodevice.h>
-#include <qaudiobuffer.h>
-#include <qaudioinput.h>
-#include <qimagecapture.h>
#include <QMimeType>
+#include <QStandardPaths>
+
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
static QList<qreal> getBufferLevels(const QAudioBuffer &buffer);
-AudioRecorder::AudioRecorder()
- : ui(new Ui::AudioRecorder)
+AudioRecorder::AudioRecorder() : ui(new Ui::AudioRecorder)
{
ui->setupUi(this);
- m_audioRecorder = new QMediaRecorder(this);
- m_captureSession.setRecorder(m_audioRecorder);
- m_captureSession.setAudioInput(new QAudioInput(this));
- // ### replace with a monitoring output once we have it.
-// m_probe = new QAudioProbe(this);
-// connect(m_probe, &QAudioProbe::audioBufferProbed,
-// this, &AudioRecorder::processBuffer);
-// m_probe->setSource(m_audioRecorder);
-
- //audio devices
- ui->audioDeviceBox->addItem(tr("Default"), QVariant(QString()));
- for (auto device: QMediaDevices::audioInputs()) {
- auto name = device.description();
- ui->audioDeviceBox->addItem(name, QVariant::fromValue(device));
- }
-
- //audio codecs and container formats
- updateFormats();
- connect(ui->audioCodecBox, &QComboBox::currentIndexChanged, this, &AudioRecorder::updateFormats);
- connect(ui->containerBox, &QComboBox::currentIndexChanged, this, &AudioRecorder::updateFormats);
-
- //sample rate
- ui->sampleRateBox->setRange(m_captureSession.audioInput()->device().minimumSampleRate(),
- m_captureSession.audioInput()->device().maximumSampleRate());
- ui->sampleRateBox->setValue(qBound(m_captureSession.audioInput()->device().minimumSampleRate(), 44100,
- m_captureSession.audioInput()->device().maximumSampleRate()));
-
- //channels
+ // channels
ui->channelsBox->addItem(tr("Default"), QVariant(-1));
ui->channelsBox->addItem(QStringLiteral("1"), QVariant(1));
ui->channelsBox->addItem(QStringLiteral("2"), QVariant(2));
ui->channelsBox->addItem(QStringLiteral("4"), QVariant(4));
- //quality
+ // quality
ui->qualitySlider->setRange(0, int(QImageCapture::VeryHighQuality));
ui->qualitySlider->setValue(int(QImageCapture::NormalQuality));
- //bit rates:
+ // bit rates:
ui->bitrateBox->addItem(tr("Default"), QVariant(0));
ui->bitrateBox->addItem(QStringLiteral("32000"), QVariant(32000));
ui->bitrateBox->addItem(QStringLiteral("64000"), QVariant(64000));
ui->bitrateBox->addItem(QStringLiteral("96000"), QVariant(96000));
ui->bitrateBox->addItem(QStringLiteral("128000"), QVariant(128000));
- connect(m_audioRecorder, &QMediaRecorder::durationChanged, this, &AudioRecorder::updateProgress);
- connect(m_audioRecorder, &QMediaRecorder::recorderStateChanged, this, &AudioRecorder::onStateChanged);
- connect(m_audioRecorder, &QMediaRecorder::errorChanged, this, &AudioRecorder::displayErrorMessage);
+ // audio input initialization
+ init();
+}
+
+void AudioRecorder::init()
+{
+#if QT_CONFIG(permissions)
+ QMicrophonePermission microphonePermission;
+ switch (qApp->checkPermission(microphonePermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(microphonePermission, this, &AudioRecorder::init);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Microphone permission is not granted!");
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+#endif
+
+ m_audioRecorder = new QMediaRecorder(this);
+ m_captureSession.setRecorder(m_audioRecorder);
+ m_captureSession.setAudioInput(new QAudioInput(this));
+ // ### replace with a monitoring output once we have it.
+ // m_probe = new QAudioProbe(this);
+ // connect(m_probe, &QAudioProbe::audioBufferProbed,
+ // this, &AudioRecorder::processBuffer);
+ // m_probe->setSource(m_audioRecorder);
+
+ // audio devices
+ m_mediaDevices = new QMediaDevices(this);
+ connect(m_mediaDevices, &QMediaDevices::audioInputsChanged, this,
+ &AudioRecorder::updateDevices);
+ updateDevices();
+
+ // audio codecs and container formats
+ updateFormats();
+ connect(ui->audioCodecBox, &QComboBox::currentIndexChanged, this,
+ &AudioRecorder::updateFormats);
+ connect(ui->containerBox, &QComboBox::currentIndexChanged, this, &AudioRecorder::updateFormats);
+
+ // sample rate
+ ui->sampleRateBox->setRange(m_captureSession.audioInput()->device().minimumSampleRate(),
+ m_captureSession.audioInput()->device().maximumSampleRate());
+ ui->sampleRateBox->setValue(
+ qBound(m_captureSession.audioInput()->device().minimumSampleRate(), 44100,
+ m_captureSession.audioInput()->device().maximumSampleRate()));
+
+ connect(m_audioRecorder, &QMediaRecorder::durationChanged, this,
+ &AudioRecorder::updateProgress);
+ connect(m_audioRecorder, &QMediaRecorder::recorderStateChanged, this,
+ &AudioRecorder::onStateChanged);
+ connect(m_audioRecorder, &QMediaRecorder::errorChanged, this,
+ &AudioRecorder::displayErrorMessage);
}
void AudioRecorder::updateProgress(qint64 duration)
@@ -171,20 +149,20 @@ static QVariant boxValue(const QComboBox *box)
void AudioRecorder::toggleRecord()
{
if (m_audioRecorder->recorderState() == QMediaRecorder::StoppedState) {
- m_captureSession.audioInput()->setDevice(boxValue(ui->audioDeviceBox).value<QAudioDevice>());
+ m_captureSession.audioInput()->setDevice(
+ boxValue(ui->audioDeviceBox).value<QAudioDevice>());
m_audioRecorder->setMediaFormat(selectedMediaFormat());
m_audioRecorder->setAudioSampleRate(ui->sampleRateBox->value());
m_audioRecorder->setAudioBitRate(boxValue(ui->bitrateBox).toInt());
m_audioRecorder->setAudioChannelCount(boxValue(ui->channelsBox).toInt());
m_audioRecorder->setQuality(QMediaRecorder::Quality(ui->qualitySlider->value()));
- m_audioRecorder->setEncodingMode(ui->constantQualityRadioButton->isChecked() ?
- QMediaRecorder::ConstantQualityEncoding :
- QMediaRecorder::ConstantBitRateEncoding);
+ m_audioRecorder->setEncodingMode(ui->constantQualityRadioButton->isChecked()
+ ? QMediaRecorder::ConstantQualityEncoding
+ : QMediaRecorder::ConstantBitRateEncoding);
m_audioRecorder->record();
- }
- else {
+ } else {
m_audioRecorder->stop();
}
}
@@ -200,9 +178,9 @@ void AudioRecorder::togglePause()
void AudioRecorder::setOutputLocation()
{
#ifdef Q_OS_ANDROID
- QString fileName = QFileDialog::getSaveFileName(this, tr("Save Recording"),
- "output."
- + selectedMediaFormat().mimeType().preferredSuffix());
+ QString fileName = QFileDialog::getSaveFileName(
+ this, tr("Save Recording"),
+ "output." + selectedMediaFormat().mimeType().preferredSuffix());
#else
QString fileName = QFileDialog::getSaveFileName();
#endif
@@ -215,6 +193,25 @@ void AudioRecorder::displayErrorMessage()
ui->statusbar->showMessage(m_audioRecorder->errorString());
}
+void AudioRecorder::updateDevices()
+{
+ const auto currentDevice = boxValue(ui->audioDeviceBox).value<QAudioDevice>();
+ int currentDeviceIndex = 0;
+
+ ui->audioDeviceBox->clear();
+
+ ui->audioDeviceBox->addItem(tr("Default"), {});
+ for (const auto &device : m_mediaDevices->audioInputs()) {
+ const auto name = device.description();
+ ui->audioDeviceBox->addItem(name, QVariant::fromValue(device));
+
+ if (device.id() == currentDevice.id())
+ currentDeviceIndex = ui->audioDeviceBox->count() - 1;
+ }
+
+ ui->audioDeviceBox->setCurrentIndex(currentDeviceIndex);
+}
+
void AudioRecorder::updateFormats()
{
if (m_updatingFormats)
@@ -229,23 +226,27 @@ void AudioRecorder::updateFormats()
int currentIndex = 0;
ui->audioCodecBox->clear();
- ui->audioCodecBox->addItem(tr("Default audio codec"), QVariant::fromValue(QMediaFormat::AudioCodec::Unspecified));
+ 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->addItem(QMediaFormat::audioCodecDescription(codec),
+ QVariant::fromValue(codec));
}
ui->audioCodecBox->setCurrentIndex(currentIndex);
currentIndex = 0;
ui->containerBox->clear();
- ui->containerBox->addItem(tr("Default file format"), QVariant::fromValue(QMediaFormat::UnspecifiedFormat));
+ ui->containerBox->addItem(tr("Default file format"),
+ QVariant::fromValue(QMediaFormat::UnspecifiedFormat));
for (auto container : format.supportedFileFormats(QMediaFormat::Encode)) {
if (container < QMediaFormat::Mpeg4Audio) // Skip video formats
continue;
if (container == format.fileFormat())
currentIndex = ui->containerBox->count();
- ui->containerBox->addItem(QMediaFormat::fileFormatDescription(container), QVariant::fromValue(container));
+ ui->containerBox->addItem(QMediaFormat::fileFormatDescription(container),
+ QVariant::fromValue(container));
}
ui->containerBox->setCurrentIndex(currentIndex);
@@ -254,7 +255,7 @@ void AudioRecorder::updateFormats()
void AudioRecorder::clearAudioLevels()
{
- for (auto m_audioLevel : qAsConst(m_audioLevels))
+ for (auto m_audioLevel : std::as_const(m_audioLevels))
m_audioLevel->setLevel(0);
}
@@ -295,7 +296,7 @@ QList<qreal> getBufferLevels(const QAudioBuffer &buffer)
return max_values;
}
-void AudioRecorder::processBuffer(const QAudioBuffer& buffer)
+void AudioRecorder::processBuffer(const QAudioBuffer &buffer)
{
if (m_audioLevels.count() != buffer.format().channelCount()) {
qDeleteAll(m_audioLevels);
@@ -311,3 +312,4 @@ void AudioRecorder::processBuffer(const QAudioBuffer& buffer)
for (int i = 0; i < levels.count(); ++i)
m_audioLevels.at(i)->setLevel(levels.at(i));
}
+
diff --git a/examples/multimedia/audiorecorder/audiorecorder.h b/examples/multimedia/audiorecorder/audiorecorder.h
index 9ef20f0d5..2ce71c162 100644
--- a/examples/multimedia/audiorecorder/audiorecorder.h
+++ b/examples/multimedia/audiorecorder/audiorecorder.h
@@ -1,64 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef AUDIORECORDER_H
#define AUDIORECORDER_H
#include <QMainWindow>
-#include <QMediaRecorder>
#include <QMediaCaptureSession>
+#include <QMediaRecorder>
#include <QUrl>
QT_BEGIN_NAMESPACE
-namespace Ui { class AudioRecorder; }
+namespace Ui {
+class AudioRecorder;
+}
class QAudioBuffer;
+class QMediaDevices;
QT_END_NAMESPACE
class AudioLevel;
@@ -71,9 +27,10 @@ public:
AudioRecorder();
public slots:
- void processBuffer(const QAudioBuffer&);
+ void processBuffer(const QAudioBuffer &);
private slots:
+ void init();
void setOutputLocation();
void togglePause();
void toggleRecord();
@@ -82,6 +39,7 @@ private slots:
void updateProgress(qint64 pos);
void displayErrorMessage();
+ void updateDevices();
void updateFormats();
private:
@@ -92,7 +50,9 @@ private:
QMediaCaptureSession m_captureSession;
QMediaRecorder *m_audioRecorder = nullptr;
- QList<AudioLevel*> m_audioLevels;
+ QMediaDevices *m_mediaDevices = nullptr;
+
+ QList<AudioLevel *> m_audioLevels;
bool m_outputLocationSet = false;
bool m_updatingFormats = false;
};
diff --git a/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc b/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc
index c96066687..71481f24d 100644
--- a/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc
+++ b/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc
@@ -1,34 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example multimedia/audiorecorder
+ \example audiorecorder
\title Audio Recorder Example
\ingroup multimedia_examples
+ \ingroup audio_examples
+ \examplecategory {Multimedia}
\brief Discovering the available devices and supported codecs.
\meta {tag} {widgets}
diff --git a/examples/multimedia/audiorecorder/main.cpp b/examples/multimedia/audiorecorder/main.cpp
index f13940b23..407729616 100644
--- a/examples/multimedia/audiorecorder/main.cpp
+++ b/examples/multimedia/audiorecorder/main.cpp
@@ -1,56 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiorecorder.h"
-#include <QtWidgets>
+#include <QApplication>
int main(int argc, char *argv[])
{
diff --git a/examples/multimedia/audiosource/CMakeLists.txt b/examples/multimedia/audiosource/CMakeLists.txt
index 8584dc2fb..65226247a 100644
--- a/examples/multimedia/audiosource/CMakeLists.txt
+++ b/examples/multimedia/audiosource/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(audiosource LANGUAGES CXX)
@@ -19,6 +22,7 @@ qt_add_executable(audiosource
set_target_properties(audiosource PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
)
target_include_directories(audiosource PUBLIC
diff --git a/examples/multimedia/audiosource/Info.plist.in b/examples/multimedia/audiosource/Info.plist.in
new file mode 100644
index 000000000..43b966509
--- /dev/null
+++ b/examples/multimedia/audiosource/Info.plist.in
@@ -0,0 +1,44 @@
+<?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>NSMicrophoneUsageDescription</key>
+ <string>Qt Multimedia Example</string>
+
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+</dict>
+</plist>
diff --git a/examples/multimedia/audiosource/audioinput.pro b/examples/multimedia/audiosource/audioinput.pro
deleted file mode 100644
index 708d40d93..000000000
--- a/examples/multimedia/audiosource/audioinput.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-TEMPLATE = app
-TARGET = audioinput
-
-QT += multimedia widgets
-
-HEADERS = audioinput.h
-
-SOURCES = audioinput.cpp \
- main.cpp
-
-target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audioinput
-INSTALLS += target
-include(../shared/shared.pri)
diff --git a/examples/multimedia/audiosource/audiosource.cpp b/examples/multimedia/audiosource/audiosource.cpp
index ef0379e02..48e33fa2d 100644
--- a/examples/multimedia/audiosource/audiosource.cpp
+++ b/examples/multimedia/audiosource/audiosource.cpp
@@ -1,70 +1,26 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiosource.h"
-#include <stdlib.h>
-#include <math.h>
-
+#include <QAudioDevice>
+#include <QAudioSource>
#include <QDateTime>
#include <QDebug>
+#include <QLabel>
#include <QPainter>
#include <QVBoxLayout>
-#include <QAudioDevice>
-#include <QAudioSource>
-#include <qendian.h>
+#include <QtEndian>
-AudioInfo::AudioInfo(const QAudioFormat &format)
- : m_format(format)
-{
-}
+#if QT_CONFIG(permissions)
+ #include <QCoreApplication>
+ #include <QPermission>
+#endif
+
+#include <math.h>
+#include <stdlib.h>
+
+AudioInfo::AudioInfo(const QAudioFormat &format) : m_format(format) { }
void AudioInfo::start()
{
@@ -110,8 +66,7 @@ qint64 AudioInfo::writeData(const char *data, qint64 len)
return len;
}
-RenderArea::RenderArea(QWidget *parent)
- : QWidget(parent)
+RenderArea::RenderArea(QWidget *parent) : QWidget(parent)
{
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
@@ -132,8 +87,7 @@ void RenderArea::paintEvent(QPaintEvent * /* event */)
return;
const int pos = qRound(qreal(frame.width() - 1) * m_level);
- painter.fillRect(frame.left() + 1, frame.top() + 1,
- pos, frame.height() - 1, Qt::red);
+ painter.fillRect(frame.left() + 1, frame.top() + 1, pos, frame.height() - 1, Qt::red);
}
void RenderArea::setLevel(qreal value)
@@ -142,12 +96,9 @@ void RenderArea::setLevel(qreal value)
update();
}
-
-InputTest::InputTest()
- : m_devices(new QMediaDevices(this))
+InputTest::InputTest() : m_devices(new QMediaDevices(this))
{
- initializeWindow();
- initializeAudio(QMediaDevices::defaultAudioInput());
+ init();
}
void InputTest::initializeWindow()
@@ -160,7 +111,7 @@ void InputTest::initializeWindow()
m_deviceBox = new QComboBox(this);
const QAudioDevice &defaultDeviceInfo = QMediaDevices::defaultAudioInput();
m_deviceBox->addItem(defaultDeviceInfo.description(), QVariant::fromValue(defaultDeviceInfo));
- for (auto &deviceInfo: m_devices->audioInputs()) {
+ for (auto &deviceInfo : m_devices->audioInputs()) {
if (deviceInfo != defaultDeviceInfo)
m_deviceBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));
}
@@ -191,18 +142,45 @@ void InputTest::initializeAudio(const QAudioDevice &deviceInfo)
format.setSampleFormat(QAudioFormat::Int16);
m_audioInfo.reset(new AudioInfo(format));
- connect(m_audioInfo.data(), &AudioInfo::levelChanged,
- m_canvas, &RenderArea::setLevel);
+ connect(m_audioInfo.data(), &AudioInfo::levelChanged, m_canvas, &RenderArea::setLevel);
m_audioInput.reset(new QAudioSource(deviceInfo, format));
- qreal initialVolume = QAudio::convertVolume(m_audioInput->volume(),
- QAudio::LinearVolumeScale,
+ qreal initialVolume = QAudio::convertVolume(m_audioInput->volume(), QAudio::LinearVolumeScale,
QAudio::LogarithmicVolumeScale);
m_volumeSlider->setValue(qRound(initialVolume * 100));
m_audioInfo->start();
toggleMode();
}
+void InputTest::initializeErrorWindow()
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ QLabel *errorLabel = new QLabel(tr("Microphone permission is not granted!"));
+ errorLabel->setWordWrap(true);
+ errorLabel->setAlignment(Qt::AlignCenter);
+ layout->addWidget(errorLabel);
+}
+
+void InputTest::init()
+{
+#if QT_CONFIG(permissions)
+ QMicrophonePermission microphonePermission;
+ switch (qApp->checkPermission(microphonePermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(microphonePermission, this, &InputTest::init);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Microphone permission is not granted!");
+ initializeErrorWindow();
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+#endif
+ initializeWindow();
+ initializeAudio(QMediaDevices::defaultAudioInput());
+}
+
void InputTest::toggleMode()
{
m_audioInput->stop();
@@ -218,18 +196,17 @@ void InputTest::toggleMode()
if (!io)
return;
- connect(io, &QIODevice::readyRead,
- [this, io]() {
- static const qint64 BufferSize = 4096;
- const qint64 len = qMin(m_audioInput->bytesAvailable(), BufferSize);
-
- QByteArray buffer(len, 0);
- qint64 l = io->read(buffer.data(), len);
- if (l > 0) {
- const qreal level = m_audioInfo->calculateLevel(buffer.constData(), l);
- m_canvas->setLevel(level);
- }
- });
+ connect(io, &QIODevice::readyRead, [this, io]() {
+ static const qint64 BufferSize = 4096;
+ const qint64 len = qMin(m_audioInput->bytesAvailable(), BufferSize);
+
+ QByteArray buffer(len, 0);
+ qint64 l = io->read(buffer.data(), len);
+ if (l > 0) {
+ const qreal level = m_audioInfo->calculateLevel(buffer.constData(), l);
+ m_canvas->setLevel(level);
+ }
+ });
}
m_pullMode = !m_pullMode;
@@ -256,18 +233,19 @@ void InputTest::toggleSuspend()
void InputTest::deviceChanged(int index)
{
- m_audioInfo->stop();
m_audioInput->stop();
m_audioInput->disconnect(this);
+ m_audioInfo->stop();
initializeAudio(m_deviceBox->itemData(index).value<QAudioDevice>());
}
void InputTest::sliderChanged(int value)
{
- qreal linearVolume = QAudio::convertVolume(value / qreal(100),
- QAudio::LogarithmicVolumeScale,
+ qreal linearVolume = QAudio::convertVolume(value / qreal(100), QAudio::LogarithmicVolumeScale,
QAudio::LinearVolumeScale);
m_audioInput->setVolume(linearVolume);
}
+
+#include "moc_audiosource.cpp"
diff --git a/examples/multimedia/audiosource/audiosource.h b/examples/multimedia/audiosource/audiosource.h
index fce664fd9..dd9a7bc4a 100644
--- a/examples/multimedia/audiosource/audiosource.h
+++ b/examples/multimedia/audiosource/audiosource.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef AUDIOINPUT_H
#define AUDIOINPUT_H
@@ -89,7 +42,6 @@ private:
qreal m_level = 0.0; // 0.0 <= m_level <= 1.0
};
-
class RenderArea : public QWidget
{
Q_OBJECT
@@ -106,7 +58,6 @@ private:
qreal m_level = 0;
};
-
class InputTest : public QWidget
{
Q_OBJECT
@@ -117,8 +68,10 @@ public:
private:
void initializeWindow();
void initializeAudio(const QAudioDevice &deviceInfo);
+ void initializeErrorWindow();
private slots:
+ void init();
void toggleMode();
void toggleSuspend();
void deviceChanged(int index);
diff --git a/examples/multimedia/audiosource/audiosource.pro b/examples/multimedia/audiosource/audiosource.pro
new file mode 100644
index 000000000..35105dace
--- /dev/null
+++ b/examples/multimedia/audiosource/audiosource.pro
@@ -0,0 +1,13 @@
+TEMPLATE = app
+TARGET = audiosource
+
+QT += multimedia widgets
+
+HEADERS = audiosource.h
+
+SOURCES = audiosource.cpp \
+ main.cpp
+
+target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audiosource
+INSTALLS += target
+include(../shared/shared.pri)
diff --git a/examples/multimedia/audiosource/doc/src/audiosource.qdoc b/examples/multimedia/audiosource/doc/src/audiosource.qdoc
index 4c89dbeae..2f120ce77 100644
--- a/examples/multimedia/audiosource/doc/src/audiosource.qdoc
+++ b/examples/multimedia/audiosource/doc/src/audiosource.qdoc
@@ -1,34 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example multimedia/audiosource
+ \example audiosource
\title Audio Source Example
\ingroup multimedia_examples
+ \ingroup audio_examples
+ \examplecategory {Multimedia}
\brief Recording audio using the QAudioSource class.
\meta {tag} {widgets}
diff --git a/examples/multimedia/audiosource/main.cpp b/examples/multimedia/audiosource/main.cpp
index e45c2bd67..49156d860 100644
--- a/examples/multimedia/audiosource/main.cpp
+++ b/examples/multimedia/audiosource/main.cpp
@@ -1,56 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiosource.h"
-#include <QtWidgets/QApplication>
+#include <QApplication>
int main(int argv, char **args)
{
diff --git a/examples/multimediawidgets/camera/CMakeLists.txt b/examples/multimedia/camera/CMakeLists.txt
index 530126ba5..f4d97c535 100644
--- a/examples/multimediawidgets/camera/CMakeLists.txt
+++ b/examples/multimedia/camera/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(camera LANGUAGES CXX)
@@ -8,7 +11,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimediawidgets/camera")
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/camera")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia MultimediaWidgets Widgets)
diff --git a/examples/multimediawidgets/camera/android/AndroidManifest.xml b/examples/multimedia/camera/android/AndroidManifest.xml
index 29c4672cf..4af2fe92f 100644
--- a/examples/multimediawidgets/camera/android/AndroidManifest.xml
+++ b/examples/multimedia/camera/android/AndroidManifest.xml
@@ -22,13 +22,16 @@
android:extractNativeLibs="true"
android:hardwareAccelerated="true"
android:label="-- %%INSERT_APP_NAME%% --"
- android:requestLegacyExternalStorage="true">
+ android:requestLegacyExternalStorage="true"
+ android:allowBackup="true"
+ android:fullBackupOnly="false">
<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">
+ android:screenOrientation="portrait"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/examples/multimediawidgets/camera/camera.cpp b/examples/multimedia/camera/camera.cpp
index e71e209f3..bc24b1b89 100644
--- a/examples/multimediawidgets/camera/camera.cpp
+++ b/examples/multimedia/camera/camera.cpp
@@ -1,88 +1,90 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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"
+# include "ui_camera_mobile.h"
#else
-#include "ui_camera.h"
+# include "ui_camera.h"
#endif
-#include "videosettings.h"
+
#include "imagesettings.h"
#include "metadatadialog.h"
+#include "videosettings.h"
-#include <QMediaRecorder>
-#include <QVideoWidget>
-#include <QCameraDevice>
-#include <QMediaMetaData>
-#include <QMediaDevices>
#include <QAudioDevice>
#include <QAudioInput>
+#include <QCameraDevice>
+#include <QMediaDevices>
+#include <QMediaFormat>
+#include <QMediaMetaData>
+#include <QMediaRecorder>
+#include <QVideoWidget>
+#include <QLineEdit>
#include <QMessageBox>
-#include <QPalette>
+
+#include <QAction>
+#include <QActionGroup>
#include <QImage>
+#include <QKeyEvent>
+#include <QPalette>
-#include <QtWidgets>
-#include <QMediaDevices>
-#include <QMediaFormat>
+#include <QDir>
+#include <QTimer>
+
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
-Camera::Camera()
- : ui(new Ui::Camera)
+Camera::Camera() : ui(new Ui::Camera)
{
ui->setupUi(this);
+ // disable all buttons by default
+ updateCameraActive(false);
+ readyForCapture(false);
+ ui->recordButton->setEnabled(false);
+ ui->pauseButton->setEnabled(false);
+ ui->stopButton->setEnabled(false);
+ ui->metaDataButton->setEnabled(false);
+
+ // try to actually initialize camera & mic
+ init();
+}
+
+void Camera::init()
+{
+#if QT_CONFIG(permissions)
+ // camera
+ QCameraPermission cameraPermission;
+ switch (qApp->checkPermission(cameraPermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(cameraPermission, this, &Camera::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, &Camera::init);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Microphone permission is not granted!");
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+#endif
m_audioInput.reset(new QAudioInput);
m_captureSession.setAudioInput(m_audioInput.get());
- //Camera devices:
+ // Camera devices:
videoDevicesGroup = new QActionGroup(this);
videoDevicesGroup->setExclusive(true);
@@ -93,6 +95,8 @@ Camera::Camera()
connect(ui->captureWidget, &QTabWidget::currentChanged, this, &Camera::updateCaptureMode);
connect(ui->metaDataButton, &QPushButton::clicked, this, &Camera::showMetaDataDialog);
+ connect(ui->exposureCompensation, &QAbstractSlider::valueChanged, this,
+ &Camera::setExposureCompensation);
setCamera(QMediaDevices::defaultVideoInput());
}
@@ -108,54 +112,38 @@ void Camera::setCamera(const QCameraDevice &cameraDevice)
if (!m_mediaRecorder) {
m_mediaRecorder.reset(new QMediaRecorder);
m_captureSession.setRecorder(m_mediaRecorder.data());
- connect(m_mediaRecorder.data(), &QMediaRecorder::recorderStateChanged, this, &Camera::updateRecorderState);
+ connect(m_mediaRecorder.data(), &QMediaRecorder::recorderStateChanged, this,
+ &Camera::updateRecorderState);
+ connect(m_mediaRecorder.data(), &QMediaRecorder::durationChanged, this,
+ &Camera::updateRecordTime);
+ connect(m_mediaRecorder.data(), &QMediaRecorder::errorChanged, this,
+ &Camera::displayRecorderError);
}
- 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);
+ if (!m_imageCapture) {
+ m_imageCapture.reset(new QImageCapture);
+ m_captureSession.setImageCapture(m_imageCapture.get());
+ connect(m_imageCapture.get(), &QImageCapture::readyForCaptureChanged, this,
+ &Camera::readyForCapture);
+ connect(m_imageCapture.get(), &QImageCapture::imageCaptured, this,
+ &Camera::processCapturedImage);
+ connect(m_imageCapture.get(), &QImageCapture::imageSaved, this, &Camera::imageSaved);
+ connect(m_imageCapture.get(), &QImageCapture::errorOccurred, this,
+ &Camera::displayCaptureError);
+ }
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)
+void Camera::keyPressEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
return;
@@ -181,23 +169,17 @@ void Camera::keyPressEvent(QKeyEvent * event)
}
}
-void Camera::keyReleaseEvent(QKeyEvent *event)
-{
- QMainWindow::keyReleaseEvent(event);
-}
-
void Camera::updateRecordTime()
{
- QString str = QString("Recorded %1 sec").arg(m_mediaRecorder->duration()/1000);
+ QString str = tr("Recorded %1 sec").arg(m_mediaRecorder->duration() / 1000);
ui->statusbar->showMessage(str);
}
-void Camera::processCapturedImage(int requestId, const QImage& img)
+void Camera::processCapturedImage(int requestId, const QImage &img)
{
Q_UNUSED(requestId);
- QImage scaledImage = img.scaled(ui->viewfinder->size(),
- Qt::KeepAspectRatio,
- Qt::SmoothTransformation);
+ QImage scaledImage =
+ img.scaled(ui->viewfinder->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
ui->lastImagePreviewLabel->setPixmap(QPixmap::fromImage(scaledImage));
@@ -217,7 +199,6 @@ void Camera::configureCaptureSettings()
void Camera::configureVideoSettings()
{
VideoSettings settingsDialog(m_mediaRecorder.data());
- settingsDialog.setWindowFlags(settingsDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
if (settingsDialog.exec())
settingsDialog.applySettings();
@@ -225,12 +206,10 @@ void Camera::configureVideoSettings()
void Camera::configureImageSettings()
{
- ImageSettings settingsDialog(m_imageCapture);
- settingsDialog.setWindowFlags(settingsDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ ImageSettings settingsDialog(m_imageCapture.get());
- if (settingsDialog.exec()) {
+ if (settingsDialog.exec() == QDialog::Accepted)
settingsDialog.applyImageSettings();
- }
}
void Camera::record()
@@ -260,7 +239,8 @@ void Camera::takeImage()
m_imageCapture->captureToFile();
}
-void Camera::displayCaptureError(int id, const QImageCapture::Error error, const QString &errorString)
+void Camera::displayCaptureError(int id, const QImageCapture::Error error,
+ const QString &errorString)
{
Q_UNUSED(id);
Q_UNUSED(error);
@@ -325,7 +305,7 @@ void Camera::updateRecorderState(QMediaRecorder::RecorderState state)
void Camera::setExposureCompensation(int index)
{
- m_camera->setExposureCompensation(index*0.5);
+ m_camera->setExposureCompensation(index * 0.5);
}
void Camera::displayRecorderError()
@@ -415,16 +395,13 @@ void Camera::saveMetaData()
if (i == QMediaMetaData::CoverArtImage) {
QImage coverArt(val);
data.insert(key, coverArt);
- }
- else if (i == QMediaMetaData::ThumbnailImage) {
+ } else if (i == QMediaMetaData::ThumbnailImage) {
QImage thumbnail(val);
data.insert(key, thumbnail);
- }
- else if (i == QMediaMetaData::Date) {
+ } else if (i == QMediaMetaData::Date) {
QDateTime date = QDateTime::fromString(val);
data.insert(key, date);
- }
- else {
+ } else {
data.insert(key, val);
}
}
@@ -432,3 +409,4 @@ void Camera::saveMetaData()
m_mediaRecorder->setMetaData(data);
}
+#include "moc_camera.cpp"
diff --git a/examples/multimedia/camera/camera.h b/examples/multimedia/camera/camera.h
new file mode 100644
index 000000000..c92aa2d6a
--- /dev/null
+++ b/examples/multimedia/camera/camera.h
@@ -0,0 +1,104 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef CAMERA_H
+#define CAMERA_H
+
+#include <QAudioInput>
+#include <QCamera>
+#include <QImageCapture>
+#include <QMediaCaptureSession>
+#include <QMediaDevices>
+#include <QMediaMetaData>
+#include <QMediaRecorder>
+#include <QScopedPointer>
+
+#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 init();
+
+ 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 closeEvent(QCloseEvent *event) override;
+
+private:
+ Ui::Camera *ui;
+
+ QActionGroup *videoDevicesGroup = nullptr;
+
+ QMediaDevices m_devices;
+ QScopedPointer<QImageCapture> m_imageCapture;
+ QMediaCaptureSession m_captureSession;
+ QScopedPointer<QCamera> m_camera;
+ QScopedPointer<QAudioInput> m_audioInput;
+ 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/multimediawidgets/camera/camera.pro b/examples/multimedia/camera/camera.pro
index e1d98c06e..283d84640 100644
--- a/examples/multimediawidgets/camera/camera.pro
+++ b/examples/multimedia/camera/camera.pro
@@ -30,7 +30,7 @@ android|ios {
}
RESOURCES += camera.qrc
-target.path = $$[QT_INSTALL_EXAMPLES]/multimediawidgets/camera
+target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/camera
INSTALLS += target
QT += widgets
diff --git a/examples/multimediawidgets/camera/camera.qrc b/examples/multimedia/camera/camera.qrc
index a915eb596..a915eb596 100644
--- a/examples/multimediawidgets/camera/camera.qrc
+++ b/examples/multimedia/camera/camera.qrc
diff --git a/examples/multimediawidgets/camera/camera.ui b/examples/multimedia/camera/camera.ui
index 560ee7fed..560ee7fed 100644
--- a/examples/multimediawidgets/camera/camera.ui
+++ b/examples/multimedia/camera/camera.ui
diff --git a/examples/multimediawidgets/camera/camera_mobile.ui b/examples/multimedia/camera/camera_mobile.ui
index 7f269b17b..7f269b17b 100644
--- a/examples/multimediawidgets/camera/camera_mobile.ui
+++ b/examples/multimedia/camera/camera_mobile.ui
diff --git a/examples/multimedia/camera/doc/images/camera-example.png b/examples/multimedia/camera/doc/images/camera-example.png
new file mode 100644
index 000000000..f7d7e4f67
--- /dev/null
+++ b/examples/multimedia/camera/doc/images/camera-example.png
Binary files differ
diff --git a/examples/multimedia/camera/doc/src/camera.qdoc b/examples/multimedia/camera/doc/src/camera.qdoc
new file mode 100644
index 000000000..ae6877129
--- /dev/null
+++ b/examples/multimedia/camera/doc/src/camera.qdoc
@@ -0,0 +1,92 @@
+// 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
+\examplecategory {Multimedia}
+\meta {tag} {widgets}
+\brief Shows how to capture a still image or record video.
+
+\image camera-example.png
+
+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
+
+\section1 The Camera constructor
+
+The Camera constructor does some basic initialization of the user interface
+including disabling all buttons by default.
+
+\quotefromfile camera/camera.cpp
+\skipto Camera::Camera()
+\printuntil ui->metaDataButton->setEnabled(false);
+
+It seeks permissions for input device access:
+
+\skipto #if QT_CONFIG(permissions)
+\printuntil #endif
+
+Inputs are assigned:
+\printuntil updateCameras()
+
+ UI signals are connected to slots that react to the triggering event:
+\printuntil setCamera(QMediaDevices::defaultVideoInput());
+
+However, most of the work is done when the \e{setCamera()} function is called,
+passing in a \l QCameraDevice.
+
+\section1 setCamera()
+
+\c{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.
+
+\skipto void Camera::setCamera(const QCameraDevice &cameraDevice)
+\printto m_captureSession.setVideoOutput(ui->viewfinder);
+
+The still and video recording visual tabs are enabled:
+
+\printuntil updateCaptureMode();
+
+Finally the \l {QCamera::start}{start()} function of the \l{QCamera}
+object is called.
+
+\printuntil m_camera->start();
+
+\section1 Triggering capturing
+
+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.
+
+Key_CameraFocus will simply display the preview and lock the camera settings.
+\printto case Qt::Key_Camera:
+
+\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.
+
+\printuntil break;
+
+*/
diff --git a/examples/multimediawidgets/camera/images/shutter.svg b/examples/multimedia/camera/images/shutter.svg
index 18493361d..18493361d 100644
--- a/examples/multimediawidgets/camera/images/shutter.svg
+++ b/examples/multimedia/camera/images/shutter.svg
diff --git a/examples/multimedia/camera/imagesettings.cpp b/examples/multimedia/camera/imagesettings.cpp
new file mode 100644
index 000000000..c2b022a70
--- /dev/null
+++ b/examples/multimedia/camera/imagesettings.cpp
@@ -0,0 +1,84 @@
+// 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 <QCamera>
+#include <QImageCapture>
+#include <QMediaCaptureSession>
+
+#include <QComboBox>
+
+#include <QDebug>
+
+using namespace Qt::StringLiterals;
+
+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(
+ u"%1x%2"_s.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
+{
+ const int idx = box->currentIndex();
+ return idx != -1 ? box->itemData(idx) : QVariant{};
+}
+
+void ImageSettings::selectComboBoxItem(QComboBox *box, const QVariant &value)
+{
+ const int idx = box->findData(value);
+ if (idx != -1)
+ box->setCurrentIndex(idx);
+}
+
+#include "moc_imagesettings.cpp"
diff --git a/examples/multimedia/camera/imagesettings.h b/examples/multimedia/camera/imagesettings.h
new file mode 100644
index 000000000..0e2ef8e5b
--- /dev/null
+++ b/examples/multimedia/camera/imagesettings.h
@@ -0,0 +1,41 @@
+// 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/multimediawidgets/camera/imagesettings.ui b/examples/multimedia/camera/imagesettings.ui
index 8c59ca01d..8c59ca01d 100644
--- a/examples/multimediawidgets/camera/imagesettings.ui
+++ b/examples/multimedia/camera/imagesettings.ui
diff --git a/examples/multimediawidgets/camera/ios/Info.plist.in b/examples/multimedia/camera/ios/Info.plist.in
index 6a6b8db11..6a6b8db11 100644
--- a/examples/multimediawidgets/camera/ios/Info.plist.in
+++ b/examples/multimedia/camera/ios/Info.plist.in
diff --git a/examples/multimediawidgets/camera/macos/Info.plist.in b/examples/multimedia/camera/macos/Info.plist.in
index ae2d945f1..ae2d945f1 100644
--- a/examples/multimediawidgets/camera/macos/Info.plist.in
+++ b/examples/multimedia/camera/macos/Info.plist.in
diff --git a/examples/multimedia/camera/main.cpp b/examples/multimedia/camera/main.cpp
new file mode 100644
index 000000000..13554fc2b
--- /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 <QApplication>
+
+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..b2147d868
--- /dev/null
+++ b/examples/multimedia/camera/metadatadialog.cpp
@@ -0,0 +1,105 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "metadatadialog.h"
+
+#include <QMediaMetaData>
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QFileDialog>
+#include <QFormLayout>
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QScrollArea>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include <QString>
+
+static QString defaultValue(QMediaMetaData::Key key)
+{
+ switch (key) {
+ case QMediaMetaData::Title:
+ return MetaDataDialog::tr("Qt Camera Example");
+ case QMediaMetaData::Author:
+ return MetaDataDialog::tr("The Qt Company");
+ case QMediaMetaData::Date:
+ return QDateTime::currentDateTime().toString();
+ default:
+ break;
+ }
+ return {};
+}
+
+MetaDataDialog::MetaDataDialog(QWidget *parent) : QDialog(parent)
+{
+ auto *viewport = new QWidget;
+ auto *metaDataLayout = new QFormLayout(viewport);
+
+ for (int i = 0; i < QMediaMetaData::NumMetaData; ++i) {
+ const auto key = static_cast<QMediaMetaData::Key>(i);
+ QString label = QMediaMetaData::metaDataKeyToString(key);
+ auto *lineEdit = new QLineEdit(defaultValue(key));
+ lineEdit->setClearButtonEnabled(true);
+ m_metaDataFields[key] = lineEdit;
+
+ switch (key) {
+ case QMediaMetaData::ThumbnailImage: {
+ QPushButton *openThumbnail = new QPushButton(tr("Open"));
+ connect(openThumbnail, &QPushButton::clicked, this,
+ &MetaDataDialog::openThumbnailImage);
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->addWidget(lineEdit);
+ layout->addWidget(openThumbnail);
+ metaDataLayout->addRow(label, layout);
+ }
+ break;
+ case QMediaMetaData::CoverArtImage: {
+ QPushButton *openCoverArt = new QPushButton(tr("Open"));
+ connect(openCoverArt, &QPushButton::clicked, this, &MetaDataDialog::openCoverArtImage);
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->addWidget(lineEdit);
+ layout->addWidget(openCoverArt);
+ metaDataLayout->addRow(label, layout);
+ }
+ break;
+ default:
+ metaDataLayout->addRow(label, lineEdit);
+ break;
+ }
+ }
+
+ QScrollArea *scrollArea = new QScrollArea;
+ scrollArea->setWidget(viewport);
+ auto *dialogLayout = new QVBoxLayout(this);
+ dialogLayout->addWidget(scrollArea);
+
+ auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ dialogLayout->addWidget(buttonBox);
+
+ setWindowTitle(tr("Set Metadata"));
+ 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);
+}
+
+#include "moc_metadatadialog.cpp"
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..68177e0f3
--- /dev/null
+++ b/examples/multimedia/camera/videosettings.cpp
@@ -0,0 +1,196 @@
+// 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 <QAudioDevice>
+#include <QAudioInput>
+#include <QCamera>
+#include <QCameraDevice>
+#include <QMediaCaptureSession>
+#include <QMediaFormat>
+#include <QMediaRecorder>
+
+#include <QComboBox>
+#include <QSpinBox>
+
+#include <QDebug>
+#include <QTextStream>
+
+static QString toFormattedString(const QCameraFormat &cameraFormat)
+{
+ QString string;
+ QTextStream str(&string);
+ str << QVideoFrameFormat::pixelFormatToString(cameraFormat.pixelFormat())
+ << ' ' << cameraFormat.resolution().width()
+ << 'x' << cameraFormat.resolution().height()
+ << ' ' << cameraFormat.minFrameRate()
+ << '-' << cameraFormat.maxFrameRate() << "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"));
+
+ auto *camera = mediaRecorder->captureSession()->camera();
+ const QList<QCameraFormat> videoFormats = camera->cameraDevice().videoFormats();
+
+ for (const QCameraFormat &format : videoFormats)
+ ui->videoFormatBox->addItem(toFormattedString(format), QVariant::fromValue(format));
+
+ connect(ui->videoFormatBox, &QComboBox::currentIndexChanged, [this](int /*index*/) {
+ this->setFpsRange(boxValue(ui->videoFormatBox).value<QCameraFormat>());
+ });
+
+ setFpsRange(camera->cameraFormat());
+
+ 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(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::setFpsRange(const QCameraFormat &format)
+{
+ ui->fpsSlider->setRange(format.minFrameRate(), format.maxFrameRate());
+ ui->fpsSpinBox->setRange(format.minFrameRate(), format.maxFrameRate());
+}
+
+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
+{
+ const int idx = box->currentIndex();
+ return idx != -1 ? box->itemData(idx) : QVariant{};
+}
+
+void VideoSettings::selectComboBoxItem(QComboBox *box, const QVariant &value)
+{
+ const int idx = box->findData(value);
+ if (idx != -1)
+ box->setCurrentIndex(idx);
+}
+
+#include "moc_videosettings.cpp"
diff --git a/examples/multimedia/camera/videosettings.h b/examples/multimedia/camera/videosettings.h
new file mode 100644
index 000000000..358b1fb16
--- /dev/null
+++ b/examples/multimedia/camera/videosettings.h
@@ -0,0 +1,42 @@
+// 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 QCameraFormat;
+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:
+ void setFpsRange(const QCameraFormat &format);
+ 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/multimediawidgets/camera/videosettings.ui b/examples/multimedia/camera/videosettings.ui
index 3c1f71f11..3c1f71f11 100644
--- a/examples/multimediawidgets/camera/videosettings.ui
+++ b/examples/multimedia/camera/videosettings.ui
diff --git a/examples/multimediawidgets/camera/videosettings_mobile.ui b/examples/multimedia/camera/videosettings_mobile.ui
index 6584f07f9..6584f07f9 100644
--- a/examples/multimediawidgets/camera/videosettings_mobile.ui
+++ b/examples/multimedia/camera/videosettings_mobile.ui
diff --git a/examples/multimedia/declarative-camera/CMakeLists.txt b/examples/multimedia/declarative-camera/CMakeLists.txt
index 7bc0ff7b3..50b0ba48d 100644
--- a/examples/multimedia/declarative-camera/CMakeLists.txt
+++ b/examples/multimedia/declarative-camera/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(declarative-camera LANGUAGES CXX)
@@ -40,6 +43,7 @@ set(declarative-camera_resource_files
"CameraListPopup.qml"
"CameraPropertyButton.qml"
"CameraPropertyPopup.qml"
+ "FlashControl.qml"
"PhotoCaptureControls.qml"
"PhotoPreview.qml"
"Popup.qml"
diff --git a/examples/multimedia/declarative-camera/CameraButton.qml b/examples/multimedia/declarative-camera/CameraButton.qml
index 1547e015f..43837bcb3 100644
--- a/examples/multimedia/declarative-camera/CameraButton.qml
+++ b/examples/multimedia/declarative-camera/CameraButton.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
diff --git a/examples/multimedia/declarative-camera/CameraListButton.qml b/examples/multimedia/declarative-camera/CameraListButton.qml
index 2b2716eb7..b6361eac2 100644
--- a/examples/multimedia/declarative-camera/CameraListButton.qml
+++ b/examples/multimedia/declarative-camera/CameraListButton.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtMultimedia
@@ -91,23 +44,30 @@ Item {
name: "MobilePortrait"
AnchorChanges {
target: popup
- anchors.bottom: parent.top;
+ // qmllint disable incompatible-type
+ anchors.bottom: cameraListButton.top
+ anchors.left: cameraListButton.left
+ // qmllint enable incompatible-type
}
},
State {
name: "MobileLandscape"
AnchorChanges {
target: popup
- anchors.top: parent.top;
- anchors.right: parent.left;
+ // qmllint disable incompatible-type
+ anchors.top: cameraListButton.top
+ anchors.right: cameraListButton.left
+ // qmllint enable incompatible-type
}
},
State {
name: "Other"
AnchorChanges {
target: popup
- anchors.top: parent.top;
- anchors.right: parent.left;
+ // qmllint disable incompatible-type
+ anchors.top: cameraListButton.top
+ anchors.right: cameraListButton.left
+ // qmllint enable incompatible-type
}
}
]
diff --git a/examples/multimedia/declarative-camera/CameraListPopup.qml b/examples/multimedia/declarative-camera/CameraListPopup.qml
index a9ccb6275..83c9843ee 100644
--- a/examples/multimedia/declarative-camera/CameraListPopup.qml
+++ b/examples/multimedia/declarative-camera/CameraListPopup.qml
@@ -1,61 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
+import QtMultimedia
Popup {
id: cameraListPopup
property alias model : view.model
- property variant currentValue
- property variant currentItem : model[view.currentIndex]
+ property cameraDevice currentValue
+ property cameraDevice currentItem : model[view.currentIndex]
property int itemWidth : 200
property int itemHeight : 50
@@ -76,11 +31,16 @@ Popup {
clip: true
delegate: Item {
+ id: cameraListItem
+
+ required property int index
+ required property cameraDevice modelData
+
width: cameraListPopup.itemWidth
height: cameraListPopup.itemHeight
Text {
- text: modelData.description
+ text: cameraListItem.modelData.description
anchors.fill: parent
anchors.margins: 5
@@ -96,8 +56,9 @@ Popup {
MouseArea {
anchors.fill: parent
onClicked: {
- view.currentIndex = index
- cameraListPopup.currentValue = modelData
+ view.currentIndex = cameraListItem.index
+ cameraListPopup.currentValue = cameraListItem.modelData
+ cameraListPopup.selected()
}
}
}
diff --git a/examples/multimedia/declarative-camera/CameraPropertyButton.qml b/examples/multimedia/declarative-camera/CameraPropertyButton.qml
index 91961ebf0..3a9e60a98 100644
--- a/examples/multimedia/declarative-camera/CameraPropertyButton.qml
+++ b/examples/multimedia/declarative-camera/CameraPropertyButton.qml
@@ -1,55 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
-import QtMultimedia
Item {
id: propertyButton
@@ -90,23 +42,30 @@ Item {
name: "MobilePortrait"
AnchorChanges {
target: popup
- anchors.bottom: parent.top;
+ // qmllint disable incompatible-type
+ anchors.bottom: propertyButton.top
+ anchors.left: propertyButton.left
+ // qmllint enable incompatible-type
}
},
State {
name: "MobileLandscape"
AnchorChanges {
target: popup
- anchors.verticalCenter: parent.top;
- anchors.right: parent.left;
+ // qmllint disable incompatible-type
+ anchors.verticalCenter: propertyButton.top
+ anchors.right: propertyButton.left
+ // qmllint enable incompatible-type
}
},
State {
name: "Other"
AnchorChanges {
target: popup
- anchors.top: parent.top;
- anchors.right: parent.left;
+ // qmllint disable incompatible-type
+ anchors.top: propertyButton.top
+ anchors.right: propertyButton.left
+ // qmllint enable incompatible-type
}
}
]
diff --git a/examples/multimedia/declarative-camera/CameraPropertyPopup.qml b/examples/multimedia/declarative-camera/CameraPropertyPopup.qml
index 6fc125144..774775a5b 100644
--- a/examples/multimedia/declarative-camera/CameraPropertyPopup.qml
+++ b/examples/multimedia/declarative-camera/CameraPropertyPopup.qml
@@ -1,61 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+pragma ComponentBehavior: Bound
import QtQuick
Popup {
id: propertyPopup
property alias model : view.model
- property variant currentValue
- property variant currentItem : model.get(view.currentIndex)
+ property var currentValue
+ property var currentItem : model.get(view.currentIndex)
property int itemWidth : 100
property int itemHeight : 70
@@ -85,21 +39,26 @@ Popup {
snapMode: ListView.SnapOneItem
highlightFollowsCurrentItem: true
highlight: Rectangle { color: "gray"; radius: 5 }
- currentIndex: indexForValue(propertyPopup.currentValue)
+ currentIndex: propertyPopup.indexForValue(propertyPopup.currentValue)
delegate: Item {
+ id: propertyItem
+
+ required property url icon
+ required property var value
+
width: propertyPopup.itemWidth
height: 70
Image {
anchors.centerIn: parent
- source: icon
+ source: propertyItem.icon
}
MouseArea {
anchors.fill: parent
onClicked: {
- propertyPopup.currentValue = value
- propertyPopup.selected(value)
+ propertyPopup.currentValue = propertyItem.value
+ propertyPopup.selected()
}
}
}
diff --git a/examples/multimedia/declarative-camera/FlashControl.qml b/examples/multimedia/declarative-camera/FlashControl.qml
new file mode 100644
index 000000000..61550f098
--- /dev/null
+++ b/examples/multimedia/declarative-camera/FlashControl.qml
@@ -0,0 +1,65 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtMultimedia
+
+Item {
+ id: flashControl
+
+ height: column.height
+
+ property Camera cameraDevice
+ property bool mIsFlashSupported: (cameraDevice && cameraDevice.active) ? cameraDevice.isFlashModeSupported(Camera.FlashOn) : false
+ property bool mIsTorchSupported: (cameraDevice && cameraDevice.active) ? cameraDevice.isTorchModeSupported(Camera.TorchOn) : false
+
+ Column {
+ id: column
+
+ Switch {
+ id: flashModeControl
+ visible: flashControl.mIsFlashSupported
+ opacity: checked ? 0.75 : 0.25
+ text: "Flash"
+ contentItem: Text {
+ text: flashModeControl.text
+ color: "white"
+ leftPadding: flashModeControl.indicator.width + flashModeControl.spacing
+ }
+
+ onPositionChanged: {
+ if (position) {
+ if (torchModeControl.checked)
+ torchModeControl.toggle();
+ flashControl.cameraDevice.flashMode = Camera.FlashOn
+
+ } else {
+ flashControl.cameraDevice.flashMode = Camera.FlashOff
+ }
+ }
+ }
+
+ Switch {
+ id: torchModeControl
+ visible: flashControl.mIsTorchSupported
+ opacity: checked ? 0.75 : 0.25
+ text: "Torch"
+ contentItem: Text {
+ text: torchModeControl.text
+ color: "white"
+ leftPadding: torchModeControl.indicator.width + torchModeControl.spacing
+ }
+
+ onPositionChanged: {
+ if (position) {
+ if (flashModeControl.checked)
+ flashModeControl.toggle();
+ flashControl.cameraDevice.torchMode = Camera.TorchOn
+ } else {
+ flashControl.cameraDevice.torchMode = Camera.TorchOff
+ }
+ }
+ }
+ }
+}
diff --git a/examples/multimedia/declarative-camera/Info.plist b/examples/multimedia/declarative-camera/Info.plist
index d664a2460..7a1b26e1e 100644
--- a/examples/multimedia/declarative-camera/Info.plist
+++ b/examples/multimedia/declarative-camera/Info.plist
@@ -13,7 +13,7 @@
<key>CFBundleExecutable</key>
<string>declarative-camera</string>
<key>CFBundleIdentifier</key>
- <string>com.digia.${PRODUCT_NAME:rfc1034identifier}</string>
+ <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleDisplayName</key>
<string>declarative-camera</string>
<key>CFBundleName</key>
diff --git a/examples/multimedia/declarative-camera/PhotoCaptureControls.qml b/examples/multimedia/declarative-camera/PhotoCaptureControls.qml
index 0f2745665..f4ecee37b 100644
--- a/examples/multimedia/declarative-camera/PhotoCaptureControls.qml
+++ b/examples/multimedia/declarative-camera/PhotoCaptureControls.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtMultimedia
@@ -71,19 +24,19 @@ FocusScope {
GridLayout {
id: buttonsColumn
- anchors.margins: buttonsmargin
+ anchors.margins: captureControls.buttonsmargin
flow: captureControls.state === "MobilePortrait"
? GridLayout.LeftToRight : GridLayout.TopToBottom
CameraButton {
text: "Capture"
- implicitWidth: buttonsWidth
- visible: captureSession.imageCapture.readyForCapture
- onClicked: captureSession.imageCapture.captureToFile("")
+ implicitWidth: captureControls.buttonsWidth
+ visible: captureControls.captureSession.imageCapture.readyForCapture
+ onClicked: captureControls.captureSession.imageCapture.captureToFile("")
}
CameraPropertyButton {
id : wbModesButton
- implicitWidth: buttonsWidth
+ implicitWidth: captureControls.buttonsWidth
state: captureControls.state
value: Camera.WhiteBalanceAuto
model: ListModel {
@@ -117,12 +70,12 @@ FocusScope {
}
Item {
- implicitWidth: buttonsWidth
- height: 70
+ implicitWidth: captureControls.buttonsWidth
+ implicitHeight: 70
CameraButton {
text: "View"
anchors.fill: parent
- onClicked:state = captureControls.previewSelected()
+ onClicked: captureControls.previewSelected()
visible: captureControls.previewAvailable
}
}
@@ -130,25 +83,25 @@ FocusScope {
GridLayout {
id: bottomColumn
- anchors.margins: buttonsmargin
+ anchors.margins: captureControls.buttonsmargin
flow: captureControls.state === "MobilePortrait"
? GridLayout.LeftToRight : GridLayout.TopToBottom
CameraListButton {
- implicitWidth: buttonsWidth
+ implicitWidth: captureControls.buttonsWidth
state: captureControls.state
- onValueChanged: captureSession.camera.cameraDevice = value
+ onValueChanged: captureControls.captureSession.camera.cameraDevice = value
}
CameraButton {
text: "Switch to Video"
- implicitWidth: buttonsWidth
+ implicitWidth: captureControls.buttonsWidth
onClicked: captureControls.videoModeSelected()
}
CameraButton {
id: quitButton
- implicitWidth: buttonsWidth
+ implicitWidth: captureControls.buttonsWidth
text: "Quit"
onClicked: Qt.quit()
}
@@ -156,107 +109,120 @@ FocusScope {
}
ZoomControl {
+ id: zoomControl
x : 0
- y : captureControls.state === "MobilePortrait" ? -buttonPaneShadow.height : 0
+ y : captureControls.state === "MobilePortrait" ? -buttonPaneShadow.height/2 : 0
width : 100
- height: parent.height
+ height: parent.height - (flashControl.visible * flashControl.height)
- currentZoom: camera.zoomFactor
- maximumZoom: camera.maximumZoomFactor
- onZoomTo: camera.setDigitalZoom(value)
+ currentZoom: captureControls.captureSession.camera.zoomFactor
+ maximumZoom: captureControls.captureSession.camera.maximumZoomFactor
+ onZoomTo: (target) => captureControls.captureSession.camera.zoomFactor = target
+ }
+
+ FlashControl {
+ id: flashControl
+ x : 10
+ y : captureControls.state === "MobilePortrait" ?
+ parent.height - (buttonPaneShadow.height + height) : parent.height - height
+
+ cameraDevice: captureControls.captureSession.camera
}
states: [
State {
name: "MobilePortrait"
PropertyChanges {
- target: buttonPaneShadow
- width: parent.width
- height: captureControls.buttonsPanelPortraitHeight
- }
- PropertyChanges {
- target: buttonsColumn
- height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
- }
- PropertyChanges {
- target: bottomColumn
- height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
+ buttonPaneShadow.width: parent.width
+ buttonPaneShadow.height: captureControls.buttonsPanelPortraitHeight
+ buttonsColumn.height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
+ bottomColumn.height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
}
AnchorChanges {
target: buttonPaneShadow
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.bottom: captureControls.bottom
+ anchors.left: captureControls.left
+ anchors.right: captureControls.right
+ // qmllint enable incompatible-type
}
AnchorChanges {
target: buttonsColumn
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
+ // qmllint disable incompatible-type
+ anchors.left: buttonPaneShadow.left
+ anchors.right: buttonPaneShadow.right
+ anchors.top: buttonPaneShadow.top
+ // qmllint enable incompatible-type
}
AnchorChanges {
target: bottomColumn
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.bottom: buttonPaneShadow.bottom
+ anchors.left: buttonPaneShadow.left
+ anchors.right: buttonPaneShadow.right
+ // qmllint enable incompatible-type
}
},
State {
name: "MobileLandscape"
PropertyChanges {
- target: buttonPaneShadow
- width: buttonsPanelWidth
- height: parent.height
- }
- PropertyChanges {
- target: buttonsColumn
- height: parent.height
- width: buttonPaneShadow.width / 2
- }
- PropertyChanges {
- target: bottomColumn
- height: parent.height
- width: buttonPaneShadow.width / 2
+ buttonPaneShadow.width: buttonsPanelWidth
+ buttonPaneShadow.height: parent.height
+ buttonsColumn.height: parent.height
+ buttonsColumn.width: buttonPaneShadow.width / 2
+ bottomColumn.height: parent.height
+ bottomColumn.width: buttonPaneShadow.width / 2
}
AnchorChanges {
target: buttonPaneShadow
- anchors.top: parent.top
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.top: captureControls.top
+ anchors.right: captureControls.right
+ // qmllint enable incompatible-type
}
AnchorChanges {
target: buttonsColumn
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.left: parent.left
+ // qmllint disable incompatible-type
+ anchors.top: buttonPaneShadow.top
+ anchors.bottom: buttonPaneShadow.bottom
+ anchors.left: buttonPaneShadow.left
+ // qmllint enable incompatible-type
}
AnchorChanges {
target: bottomColumn
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.top: buttonPaneShadow.top
+ anchors.bottom: buttonPaneShadow.bottom
+ anchors.right: buttonPaneShadow.right
+ // qmllint enable incompatible-type
}
},
State {
name: "Other"
PropertyChanges {
- target: buttonPaneShadow
- width: bottomColumn.width + 16
- height: parent.height
+ buttonPaneShadow.width: bottomColumn.width + 16
+ buttonPaneShadow.height: parent.height
}
AnchorChanges {
target: buttonPaneShadow
- anchors.top: parent.top
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.top: captureControls.top
+ anchors.right: captureControls.right
+ // qmllint enable incompatible-type
}
AnchorChanges {
target: buttonsColumn
- anchors.top: parent.top
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.top: buttonPaneShadow.top
+ anchors.right: buttonPaneShadow.right
+ // qmllint enable incompatible-type
}
AnchorChanges {
target: bottomColumn
- anchors.bottom: parent.bottom
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.bottom: buttonPaneShadow.bottom
+ anchors.right: buttonPaneShadow.right
+ // qmllint enable incompatible-type
}
}
]
diff --git a/examples/multimedia/declarative-camera/PhotoPreview.qml b/examples/multimedia/declarative-camera/PhotoPreview.qml
index 8aeb116dd..98af8a12a 100644
--- a/examples/multimedia/declarative-camera/PhotoPreview.qml
+++ b/examples/multimedia/declarative-camera/PhotoPreview.qml
@@ -1,55 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
-import QtMultimedia
Item {
property alias source : preview.source
diff --git a/examples/multimedia/declarative-camera/Popup.qml b/examples/multimedia/declarative-camera/Popup.qml
index db304dccb..fc57f3002 100644
--- a/examples/multimedia/declarative-camera/Popup.qml
+++ b/examples/multimedia/declarative-camera/Popup.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
@@ -64,12 +17,12 @@ Rectangle {
states: [
State {
name: "invisible"
- PropertyChanges { target: popup; opacity: 0 }
+ PropertyChanges { popup.opacity: 0 }
},
State {
name: "visible"
- PropertyChanges { target: popup; opacity: 1.0 }
+ PropertyChanges { popup.opacity: 1.0 }
}
]
diff --git a/examples/multimedia/declarative-camera/VideoCaptureControls.qml b/examples/multimedia/declarative-camera/VideoCaptureControls.qml
index b2dbe3bd3..39acae106 100644
--- a/examples/multimedia/declarative-camera/VideoCaptureControls.qml
+++ b/examples/multimedia/declarative-camera/VideoCaptureControls.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtMultimedia
@@ -71,67 +24,68 @@ FocusScope {
GridLayout {
id: buttonsColumn
- anchors.margins: buttonsmargin
+ anchors.margins: captureControls.buttonsmargin
flow: captureControls.state === "MobilePortrait"
? GridLayout.LeftToRight : GridLayout.TopToBottom
Item {
- implicitWidth: buttonsWidth
- height: 70
+ implicitWidth: captureControls.buttonsWidth
+ implicitHeight: 70
CameraButton {
text: "Record"
anchors.fill: parent
- visible: captureSession.recorder.recorderState !== MediaRecorder.RecordingState
- onClicked: captureSession.recorder.record()
+ visible: captureControls.captureSession.recorder.recorderState !== MediaRecorder.RecordingState
+ onClicked: captureControls.captureSession.recorder.record()
}
}
Item {
- implicitWidth: buttonsWidth
- height: 70
+ implicitWidth: captureControls.buttonsWidth
+ implicitHeight: 70
CameraButton {
id: stopButton
text: "Stop"
anchors.fill: parent
- visible: captureSession.recorder.recorderState === MediaRecorder.RecordingState
- onClicked: captureSession.recorder.stop()
+ visible: captureControls.captureSession.recorder.recorderState === MediaRecorder.RecordingState
+ onClicked: captureControls.captureSession.recorder.stop()
}
}
Item {
- implicitWidth: buttonsWidth
- height: 70
+ implicitWidth: captureControls.buttonsWidth
+ implicitHeight: 70
CameraButton {
text: "View"
anchors.fill: parent
onClicked: captureControls.previewSelected()
//don't show View button during recording
- visible: captureSession.recorder.actualLocation && !stopButton.visible
+ visible: captureControls.captureSession.recorder.actualLocation.toString() !== ""
+ && !stopButton.visible
}
}
}
GridLayout {
id: bottomColumn
- anchors.margins: buttonsmargin
+ anchors.margins: captureControls.buttonsmargin
flow: captureControls.state === "MobilePortrait"
? GridLayout.LeftToRight : GridLayout.TopToBottom
CameraListButton {
- implicitWidth: buttonsWidth
- onValueChanged: captureSession.camera.cameraDevice = value
+ implicitWidth: captureControls.buttonsWidth
+ onValueChanged: captureControls.captureSession.camera.cameraDevice = value
state: captureControls.state
}
CameraButton {
text: "Switch to Photo"
- implicitWidth: buttonsWidth
+ implicitWidth: captureControls.buttonsWidth
onClicked: captureControls.photoModeSelected()
}
CameraButton {
id: quitButton
text: "Quit"
- implicitWidth: buttonsWidth
+ implicitWidth: captureControls.buttonsWidth
onClicked: Qt.quit()
}
}
@@ -139,106 +93,118 @@ FocusScope {
ZoomControl {
x : 0
- y : captureControls.state === "MobilePortrait" ? -buttonPaneShadow.height : 0
+ y : captureControls.state === "MobilePortrait" ? -buttonPaneShadow.height/2 : 0
width : 100
- height: parent.height
+ height: parent.height - (flashControl.visible * flashControl.height)
- currentZoom: captureSession.camera.zoomFactor
- maximumZoom: captureSession.camera.maximumZoomFactor
- onZoomTo: captureSession.camera.zoomFactor = value
+ currentZoom: captureControls.captureSession.camera.zoomFactor
+ maximumZoom: captureControls.captureSession.camera.maximumZoomFactor
+ onZoomTo: (target) => captureControls.captureSession.camera.zoomFactor = target
+ }
+
+ FlashControl {
+ id: flashControl
+ x : 10
+ y : captureControls.state === "MobilePortrait" ?
+ parent.height - (buttonPaneShadow.height + height) : parent.height - height
+
+ cameraDevice: captureControls.captureSession.camera
}
states: [
State {
name: "MobilePortrait"
PropertyChanges {
- target: buttonPaneShadow
- width: parent.width
- height: buttonsPanelPortraitHeight
- }
- PropertyChanges {
- target: buttonsColumn
- height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
- }
- PropertyChanges {
- target: bottomColumn
- height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
+ buttonPaneShadow.width: parent.width
+ buttonPaneShadow.height: buttonsPanelPortraitHeight
+ buttonsColumn.height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
+ bottomColumn.height: captureControls.buttonsPanelPortraitHeight / 2 - buttonsmargin
}
AnchorChanges {
target: buttonPaneShadow
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.bottom: captureControls.bottom
+ anchors.left: captureControls.left
+ anchors.right: captureControls.right
+ // qmllint enable incompatible-type
}
AnchorChanges {
target: buttonsColumn
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
+ // qmllint disable incompatible-type
+ anchors.left: buttonPaneShadow.left
+ anchors.right: buttonPaneShadow.right
+ anchors.top: buttonPaneShadow.top
+ // qmllint enable incompatible-type
}
AnchorChanges {
- target: bottomColumn;
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
+ target: bottomColumn
+ // qmllint disable incompatible-type
+ anchors.bottom: buttonPaneShadow.bottom
+ anchors.left: buttonPaneShadow.left
+ anchors.right: buttonPaneShadow.right
+ // qmllint enable incompatible-type
}
},
State {
name: "MobileLandscape"
PropertyChanges {
- target: buttonPaneShadow
- width: buttonsPanelWidth
- height: parent.height
- }
- PropertyChanges {
- target: buttonsColumn
- height: parent.height
- width: buttonPaneShadow.width / 2
- }
- PropertyChanges {
- target: bottomColumn
- height: parent.height
- width: buttonPaneShadow.width / 2
+ buttonPaneShadow.width: buttonsPanelWidth
+ buttonPaneShadow.height: parent.height
+ buttonsColumn.height: parent.height
+ buttonsColumn.width: buttonPaneShadow.width / 2
+ bottomColumn.height: parent.height
+ bottomColumn.width: buttonPaneShadow.width / 2
}
AnchorChanges {
target: buttonPaneShadow
- anchors.top: parent.top
- anchors.right: parent.right
+ // qmllint disable incompatible-type
+ anchors.top: captureControls.top
+ anchors.right: captureControls.right
+ // qmllint enable incompatible-type
}
AnchorChanges {
- target: buttonsColumn;
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.left: parent.left;
+ target: buttonsColumn
+ // qmllint disable incompatible-type
+ anchors.top: buttonPaneShadow.top
+ anchors.bottom: buttonPaneShadow.bottom
+ anchors.left: buttonPaneShadow.left
+ // qmllint enable incompatible-type
}
AnchorChanges {
- target: bottomColumn;
- anchors.top: parent.top;
- anchors.bottom: parent.bottom;
- anchors.right: parent.right;
+ target: bottomColumn
+ // qmllint disable incompatible-type
+ anchors.top: buttonPaneShadow.top
+ anchors.bottom: buttonPaneShadow.bottom
+ anchors.right: buttonPaneShadow.right
+ // qmllint enable incompatible-type
}
},
State {
name: "Other"
PropertyChanges {
- target: buttonPaneShadow;
- width: bottomColumn.width + 16;
- height: parent.height;
+ buttonPaneShadow.width: bottomColumn.width + 16
+ buttonPaneShadow.height: parent.height
}
AnchorChanges {
- target: buttonPaneShadow;
- anchors.top: parent.top;
- anchors.right: parent.right;
+ target: buttonPaneShadow
+ // qmllint disable incompatible-type
+ anchors.top: captureControls.top
+ anchors.right: captureControls.right
+ // qmllint enable incompatible-type
}
AnchorChanges {
- target: buttonsColumn;
- anchors.top: parent.top
- anchors.right: parent.right
+ target: buttonsColumn
+ // qmllint disable incompatible-type
+ anchors.top: buttonPaneShadow.top
+ anchors.right: buttonPaneShadow.right
+ // qmllint enable incompatible-type
}
AnchorChanges {
- target: bottomColumn;
- anchors.bottom: parent.bottom;
- anchors.right: parent.right;
+ target: bottomColumn
+ // qmllint disable incompatible-type
+ anchors.bottom: buttonPaneShadow.bottom
+ anchors.right: buttonPaneShadow.right
+ // qmllint enable incompatible-type
}
}
]
diff --git a/examples/multimedia/declarative-camera/VideoPreview.qml b/examples/multimedia/declarative-camera/VideoPreview.qml
index e2d6a47e0..f7b306794 100644
--- a/examples/multimedia/declarative-camera/VideoPreview.qml
+++ b/examples/multimedia/declarative-camera/VideoPreview.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtMultimedia
@@ -65,7 +18,7 @@ Item {
videoPreview.closed();
}
onSourceChanged: {
- if (visible && source !== "")
+ if (videoPreview.visible && source !== "")
play();
}
diff --git a/examples/multimedia/declarative-camera/ZoomControl.qml b/examples/multimedia/declarative-camera/ZoomControl.qml
index 9107ab4ae..c60495fd0 100644
--- a/examples/multimedia/declarative-camera/ZoomControl.qml
+++ b/examples/multimedia/declarative-camera/ZoomControl.qml
@@ -1,61 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
-import QtMultimedia
Item {
id : zoomControl
property real currentZoom : 1
property real maximumZoom : 1
- signal zoomTo(real value)
+ signal zoomTo(real target)
visible: zoomControl.maximumZoom > 1
diff --git a/examples/multimedia/declarative-camera/declarative-camera.qml b/examples/multimedia/declarative-camera/declarative-camera.qml
index 6260c4281..24477150e 100644
--- a/examples/multimedia/declarative-camera/declarative-camera.qml
+++ b/examples/multimedia/declarative-camera/declarative-camera.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtMultimedia
@@ -56,30 +9,13 @@ Rectangle {
width: 800
height: 480
+
color: "black"
state: "PhotoCapture"
property string platformScreen: ""
- property int buttonsPanelLandscapeWidth: 328
- property int buttonsPanelPortraitHeight: 180
-
- onWidthChanged: {
- setState()
- }
- function setState() {
- if (Qt.platform.os === "android" || Qt.platform.os === "ios") {
- if (Screen.desktopAvailableWidth < Screen.desktopAvailableHeight) {
- stillControls.state = "MobilePortrait";
- } else {
- stillControls.state = "MobileLandscape";
- }
- } else {
- stillControls.state = "Other";
- }
- console.log("State: " + stillControls.state);
- stillControls.buttonsWidth = (stillControls.state === "MobilePortrait")
- ? Screen.desktopAvailableWidth/3.4 : 144
- }
+ property int buttonsPanelLandscapeWidth: cameraUI.width/2
+ property int buttonsPanelPortraitHeight: cameraUI.height/3
states: [
State {
@@ -151,18 +87,42 @@ Rectangle {
VideoOutput {
id: viewfinder
visible: ((cameraUI.state === "PhotoCapture") || (cameraUI.state === "VideoCapture"))
-
- x: 0
- y: 0
- width: ((stillControls.state === "MobilePortrait") ? parent.width : (parent.width-buttonsPanelLandscapeWidth))
- height: ((stillControls.state === "MobilePortrait") ? parent.height - buttonsPanelPortraitHeight : parent.height)
+ anchors.fill: parent
// autoOrientation: true
}
+ Item {
+ id: controlLayout
+
+ readonly property bool isMobile: Qt.platform.os === "android" || Qt.platform.os === "ios"
+ readonly property bool isLandscape: Screen.desktopAvailableWidth >= Screen.desktopAvailableHeight
+ property int buttonsWidth: state === "MobilePortrait" ? Screen.desktopAvailableWidth / 3.4 : 114
+
+ states: [
+ State {
+ name: "MobileLandscape"
+ when: controlLayout.isMobile && controlLayout.isLandscape
+ },
+ State {
+ name: "MobilePortrait"
+ when: controlLayout.isMobile && !controlLayout.isLandscape
+ },
+ State {
+ name: "Other"
+ when: !controlLayout.isMobile
+ }
+ ]
+
+ onStateChanged: {
+ console.log("State: " + controlLayout.state)
+ }
+ }
+
PhotoCaptureControls {
id: stillControls
- state: setState()
+ state: controlLayout.state
anchors.fill: parent
+ buttonsWidth: controlLayout.buttonsWidth
buttonsPanelPortraitHeight: cameraUI.buttonsPanelPortraitHeight
buttonsPanelWidth: cameraUI.buttonsPanelLandscapeWidth
captureSession: captureSession
@@ -174,9 +134,9 @@ Rectangle {
VideoCaptureControls {
id: videoControls
- state: stillControls.state
+ state: controlLayout.state
anchors.fill: parent
- buttonsWidth: stillControls.buttonsWidth
+ buttonsWidth: controlLayout.buttonsWidth
buttonsPanelPortraitHeight: cameraUI.buttonsPanelPortraitHeight
buttonsPanelWidth: cameraUI.buttonsPanelLandscapeWidth
captureSession: captureSession
diff --git a/examples/multimedia/declarative-camera/declarative-camera.qrc b/examples/multimedia/declarative-camera/declarative-camera.qrc
index e4f6ee981..ddfcefa7b 100644
--- a/examples/multimedia/declarative-camera/declarative-camera.qrc
+++ b/examples/multimedia/declarative-camera/declarative-camera.qrc
@@ -12,6 +12,7 @@
<file>CameraButton.qml</file>
<file>CameraListPopup.qml</file>
<file>CameraListButton.qml</file>
+ <file>FlashControl.qml</file>
<file>images/camera_auto_mode.png</file>
<file>images/camera_camera_setting.png</file>
<file>images/camera_flash_auto.png</file>
diff --git a/examples/multimedia/declarative-camera/doc/images/CaptureControls.png b/examples/multimedia/declarative-camera/doc/images/CaptureControls.png
new file mode 100644
index 000000000..8c24d8030
--- /dev/null
+++ b/examples/multimedia/declarative-camera/doc/images/CaptureControls.png
Binary files differ
diff --git a/examples/multimedia/declarative-camera/doc/images/FlashControls.png b/examples/multimedia/declarative-camera/doc/images/FlashControls.png
new file mode 100644
index 000000000..93eec867b
--- /dev/null
+++ b/examples/multimedia/declarative-camera/doc/images/FlashControls.png
Binary files differ
diff --git a/examples/multimedia/declarative-camera/doc/images/VideoCaptureControls.png b/examples/multimedia/declarative-camera/doc/images/VideoCaptureControls.png
new file mode 100644
index 000000000..c35be7beb
--- /dev/null
+++ b/examples/multimedia/declarative-camera/doc/images/VideoCaptureControls.png
Binary files differ
diff --git a/examples/multimedia/declarative-camera/doc/images/ZoomControl.png b/examples/multimedia/declarative-camera/doc/images/ZoomControl.png
new file mode 100644
index 000000000..d628e60f1
--- /dev/null
+++ b/examples/multimedia/declarative-camera/doc/images/ZoomControl.png
Binary files differ
diff --git a/examples/multimedia/declarative-camera/doc/images/qml-camera.png b/examples/multimedia/declarative-camera/doc/images/qml-camera.png
index 0cd61f0a0..4b710670d 100644
--- a/examples/multimedia/declarative-camera/doc/images/qml-camera.png
+++ b/examples/multimedia/declarative-camera/doc/images/qml-camera.png
Binary files differ
diff --git a/examples/multimedia/declarative-camera/doc/images/qml-declarative-portrait.png b/examples/multimedia/declarative-camera/doc/images/qml-declarative-portrait.png
new file mode 100644
index 000000000..7adfdb021
--- /dev/null
+++ b/examples/multimedia/declarative-camera/doc/images/qml-declarative-portrait.png
Binary files differ
diff --git a/examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc b/examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc
index 6aa8e8fa6..c6701a68b 100644
--- a/examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc
+++ b/examples/multimedia/declarative-camera/doc/src/declarative-camera.qdoc
@@ -1,55 +1,158 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
-\example multimedia/declarative-camera
-\title QML Camera Example
+\example declarative-camera
+\title QML Camera Application
\ingroup multimedia_examples
\ingroup camera_examples_qml
-\brief The Camera Example shows how to use the API to capture a still image
-or video.
+\examplecategory {Multimedia}
+\examplecategory {Mobile}
+\brief This Qt Quick based application shows how to use the API to capture a
+still image or video.
\image qml-camera.png
This example demonstrates how to access camera functions via QML.
-It shows how to change settings and to capture images.
+It shows how to change settings and capture images or video.
\include examples-run.qdocinc
-\section1 Application Structure
+\section1 Application structure
-Most of the QML code supports the user interface for this application with the
-camera types being mostly found in \e {declarative-camera.qml} and
-\e {CaptureControls.qml}.
+Most of the QML code in this example supports the user interface. Custom types
+that support the requirements have been implemented using existing Qt Quick
+controls.
-CaptureControls, which is implemented in \e {CaptureControls.qml},
-generates a column on the right hand side of the screen which includes control
-buttons for focus (not initially visible), capture, flash modes,
-white balance, exposure compensation, and if a preview is
-available, a preview button. The last button exits from the application.
+\section1 Using screen orientation to select layout
+The orientation and control layout state logic is encapsulated in a separate
+Item, \c controlLayout like so:
+
+\quotefromfile declarative-camera/declarative-camera.qml
+\skipto Item {
+\printuntil /^ }/
+
+The \c stillControls and \c videoControls objects both bind to the \c state
+and \c buttonsWidth properties of this Item, as shown in \c stillControls:
+
+\skipto PhotoCaptureControls
+\printuntil /^ }/
+
+To support debugging, a message about layout state change is logged.
+
+Here is the portrait layout:
+
+\image qml-declarative-portrait.png
+
+You can see the \c state property is initially set as \c PhotoCapture.
+
+Then the \c states themselves are defined like so:
+
+\quotefromfile declarative-camera/declarative-camera.qml
+\skipto states: [
+\printuntil /^ ]/
+
+\section1 Controls for capturing
+
+Controls for capturing are implemented in \c PhotoCaptureControls.qml and
+VideoCaptureControls.qml. They each are based on a \l FocusScope that defines
+common buttons dimensions and margins that are used by the control buttons and
+then declares the buttons.
+
+This generates a column on the right hand side of the screen which includes,
+listed top to bottom, the following controls:
+\div {class="multi-column"}
+ \div {class="doc-column"}
+ \list
+ \li A \c Capture or \c Record button, which initiates capturing.
+ \li A \c{capture properties} button that displays the icon of the current
+ white balance mode selected and when pressed uses a pop-up to displays
+ the following option's icons:
+ \list
+ \li Flash mode (if available)
+ \li White balance modes
+ \li Exposure compensation
+ \endlist
+ \li A \c View button, once something has been captured.
+ \li A button which displays the currently selected capture device and
+ when pressed provides a list of available devices to switch to, using a
+ pop-up.
+ \li A \c{Switch To} button that displays the alternate capture mode
+ (video or photo) depending on the current active selection and switches
+ the mode when pressed.
+ \li A \c Quit button, that exits the application.
+ \endlist
+ \enddiv
+ \div {class="doc-column"}
+ \inlineimage CaptureControls.png
+ \enddiv
+ \div {class="doc-column"}
+ \inlineimage VideoCaptureControls.png
+ \enddiv
+\enddiv
+
+\section1 Image capturing
+
+The button that triggers this is defined in CameraButton.qml:
+but its interaction with the camera is in the controls types, lets look at
+PhotoCaptureControls:
+
+\quotefromfile declarative-camera/PhotoCaptureControls.qml
+\skipto CameraButton {
+\printto CameraPropertyButton {
+
+\section1 Zoom control
+
+\div {class="multi-column"}
+ \div {class="doc-column"}
+ Implemented in \c ZoomControl.qml the ZoomControl type is based on an Item
+ and creates a bar that represents the zoom level, which can also be dragged.
+ It uses an exponential calculation method to determine the zoom factor given
+ the position of the \c grove.
+
+ The bar is only visible if the initialZoom is greater than 1. This means
+ the currently active camera has a zoom function.
+ \enddiv
+ \div {class="doc-column"}
+ \inlineimage ZoomControl.png
+ \enddiv
+\enddiv
+
+\quotefromfile declarative-camera/ZoomControl.qml
+\skipto Item {
+\printuntil font.pixelSize: 18
+
+In PhotoCaptureControls.qml and VideoCaptureControls.qml the signal \c zoomTo
+will set the selected camera's \l{Camera::}{zoomFactor} property to the
+calculated \c target value, as well as updating the ZoomControl bar.
+
+\quotefromfile declarative-camera/VideoCaptureControls.qml
+\skipto ZoomControl {
+\printto FlashControl {
+
+\section1 Flash and torch control
+
+\image FlashControls.png
+
+Defined in \c FlashControl.qml this enables flash mode selection and torch
+functionality to be toggled via a Switch. As with the zoom control, the switches
+are only visible on top of the preview window if the active device supports
+these functions.
+
+Here we check if the functions are supported:
+
+\quotefromfile declarative-camera/FlashControl.qml
+\skipto property Camera cameraDevice
+\printline Camera
+\printline FlashOn
+\printline TorchOn
+
+Here we implement the \c flashModeControl switch, which also directly controls
+the Camera device.
+
+\skipto Switch {
+\printuntil /^ }/
+
+Torch control is implemented in a similar way.
*/
diff --git a/examples/multimedia/declarative-camera/permission-denied.qml b/examples/multimedia/declarative-camera/permission-denied.qml
new file mode 100644
index 000000000..19eb58ead
--- /dev/null
+++ b/examples/multimedia/declarative-camera/permission-denied.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+
+Rectangle {
+ color: "black"
+ width: 800
+ height: 600
+
+ Text {
+ anchors.fill: parent
+ text: qsTr("Grant the camera permission and restart the app")
+ color: "white"
+ font.pointSize: 20
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+}
diff --git a/examples/multimedia/declarative-camera/qmlcamera.cpp b/examples/multimedia/declarative-camera/qmlcamera.cpp
index edffe6bb0..9844fd53b 100644
--- a/examples/multimedia/declarative-camera/qmlcamera.cpp
+++ b/examples/multimedia/declarative-camera/qmlcamera.cpp
@@ -1,68 +1,39 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
-#include <QQuickView>
#include <QQmlEngine>
+#include <QQuickView>
+
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
-int main(int argc, char* argv[])
+int main(int argc, char *argv[])
{
- QGuiApplication app(argc,argv);
+ QGuiApplication app(argc, argv);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
- // Qt.quit() called in embedded .qml by default only emits
- // quit() signal, so do this (optionally use Qt.exit()).
- QObject::connect(view.engine(), &QQmlEngine::quit,
- qApp, &QGuiApplication::quit);
- view.setSource(QUrl("qrc:///declarative-camera.qml"));
- view.resize(800, 480);
- view.show();
+
+ auto setupView = [&view](const QUrl &viewSource) {
+ // Qt.quit() called in embedded .qml by default only emits
+ // quit() signal, so do this (optionally use Qt.exit()).
+ QObject::connect(view.engine(), &QQmlEngine::quit, qApp, &QGuiApplication::quit);
+ view.setSource(viewSource);
+ view.show();
+ };
+
+#if QT_CONFIG(permissions)
+ QCameraPermission cameraPermission;
+ qApp->requestPermission(cameraPermission, [&setupView](const QPermission &permission) {
+ if (permission.status() == Qt::PermissionStatus::Denied)
+ setupView(QUrl("qrc:///permission-denied.qml"));
+ else
+ setupView(QUrl("qrc:///declarative-camera.qml"));
+ });
+#else
+ setupView();
+#endif
+
return app.exec();
}
diff --git a/examples/multimedia/devices/main.cpp b/examples/multimedia/devices/main.cpp
deleted file mode 100644
index 79c1afb25..000000000
--- a/examples/multimedia/devices/main.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QTextStream>
-#include <QString>
-#include <QAudioFormat>
-
-#include <QAudioDevice>
-#include <QCameraDevice>
-#include <qmediadevices.h>
-
-#include <stdio.h>
-
-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";
- }
-}
-
-QString positionToString(QCameraDevice::Position position)
-{
- switch (position) {
- case QCameraDevice::BackFace:
- return "BackFace";
- case QCameraDevice::FrontFace:
- return "FrontFace";
- default:
- return "Unspecified";
- }
-}
-
-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;
-}
-
-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 = QString("%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 = QString("%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[])
-{
- Q_UNUSED(argc);
- Q_UNUSED(argv);
- 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);
-
- return 0;
-}
diff --git a/examples/multimedia/multimedia.pro b/examples/multimedia/multimedia.pro
index b34d8f95e..765aa69a7 100644
--- a/examples/multimedia/multimedia.pro
+++ b/examples/multimedia/multimedia.pro
@@ -1,15 +1,18 @@
TEMPLATE = subdirs
QT_FOR_CONFIG += multimedia-private
-SUBDIRS += audiodecoder
-
# These examples all need widgets for now (using creator templates that use widgets)
qtHaveModule(widgets) {
SUBDIRS += \
- spectrum \
- audiorecorder \
audiodevices \
- audiooutput
+ audiooutput \
+ audiorecorder \
+ camera \
+ player \
+ spectrum \
+ videographicsitem \
+ videowidget \
+ screencapture
}
qtHaveModule(quick) {
diff --git a/examples/multimediawidgets/player/CMakeLists.txt b/examples/multimedia/player/CMakeLists.txt
index 7c0304eaf..93a0037a7 100644
--- a/examples/multimediawidgets/player/CMakeLists.txt
+++ b/examples/multimedia/player/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(player LANGUAGES CXX)
@@ -7,7 +10,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimediawidgets/player")
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/player")
find_package(Qt6 REQUIRED COMPONENTS MultimediaWidgets Network)
@@ -17,8 +20,9 @@ qt_add_executable(player
playercontrols.cpp playercontrols.h
playlistmodel.cpp playlistmodel.h
videowidget.cpp videowidget.h
- qmediaplaylist.cpp qmediaplaylist.h qmediaplaylist_p.h
- qplaylistfileparser.cpp qplaylistfileparser_p.h
+ qmediaplaylist.cpp qmediaplaylist.h
+ qmediaplaylist_p.cpp qmediaplaylist_p.h
+ qplaylistfileparser.cpp qplaylistfileparser.h
)
set_target_properties(player PROPERTIES
diff --git a/examples/multimediawidgets/player/doc/images/mediaplayerex.jpg b/examples/multimedia/player/doc/images/mediaplayerex.jpg
index e875bd134..e875bd134 100644
--- a/examples/multimediawidgets/player/doc/images/mediaplayerex.jpg
+++ b/examples/multimedia/player/doc/images/mediaplayerex.jpg
Binary files differ
diff --git a/examples/multimediawidgets/player/doc/src/player.qdoc b/examples/multimedia/player/doc/src/player.qdoc
index dad969375..4c4b26969 100644
--- a/examples/multimediawidgets/player/doc/src/player.qdoc
+++ b/examples/multimedia/player/doc/src/player.qdoc
@@ -1,35 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example multimediawidgets/player
+ \example player
\title Media Player Example
\ingroup multimedia_examples
\ingroup video_examples
+ \examplecategory {Multimedia}
\brief Playing audio and video.
\meta {tag} {widgets}
diff --git a/examples/multimedia/player/main.cpp b/examples/multimedia/player/main.cpp
new file mode 100644
index 000000000..3f9993570
--- /dev/null
+++ b/examples/multimedia/player/main.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "player.h"
+
+#include <QApplication>
+#include <QCommandLineOption>
+#include <QCommandLineParser>
+#include <QDir>
+#include <QUrl>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("Player Example");
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Qt MultiMedia Player Example");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("url", "The URL(s) to open.");
+ parser.process(app);
+
+ Player player;
+
+ if (!parser.positionalArguments().isEmpty() && player.isPlayerAvailable()) {
+ QList<QUrl> urls;
+ for (auto &a : parser.positionalArguments())
+ urls.append(QUrl::fromUserInput(a, QDir::currentPath()));
+ player.addToPlaylist(urls);
+ }
+
+ player.show();
+ return app.exec();
+}
diff --git a/examples/multimediawidgets/player/player.cpp b/examples/multimedia/player/player.cpp
index 25722673d..c674554e8 100644
--- a/examples/multimediawidgets/player/player.cpp
+++ b/examples/multimedia/player/player.cpp
@@ -1,93 +1,60 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "player.h"
-
#include "playercontrols.h"
#include "playlistmodel.h"
#include "qmediaplaylist.h"
#include "videowidget.h"
-#include <QMediaMetaData>
-#include <QMediaDevices>
+#include <QApplication>
#include <QAudioDevice>
#include <QAudioOutput>
+#include <QBoxLayout>
+#include <QComboBox>
+#include <QDir>
+#include <QFileDialog>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QListView>
+#include <QMediaDevices>
#include <QMediaFormat>
-#include <QtWidgets>
-
-Player::Player(QWidget *parent)
- : QWidget(parent)
+#include <QMediaMetaData>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QSlider>
+#include <QStandardPaths>
+#include <QStatusBar>
+#include <QVBoxLayout>
+
+Player::Player(QWidget *parent) : QWidget(parent)
{
-//! [create-objs]
+ //! [create-objs]
m_player = new QMediaPlayer(this);
m_audioOutput = new QAudioOutput(this);
m_player->setAudioOutput(m_audioOutput);
-//! [create-objs]
+ //! [create-objs]
connect(m_player, &QMediaPlayer::durationChanged, this, &Player::durationChanged);
connect(m_player, &QMediaPlayer::positionChanged, this, &Player::positionChanged);
- connect(m_player, QOverload<>::of(&QMediaPlayer::metaDataChanged), this, &Player::metaDataChanged);
+ connect(m_player, QOverload<>::of(&QMediaPlayer::metaDataChanged), this,
+ &Player::metaDataChanged);
connect(m_player, &QMediaPlayer::mediaStatusChanged, this, &Player::statusChanged);
connect(m_player, &QMediaPlayer::bufferProgressChanged, this, &Player::bufferingProgress);
connect(m_player, &QMediaPlayer::hasVideoChanged, this, &Player::videoAvailableChanged);
connect(m_player, &QMediaPlayer::errorChanged, this, &Player::displayErrorMessage);
connect(m_player, &QMediaPlayer::tracksChanged, this, &Player::tracksChanged);
-//! [2]
+ //! [2]
m_videoWidget = new VideoWidget(this);
m_videoWidget->resize(1280, 720);
m_player->setVideoOutput(m_videoWidget);
m_playlistModel = new PlaylistModel(this);
m_playlist = m_playlistModel->playlist();
-//! [2]
- connect(m_playlist, &QMediaPlaylist::currentIndexChanged, this, &Player::playlistPositionChanged);
+ //! [2]
+ connect(m_playlist, &QMediaPlaylist::currentIndexChanged, this,
+ &Player::playlistPositionChanged);
// player layout
QBoxLayout *layout = new QVBoxLayout(this);
@@ -154,8 +121,8 @@ Player::Player(QWidget *parent)
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
m_audioOutputCombo = new QComboBox(this);
- m_audioOutputCombo->addItem(QString::fromUtf8("Default"), QVariant::fromValue(QAudioDevice()));
- for (auto &deviceInfo: QMediaDevices::audioOutputs())
+ m_audioOutputCombo->addItem(QStringLiteral("Default"), QVariant::fromValue(QAudioDevice()));
+ for (auto &deviceInfo : QMediaDevices::audioOutputs())
m_audioOutputCombo->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));
connect(m_audioOutputCombo, QOverload<int>::of(&QComboBox::activated), this,
&Player::audioOutputChanged);
@@ -227,7 +194,7 @@ Player::Player(QWidget *parent)
if (!isPlayerAvailable()) {
QMessageBox::warning(this, tr("Service not available"),
- tr("The QMediaPlayer object does not have a valid service.\n"\
+ tr("The QMediaPlayer object does not have a valid service.\n"
"Please check the media service plugins are installed."));
controls->setEnabled(false);
@@ -250,7 +217,8 @@ void Player::open()
QFileDialog fileDialog(this);
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
fileDialog.setWindowTitle(tr("Open Files"));
- fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).value(0, QDir::homePath()));
+ fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MoviesLocation)
+ .value(0, QDir::homePath()));
if (fileDialog.exec() == QDialog::Accepted)
addToPlaylist(fileDialog.selectedUrls());
}
@@ -260,13 +228,14 @@ static bool isPlaylist(const QUrl &url) // Check for ".m3u" playlists.
if (!url.isLocalFile())
return false;
const QFileInfo fileInfo(url.toLocalFile());
- return fileInfo.exists() && !fileInfo.suffix().compare(QLatin1String("m3u"), Qt::CaseInsensitive);
+ return fileInfo.exists()
+ && !fileInfo.suffix().compare(QLatin1String("m3u"), Qt::CaseInsensitive);
}
void Player::addToPlaylist(const QList<QUrl> &urls)
{
const int previousMediaCount = m_playlist->mediaCount();
- for (auto &url: urls) {
+ for (auto &url : urls) {
if (isPlaylist(url))
m_playlist->load(url);
else
@@ -297,15 +266,15 @@ void Player::positionChanged(qint64 progress)
void Player::metaDataChanged()
{
auto metaData = m_player->metaData();
- setTrackInfo(QString("%1 - %2")
- .arg(metaData.value(QMediaMetaData::AlbumArtist).toString())
- .arg(metaData.value(QMediaMetaData::Title).toString()));
+ setTrackInfo(QStringLiteral("%1 - %2")
+ .arg(metaData.value(QMediaMetaData::AlbumArtist).toString())
+ .arg(metaData.value(QMediaMetaData::Title).toString()));
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
for (int i = 0; i < QMediaMetaData::NumMetaData; i++) {
- if (QLineEdit* field = qobject_cast<QLineEdit*>(m_metaDataFields[i]))
+ if (QLineEdit *field = qobject_cast<QLineEdit *>(m_metaDataFields[i]))
field->clear();
- else if (QLabel* label = qobject_cast<QLabel*>(m_metaDataFields[i]))
+ else if (QLabel *label = qobject_cast<QLabel *>(m_metaDataFields[i]))
label->clear();
m_metaDataFields[i]->setDisabled(true);
m_metaDataLabels[i]->setDisabled(true);
@@ -315,17 +284,17 @@ void Player::metaDataChanged()
int i = int(key);
if (key == QMediaMetaData::CoverArtImage) {
QVariant v = metaData.value(key);
- if (QLabel *cover = qobject_cast<QLabel*>(m_metaDataFields[key])) {
+ if (QLabel *cover = qobject_cast<QLabel *>(m_metaDataFields[key])) {
QImage coverImage = v.value<QImage>();
cover->setPixmap(QPixmap::fromImage(coverImage));
}
} else if (key == QMediaMetaData::ThumbnailImage) {
QVariant v = metaData.value(key);
- if (QLabel *thumbnail = qobject_cast<QLabel*>(m_metaDataFields[key])) {
+ if (QLabel *thumbnail = qobject_cast<QLabel *>(m_metaDataFields[key])) {
QImage thumbnailImage = v.value<QImage>();
thumbnail->setPixmap(QPixmap::fromImage(thumbnailImage));
}
- } else if (QLineEdit *field = qobject_cast<QLineEdit*>(m_metaDataFields[key])) {
+ } else if (QLineEdit *field = qobject_cast<QLineEdit *>(m_metaDataFields[key])) {
QString stringValue = metaData.stringValue(key);
field->setText(stringValue);
}
@@ -343,14 +312,14 @@ QString Player::trackName(const QMediaMetaData &metaData, int index)
if (title.isEmpty()) {
if (lang == QLocale::Language::AnyLanguage)
- name = tr("Track %1").arg(index+1);
+ name = tr("Track %1").arg(index + 1);
else
name = QLocale::languageToString(lang);
} else {
if (lang == QLocale::Language::AnyLanguage)
name = title;
else
- name = QString("%1 - [%2]").arg(title).arg(QLocale::languageToString(lang));
+ name = QStringLiteral("%1 - [%2]").arg(title).arg(QLocale::languageToString(lang));
}
return name;
}
@@ -362,18 +331,18 @@ void Player::tracksChanged()
m_subtitleTracks->clear();
const auto audioTracks = m_player->audioTracks();
- m_audioTracks->addItem(QString::fromUtf8("No audio"), -1);
+ m_audioTracks->addItem(QStringLiteral("No audio"), -1);
for (int i = 0; i < audioTracks.size(); ++i)
m_audioTracks->addItem(trackName(audioTracks.at(i), i), i);
m_audioTracks->setCurrentIndex(m_player->activeAudioTrack() + 1);
const auto videoTracks = m_player->videoTracks();
- m_videoTracks->addItem(QString::fromUtf8("No video"), -1);
+ m_videoTracks->addItem(QStringLiteral("No video"), -1);
for (int i = 0; i < videoTracks.size(); ++i)
m_videoTracks->addItem(trackName(videoTracks.at(i), i), i);
m_videoTracks->setCurrentIndex(m_player->activeVideoTrack() + 1);
- m_subtitleTracks->addItem(QString::fromUtf8("No subtitles"), -1);
+ m_subtitleTracks->addItem(QStringLiteral("No subtitles"), -1);
const auto subtitleTracks = m_player->subtitleTracks();
for (int i = 0; i < subtitleTracks.size(); ++i)
m_subtitleTracks->addItem(trackName(subtitleTracks.at(i), i), i);
@@ -425,10 +394,10 @@ void Player::statusChanged(QMediaPlayer::MediaStatus status)
break;
case QMediaPlayer::BufferingMedia:
case QMediaPlayer::BufferedMedia:
- setStatusInfo(tr("Buffering %1%").arg(qRound(m_player->bufferProgress()*100.)));
+ setStatusInfo(tr("Buffering %1%").arg(qRound(m_player->bufferProgress() * 100.)));
break;
case QMediaPlayer::StalledMedia:
- setStatusInfo(tr("Stalled %1%").arg(qRound(m_player->bufferProgress()*100.)));
+ setStatusInfo(tr("Stalled %1%").arg(qRound(m_player->bufferProgress() * 100.)));
break;
case QMediaPlayer::EndOfMedia:
QApplication::alert(this);
@@ -443,9 +412,8 @@ void Player::statusChanged(QMediaPlayer::MediaStatus status)
void Player::handleCursor(QMediaPlayer::MediaStatus status)
{
#ifndef QT_NO_CURSOR
- if (status == QMediaPlayer::LoadingMedia ||
- status == QMediaPlayer::BufferingMedia ||
- status == QMediaPlayer::StalledMedia)
+ if (status == QMediaPlayer::LoadingMedia || status == QMediaPlayer::BufferingMedia
+ || status == QMediaPlayer::StalledMedia)
setCursor(QCursor(Qt::BusyCursor));
else
unsetCursor();
@@ -455,20 +423,24 @@ void Player::handleCursor(QMediaPlayer::MediaStatus status)
void Player::bufferingProgress(float progress)
{
if (m_player->mediaStatus() == QMediaPlayer::StalledMedia)
- setStatusInfo(tr("Stalled %1%").arg(qRound(progress*100.)));
+ setStatusInfo(tr("Stalled %1%").arg(qRound(progress * 100.)));
else
- setStatusInfo(tr("Buffering %1%").arg(qRound(progress*100.)));
+ setStatusInfo(tr("Buffering %1%").arg(qRound(progress * 100.)));
}
void Player::videoAvailableChanged(bool available)
{
if (!available) {
- disconnect(m_fullScreenButton, &QPushButton::clicked, m_videoWidget, &QVideoWidget::setFullScreen);
- disconnect(m_videoWidget, &QVideoWidget::fullScreenChanged, m_fullScreenButton, &QPushButton::setChecked);
+ disconnect(m_fullScreenButton, &QPushButton::clicked, m_videoWidget,
+ &QVideoWidget::setFullScreen);
+ disconnect(m_videoWidget, &QVideoWidget::fullScreenChanged, m_fullScreenButton,
+ &QPushButton::setChecked);
m_videoWidget->setFullScreen(false);
} else {
- connect(m_fullScreenButton, &QPushButton::clicked, m_videoWidget, &QVideoWidget::setFullScreen);
- connect(m_videoWidget, &QVideoWidget::fullScreenChanged, m_fullScreenButton, &QPushButton::setChecked);
+ connect(m_fullScreenButton, &QPushButton::clicked, m_videoWidget,
+ &QVideoWidget::setFullScreen);
+ connect(m_videoWidget, &QVideoWidget::fullScreenChanged, m_fullScreenButton,
+ &QPushButton::setChecked);
if (m_fullScreenButton->isChecked())
m_videoWidget->setFullScreen(true);
@@ -502,7 +474,7 @@ void Player::setTrackInfo(const QString &info)
m_statusLabel->setText(m_statusInfo);
} else {
if (!m_statusInfo.isEmpty())
- setWindowTitle(QString("%1 | %2").arg(m_trackInfo).arg(m_statusInfo));
+ setWindowTitle(QStringLiteral("%1 | %2").arg(m_trackInfo).arg(m_statusInfo));
else
setWindowTitle(m_trackInfo);
}
@@ -517,7 +489,7 @@ void Player::setStatusInfo(const QString &info)
m_statusLabel->setText(m_statusInfo);
} else {
if (!m_statusInfo.isEmpty())
- setWindowTitle(QString("%1 | %2").arg(m_trackInfo).arg(m_statusInfo));
+ setWindowTitle(QStringLiteral("%1 | %2").arg(m_trackInfo).arg(m_statusInfo));
else
setWindowTitle(m_trackInfo);
}
@@ -534,10 +506,10 @@ void Player::updateDurationInfo(qint64 currentInfo)
{
QString tStr;
if (currentInfo || m_duration) {
- QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60,
- currentInfo % 60, (currentInfo * 1000) % 1000);
- QTime totalTime((m_duration / 3600) % 60, (m_duration / 60) % 60,
- m_duration % 60, (m_duration * 1000) % 1000);
+ QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60,
+ (currentInfo * 1000) % 1000);
+ QTime totalTime((m_duration / 3600) % 60, (m_duration / 60) % 60, m_duration % 60,
+ (m_duration * 1000) % 1000);
QString format = "mm:ss";
if (m_duration > 3600)
format = "hh:mm:ss";
@@ -551,3 +523,5 @@ void Player::audioOutputChanged(int index)
auto device = m_audioOutputCombo->itemData(index).value<QAudioDevice>();
m_player->audioOutput()->setDevice(device);
}
+
+#include "moc_player.cpp"
diff --git a/examples/multimedia/player/player.h b/examples/multimedia/player/player.h
new file mode 100644
index 000000000..66b1b8fab
--- /dev/null
+++ b/examples/multimedia/player/player.h
@@ -0,0 +1,99 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include "qmediaplaylist.h"
+
+#include <QMediaMetaData>
+#include <QMediaPlayer>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QAbstractItemView;
+class QLabel;
+class QMediaPlayer;
+class QModelIndex;
+class QPushButton;
+class QComboBox;
+class QSlider;
+class QStatusBar;
+class QVideoWidget;
+QT_END_NAMESPACE
+
+class PlaylistModel;
+
+class Player : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit Player(QWidget *parent = nullptr);
+ ~Player() = default;
+
+ bool isPlayerAvailable() const;
+
+ void addToPlaylist(const QList<QUrl> &urls);
+
+signals:
+ void fullScreenChanged(bool fullScreen);
+
+private slots:
+ void open();
+ void durationChanged(qint64 duration);
+ void positionChanged(qint64 progress);
+ void metaDataChanged();
+ void tracksChanged();
+
+ void previousClicked();
+
+ void seek(int mseconds);
+ void jump(const QModelIndex &index);
+ void playlistPositionChanged(int);
+
+ void statusChanged(QMediaPlayer::MediaStatus status);
+ void bufferingProgress(float progress);
+ void videoAvailableChanged(bool available);
+
+ void selectAudioStream();
+ void selectVideoStream();
+ void selectSubtitleStream();
+
+ void displayErrorMessage();
+
+ void audioOutputChanged(int);
+
+private:
+ void setTrackInfo(const QString &info);
+ void setStatusInfo(const QString &info);
+ void handleCursor(QMediaPlayer::MediaStatus status);
+ void updateDurationInfo(qint64 currentInfo);
+ QString trackName(const QMediaMetaData &metaData, int index);
+
+ QMediaPlayer *m_player = nullptr;
+ QAudioOutput *m_audioOutput = nullptr;
+ QMediaPlaylist *m_playlist = nullptr;
+ QVideoWidget *m_videoWidget = nullptr;
+ QSlider *m_slider = nullptr;
+ QLabel *m_labelDuration = nullptr;
+ QPushButton *m_fullScreenButton = nullptr;
+ QComboBox *m_audioOutputCombo = nullptr;
+ QLabel *m_statusLabel = nullptr;
+ QStatusBar *m_statusBar = nullptr;
+
+ QComboBox *m_audioTracks = nullptr;
+ QComboBox *m_videoTracks = nullptr;
+ QComboBox *m_subtitleTracks = nullptr;
+
+ PlaylistModel *m_playlistModel = nullptr;
+ QAbstractItemView *m_playlistView = nullptr;
+ QString m_trackInfo;
+ QString m_statusInfo;
+ qint64 m_duration;
+
+ QWidget *m_metaDataFields[QMediaMetaData::NumMetaData] = {};
+ QLabel *m_metaDataLabels[QMediaMetaData::NumMetaData] = {};
+};
+
+#endif // PLAYER_H
diff --git a/examples/multimediawidgets/player/player.pro b/examples/multimedia/player/player.pro
index 950fe8e0d..6d9383692 100644
--- a/examples/multimediawidgets/player/player.pro
+++ b/examples/multimedia/player/player.pro
@@ -13,7 +13,7 @@ HEADERS = \
videowidget.h \
qmediaplaylist.h \
qmediaplaylist_p.h \
- qplaylistfileparser_p.h
+ qplaylistfileparser.h
SOURCES = main.cpp \
player.cpp \
@@ -21,7 +21,8 @@ SOURCES = main.cpp \
playlistmodel.cpp \
videowidget.cpp \
qmediaplaylist.cpp \
+ qmediaplaylist_p.cpp \
qplaylistfileparser.cpp
-target.path = $$[QT_INSTALL_EXAMPLES]/multimediawidgets/player
+target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/player
INSTALLS += target
diff --git a/examples/multimediawidgets/player/playercontrols.cpp b/examples/multimedia/player/playercontrols.cpp
index 73cf9ae21..4933bf8cf 100644
--- a/examples/multimediawidgets/player/playercontrols.cpp
+++ b/examples/multimedia/player/playercontrols.cpp
@@ -1,64 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "playercontrols.h"
+#include <QAudio>
#include <QBoxLayout>
+#include <QComboBox>
#include <QSlider>
#include <QStyle>
#include <QToolButton>
-#include <QComboBox>
-#include <QAudio>
-PlayerControls::PlayerControls(QWidget *parent)
- : QWidget(parent)
+PlayerControls::PlayerControls(QWidget *parent) : QWidget(parent)
{
m_playButton = new QToolButton(this);
m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
@@ -88,8 +40,12 @@ PlayerControls::PlayerControls(QWidget *parent)
m_volumeSlider = new QSlider(Qt::Horizontal, this);
m_volumeSlider->setRange(0, 100);
+ QSizePolicy sp = m_volumeSlider->sizePolicy();
+ sp.setHorizontalPolicy(QSizePolicy::MinimumExpanding);
+ m_volumeSlider->setSizePolicy(sp);
- connect(m_volumeSlider, &QSlider::valueChanged, this, &PlayerControls::onVolumeSliderValueChanged);
+ connect(m_volumeSlider, &QSlider::valueChanged, this,
+ &PlayerControls::onVolumeSliderValueChanged);
m_rateBox = new QComboBox(this);
m_rateBox->addItem("0.5x", QVariant(0.5));
@@ -97,7 +53,8 @@ PlayerControls::PlayerControls(QWidget *parent)
m_rateBox->addItem("2.0x", QVariant(2.0));
m_rateBox->setCurrentIndex(1);
- connect(m_rateBox, QOverload<int>::of(&QComboBox::activated), this, &PlayerControls::updateRate);
+ connect(m_rateBox, QOverload<int>::of(&QComboBox::activated), this,
+ &PlayerControls::updateRate);
QBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
@@ -140,17 +97,16 @@ void PlayerControls::setState(QMediaPlayer::PlaybackState state)
float PlayerControls::volume() const
{
- qreal linearVolume = QAudio::convertVolume(m_volumeSlider->value() / qreal(100),
- QAudio::LogarithmicVolumeScale,
- QAudio::LinearVolumeScale);
+ qreal linearVolume =
+ QAudio::convertVolume(m_volumeSlider->value() / qreal(100),
+ QAudio::LogarithmicVolumeScale, QAudio::LinearVolumeScale);
return linearVolume;
}
void PlayerControls::setVolume(float volume)
{
- qreal logarithmicVolume = QAudio::convertVolume(volume,
- QAudio::LinearVolumeScale,
+ qreal logarithmicVolume = QAudio::convertVolume(volume, QAudio::LinearVolumeScale,
QAudio::LogarithmicVolumeScale);
m_volumeSlider->setValue(qRound(logarithmicVolume * 100));
@@ -166,9 +122,8 @@ void PlayerControls::setMuted(bool muted)
if (muted != m_playerMuted) {
m_playerMuted = muted;
- m_muteButton->setIcon(style()->standardIcon(muted
- ? QStyle::SP_MediaVolumeMuted
- : QStyle::SP_MediaVolume));
+ m_muteButton->setIcon(style()->standardIcon(muted ? QStyle::SP_MediaVolumeMuted
+ : QStyle::SP_MediaVolume));
}
}
@@ -204,7 +159,7 @@ void PlayerControls::setPlaybackRate(float rate)
}
}
- m_rateBox->addItem(QString("%1x").arg(rate), QVariant(rate));
+ m_rateBox->addItem(QStringLiteral("%1x").arg(rate), QVariant(rate));
m_rateBox->setCurrentIndex(m_rateBox->count() - 1);
}
@@ -217,3 +172,5 @@ void PlayerControls::onVolumeSliderValueChanged()
{
emit changeVolume(volume());
}
+
+#include "moc_playercontrols.cpp"
diff --git a/examples/multimedia/player/playercontrols.h b/examples/multimedia/player/playercontrols.h
new file mode 100644
index 000000000..72dddd68f
--- /dev/null
+++ b/examples/multimedia/player/playercontrols.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PLAYERCONTROLS_H
+#define PLAYERCONTROLS_H
+
+#include <QMediaPlayer>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QAbstractButton;
+class QAbstractSlider;
+class QComboBox;
+QT_END_NAMESPACE
+
+class PlayerControls : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PlayerControls(QWidget *parent = nullptr);
+
+ QMediaPlayer::PlaybackState state() const;
+ float volume() const;
+ bool isMuted() const;
+ qreal playbackRate() const;
+
+public slots:
+ void setState(QMediaPlayer::PlaybackState state);
+ void setVolume(float volume);
+ void setMuted(bool muted);
+ void setPlaybackRate(float rate);
+
+signals:
+ void play();
+ void pause();
+ void stop();
+ void next();
+ void previous();
+ void changeVolume(float volume);
+ void changeMuting(bool muting);
+ void changeRate(qreal rate);
+
+private slots:
+ void playClicked();
+ void muteClicked();
+ void updateRate();
+ void onVolumeSliderValueChanged();
+
+private:
+ QMediaPlayer::PlaybackState m_playerState = QMediaPlayer::StoppedState;
+ bool m_playerMuted = false;
+ QAbstractButton *m_playButton = nullptr;
+ QAbstractButton *m_stopButton = nullptr;
+ QAbstractButton *m_nextButton = nullptr;
+ QAbstractButton *m_previousButton = nullptr;
+ QAbstractButton *m_muteButton = nullptr;
+ QAbstractSlider *m_volumeSlider = nullptr;
+ QComboBox *m_rateBox = nullptr;
+};
+
+#endif // PLAYERCONTROLS_H
diff --git a/examples/multimedia/player/playlistmodel.cpp b/examples/multimedia/player/playlistmodel.cpp
new file mode 100644
index 000000000..7ae76bcac
--- /dev/null
+++ b/examples/multimedia/player/playlistmodel.cpp
@@ -0,0 +1,105 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "playlistmodel.h"
+#include "qmediaplaylist.h"
+
+#include <QFileInfo>
+#include <QUrl>
+
+PlaylistModel::PlaylistModel(QObject *parent) : QAbstractItemModel(parent)
+{
+ m_playlist.reset(new QMediaPlaylist);
+ connect(m_playlist.data(), &QMediaPlaylist::mediaAboutToBeInserted, this,
+ &PlaylistModel::beginInsertItems);
+ connect(m_playlist.data(), &QMediaPlaylist::mediaInserted, this,
+ &PlaylistModel::endInsertItems);
+ connect(m_playlist.data(), &QMediaPlaylist::mediaAboutToBeRemoved, this,
+ &PlaylistModel::beginRemoveItems);
+ connect(m_playlist.data(), &QMediaPlaylist::mediaRemoved, this, &PlaylistModel::endRemoveItems);
+ connect(m_playlist.data(), &QMediaPlaylist::mediaChanged, this, &PlaylistModel::changeItems);
+}
+
+PlaylistModel::~PlaylistModel() = default;
+
+int PlaylistModel::rowCount(const QModelIndex &parent) const
+{
+ return m_playlist && !parent.isValid() ? m_playlist->mediaCount() : 0;
+}
+
+int PlaylistModel::columnCount(const QModelIndex &parent) const
+{
+ return !parent.isValid() ? ColumnCount : 0;
+}
+
+QModelIndex PlaylistModel::index(int row, int column, const QModelIndex &parent) const
+{
+ return m_playlist && !parent.isValid() && row >= 0 && row < m_playlist->mediaCount()
+ && column >= 0 && column < ColumnCount
+ ? createIndex(row, column)
+ : QModelIndex();
+}
+
+QModelIndex PlaylistModel::parent(const QModelIndex &child) const
+{
+ Q_UNUSED(child);
+
+ return QModelIndex();
+}
+
+QVariant PlaylistModel::data(const QModelIndex &index, int role) const
+{
+ if (index.isValid() && role == Qt::DisplayRole) {
+ QVariant value = m_data[index];
+ if (!value.isValid() && index.column() == Title) {
+ QUrl location = m_playlist->media(index.row());
+ return QFileInfo(location.path()).fileName();
+ }
+
+ return value;
+ }
+ return QVariant();
+}
+
+QMediaPlaylist *PlaylistModel::playlist() const
+{
+ return m_playlist.data();
+}
+
+bool PlaylistModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ Q_UNUSED(role);
+ m_data[index] = value;
+ emit dataChanged(index, index);
+ return true;
+}
+
+void PlaylistModel::beginInsertItems(int start, int end)
+{
+ m_data.clear();
+ beginInsertRows(QModelIndex(), start, end);
+}
+
+void PlaylistModel::endInsertItems()
+{
+ endInsertRows();
+}
+
+void PlaylistModel::beginRemoveItems(int start, int end)
+{
+ m_data.clear();
+ beginRemoveRows(QModelIndex(), start, end);
+}
+
+void PlaylistModel::endRemoveItems()
+{
+ endInsertRows();
+}
+
+void PlaylistModel::changeItems(int start, int end)
+{
+ m_data.clear();
+ emit dataChanged(index(start, 0), index(end, ColumnCount));
+}
+
+#include "moc_playlistmodel.cpp"
diff --git a/examples/multimedia/player/playlistmodel.h b/examples/multimedia/player/playlistmodel.h
new file mode 100644
index 000000000..0c510f6e0
--- /dev/null
+++ b/examples/multimedia/player/playlistmodel.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PLAYLISTMODEL_H
+#define PLAYLISTMODEL_H
+
+#include <QAbstractItemModel>
+#include <QScopedPointer>
+
+QT_BEGIN_NAMESPACE
+class QMediaPlaylist;
+QT_END_NAMESPACE
+
+class PlaylistModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ enum Column { Title = 0, ColumnCount };
+
+ explicit PlaylistModel(QObject *parent = nullptr);
+ ~PlaylistModel();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QModelIndex index(int row, int column,
+ const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &child) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ QMediaPlaylist *playlist() const;
+
+ bool setData(const QModelIndex &index, const QVariant &value,
+ int role = Qt::DisplayRole) override;
+
+private slots:
+ void beginInsertItems(int start, int end);
+ void endInsertItems();
+ void beginRemoveItems(int start, int end);
+ void endRemoveItems();
+ void changeItems(int start, int end);
+
+private:
+ QScopedPointer<QMediaPlaylist> m_playlist;
+ QMap<QModelIndex, QVariant> m_data;
+};
+
+#endif // PLAYLISTMODEL_H
diff --git a/examples/multimediawidgets/player/qmediaplaylist.cpp b/examples/multimedia/player/qmediaplaylist.cpp
index d2fbe3753..739dd1cb2 100644
--- a/examples/multimediawidgets/player/qmediaplaylist.cpp
+++ b/examples/multimedia/player/qmediaplaylist.cpp
@@ -1,52 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "qmediaplaylist.h"
#include "qmediaplaylist_p.h"
-#include "qplaylistfileparser_p.h"
+#include "qplaylistfileparser.h"
-#include <QtCore/qlist.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qurl.h>
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qcoreapplication.h>
+#include <QCoreApplication>
+#include <QFile>
+#include <QList>
#include <QRandomGenerator>
+#include <QUrl>
QT_BEGIN_NAMESPACE
@@ -54,16 +17,13 @@ class QM3uPlaylistWriter
{
public:
QM3uPlaylistWriter(QIODevice *device)
- :m_device(device), m_textStream(new QTextStream(m_device))
+ : m_device(device), m_textStream(new QTextStream(m_device))
{
}
- ~QM3uPlaylistWriter()
- {
- delete m_textStream;
- }
+ ~QM3uPlaylistWriter() { delete m_textStream; }
- bool writeItem(const QUrl& item)
+ bool writeItem(const QUrl &item)
{
*m_textStream << item.toString() << Qt::endl;
return true;
@@ -74,7 +34,6 @@ private:
QTextStream *m_textStream;
};
-
int QMediaPlaylistPrivate::nextPosition(int steps) const
{
if (playlist.count() == 0)
@@ -89,7 +48,7 @@ int QMediaPlaylistPrivate::nextPosition(int steps) const
return currentPos;
case QMediaPlaylist::Sequential:
if (next >= playlist.size())
- next = -1;
+ next = 0;
break;
case QMediaPlaylist::Loop:
next %= playlist.count();
@@ -116,7 +75,7 @@ int QMediaPlaylistPrivate::prevPosition(int steps) const
return currentPos;
case QMediaPlaylist::Sequential:
if (next < 0)
- next = -1;
+ next = playlist.size() - 1;
break;
case QMediaPlaylist::Loop:
next %= playlist.size();
@@ -153,7 +112,6 @@ int QMediaPlaylistPrivate::prevPosition(int steps) const
\sa QUrl
*/
-
/*!
\enum QMediaPlaylist::PlaybackMode
@@ -163,23 +121,21 @@ int QMediaPlaylistPrivate::prevPosition(int steps) const
\value CurrentItemInLoop The current item is played repeatedly in a loop.
- \value Sequential Playback starts from the current and moves through each successive item until the last is reached and then stops.
- The next item is a null item when the last one is currently playing.
+ \value Sequential Playback starts from the current and moves through each successive
+ item until the last is reached and then stops. The next item is a null item when the last one is
+ currently playing.
- \value Loop Playback restarts at the first item after the last has finished playing.
+ \value Loop Playback restarts at the first item after the last has finished
+ playing.
\value Random Play items in random order.
*/
-
-
/*!
Create a new playlist object with the given \a parent.
*/
-QMediaPlaylist::QMediaPlaylist(QObject *parent)
- : QObject(parent)
- , d_ptr(new QMediaPlaylistPrivate)
+QMediaPlaylist::QMediaPlaylist(QObject *parent) : QObject(parent), d_ptr(new QMediaPlaylistPrivate)
{
Q_D(QMediaPlaylist);
@@ -266,7 +222,6 @@ int QMediaPlaylist::previousIndex(int steps) const
return d_func()->prevPosition(steps);
}
-
/*!
Returns the number of items in the playlist.
@@ -380,8 +335,7 @@ bool QMediaPlaylist::insertMedia(int pos, const QList<QUrl> &items)
bool QMediaPlaylist::moveMedia(int from, int to)
{
Q_D(QMediaPlaylist);
- if (from < 0 || from > d->playlist.count() ||
- to < 0 || to > d->playlist.count())
+ if (from < 0 || from > d->playlist.count() || to < 0 || to > d->playlist.count())
return false;
d->playlist.move(from, to);
@@ -551,7 +505,8 @@ void QMediaPlaylist::shuffle()
current = d->playlist.takeAt(d->currentPos);
while (!d->playlist.isEmpty())
- playlist.append(d->playlist.takeAt(QRandomGenerator::global()->bounded(int(d->playlist.size()))));
+ playlist.append(
+ d->playlist.takeAt(QRandomGenerator::global()->bounded(int(d->playlist.size()))));
if (d->currentPos != -1)
playlist.insert(d->currentPos, current);
@@ -559,7 +514,6 @@ void QMediaPlaylist::shuffle()
emit mediaChanged(0, d->playlist.count());
}
-
/*!
Advance to the next media content in playlist.
*/
diff --git a/examples/multimediawidgets/player/qmediaplaylist.h b/examples/multimedia/player/qmediaplaylist.h
index 163ef93fe..358f8ca60 100644
--- a/examples/multimediawidgets/player/qmediaplaylist.h
+++ b/examples/multimedia/player/qmediaplaylist.h
@@ -1,50 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef QMEDIAPLAYLIST_H
#define QMEDIAPLAYLIST_H
-#include <QtCore/qobject.h>
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qmediaenumdebug.h>
+#include <QObject>
+#include <qmediaenumdebug.h>
QT_BEGIN_NAMESPACE
@@ -52,7 +14,8 @@ class QMediaPlaylistPrivate;
class QMediaPlaylist : public QObject
{
Q_OBJECT
- Q_PROPERTY(QMediaPlaylist::PlaybackMode playbackMode READ playbackMode WRITE setPlaybackMode NOTIFY playbackModeChanged)
+ Q_PROPERTY(QMediaPlaylist::PlaybackMode playbackMode READ playbackMode WRITE setPlaybackMode
+ NOTIFY playbackModeChanged)
Q_PROPERTY(QUrl currentMedia READ currentMedia NOTIFY currentMediaChanged)
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
@@ -97,7 +60,7 @@ public:
Error error() const;
QString errorString() const;
-public Q_SLOTS:
+public slots:
void shuffle();
void next();
@@ -105,10 +68,10 @@ public Q_SLOTS:
void setCurrentIndex(int index);
-Q_SIGNALS:
+signals:
void currentIndexChanged(int index);
void playbackModeChanged(QMediaPlaylist::PlaybackMode mode);
- void currentMediaChanged(const QUrl&);
+ void currentMediaChanged(const QUrl &);
void mediaAboutToBeInserted(int start, int end);
void mediaInserted(int start, int end);
@@ -129,4 +92,4 @@ QT_END_NAMESPACE
Q_MEDIA_ENUM_DEBUG(QMediaPlaylist, PlaybackMode)
Q_MEDIA_ENUM_DEBUG(QMediaPlaylist, Error)
-#endif // QMEDIAPLAYLIST_H
+#endif // QMEDIAPLAYLIST_H
diff --git a/examples/multimedia/player/qmediaplaylist_p.cpp b/examples/multimedia/player/qmediaplaylist_p.cpp
new file mode 100644
index 000000000..13e164484
--- /dev/null
+++ b/examples/multimedia/player/qmediaplaylist_p.cpp
@@ -0,0 +1,65 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "qmediaplaylist_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QMediaPlaylistPrivate::QMediaPlaylistPrivate() : error(QMediaPlaylist::NoError) { }
+
+QMediaPlaylistPrivate::~QMediaPlaylistPrivate()
+{
+ delete parser;
+}
+
+void QMediaPlaylistPrivate::loadFailed(QMediaPlaylist::Error error, const QString &errorString)
+{
+ this->error = error;
+ this->errorString = errorString;
+
+ emit q_ptr->loadFailed();
+}
+
+void QMediaPlaylistPrivate::loadFinished()
+{
+ q_ptr->addMedia(parser->playlist);
+
+ emit q_ptr->loaded();
+}
+
+bool QMediaPlaylistPrivate::checkFormat(const char *format) const
+{
+ QLatin1String f(format);
+ QPlaylistFileParser::FileType type =
+ format ? QPlaylistFileParser::UNKNOWN : QPlaylistFileParser::M3U8;
+ if (format) {
+ if (f == QLatin1String("m3u") || f == QLatin1String("text/uri-list")
+ || f == QLatin1String("audio/x-mpegurl") || f == QLatin1String("audio/mpegurl"))
+ type = QPlaylistFileParser::M3U;
+ else if (f == QLatin1String("m3u8") || f == QLatin1String("application/x-mpegURL")
+ || f == QLatin1String("application/vnd.apple.mpegurl"))
+ type = QPlaylistFileParser::M3U8;
+ }
+
+ if (type == QPlaylistFileParser::UNKNOWN || type == QPlaylistFileParser::PLS) {
+ error = QMediaPlaylist::FormatNotSupportedError;
+ errorString = QMediaPlaylist::tr("This file format is not supported.");
+ return false;
+ }
+ return true;
+}
+
+void QMediaPlaylistPrivate::ensureParser()
+{
+ if (parser)
+ return;
+
+ parser = new QPlaylistFileParser(q_ptr);
+ QObject::connect(parser, &QPlaylistFileParser::finished, q_ptr, [this]() { loadFinished(); });
+ QObject::connect(parser, &QPlaylistFileParser::error, q_ptr,
+ [this](QMediaPlaylist::Error err, const QString &errorMsg) {
+ loadFailed(err, errorMsg);
+ });
+}
+
+QT_END_NAMESPACE
diff --git a/examples/multimedia/player/qmediaplaylist_p.h b/examples/multimedia/player/qmediaplaylist_p.h
new file mode 100644
index 000000000..df43776fc
--- /dev/null
+++ b/examples/multimedia/player/qmediaplaylist_p.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef QMEDIAPLAYLIST_P_H
+#define QMEDIAPLAYLIST_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmediaplaylist.h"
+#include "qplaylistfileparser.h"
+
+#include <QUrl>
+
+#include <QDebug>
+
+#ifdef Q_MOC_RUN
+# pragma Q_MOC_EXPAND_MACROS
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMediaPlaylistControl;
+
+class QMediaPlaylistPrivate
+{
+ Q_DECLARE_PUBLIC(QMediaPlaylist)
+public:
+ QMediaPlaylistPrivate();
+
+ virtual ~QMediaPlaylistPrivate();
+
+ void loadFailed(QMediaPlaylist::Error error, const QString &errorString);
+
+ void loadFinished();
+
+ bool checkFormat(const char *format) const;
+
+ void ensureParser();
+
+ int nextPosition(int steps) const;
+ int prevPosition(int steps) const;
+
+ QList<QUrl> playlist;
+
+ int currentPos = -1;
+ QMediaPlaylist::PlaybackMode playbackMode = QMediaPlaylist::Sequential;
+
+ QPlaylistFileParser *parser = nullptr;
+ mutable QMediaPlaylist::Error error;
+ mutable QString errorString;
+
+ QMediaPlaylist *q_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMEDIAPLAYLIST_P_H
diff --git a/examples/multimediawidgets/player/qplaylistfileparser.cpp b/examples/multimedia/player/qplaylistfileparser.cpp
index d684a9853..3f5104761 100644
--- a/examples/multimediawidgets/player/qplaylistfileparser.cpp
+++ b/examples/multimedia/player/qplaylistfileparser.cpp
@@ -1,51 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qplaylistfileparser_p.h"
-#include <qfileinfo.h>
-#include <QtCore/QDebug>
-#include <QtCore/qiodevice.h>
-#include <QtCore/qpointer.h>
-#include <QtNetwork/QNetworkReply>
-#include <QtNetwork/QNetworkRequest>
-#include "qmediaplayer.h"
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#include "qmediametadata.h"
+#include "qmediaplayer.h"
+#include "qplaylistfileparser.h"
+
+#include <QDebug>
+#include <QFileInfo>
+#include <QIODevice>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QPointer>
QT_BEGIN_NAMESPACE
@@ -54,14 +19,12 @@ namespace {
class ParserBase
{
public:
- explicit ParserBase(QPlaylistFileParser *parent)
- : m_parent(parent)
- , m_aborted(false)
+ explicit ParserBase(QPlaylistFileParser *parent) : m_parent(parent), m_aborted(false)
{
Q_ASSERT(m_parent);
}
- bool parseLine(int lineIndex, const QString& line, const QUrl& root)
+ bool parseLine(int lineIndex, const QString &line, const QUrl &root)
{
if (m_aborted)
return false;
@@ -74,7 +37,7 @@ public:
virtual ~ParserBase() = default;
protected:
- virtual bool parseLineImpl(int lineIndex, const QString& line, const QUrl& root) = 0;
+ virtual bool parseLineImpl(int lineIndex, const QString &line, const QUrl &root) = 0;
static QUrl expandToFullPath(const QUrl &root, const QString &line)
{
@@ -88,7 +51,8 @@ protected:
if (url.scheme().isEmpty()) {
// Resolve it relative to root
if (root.isLocalFile())
- return QUrl::fromUserInput(line, root.adjusted(QUrl::RemoveFilename).toLocalFile(), QUrl::AssumeLocalFile);
+ return QUrl::fromUserInput(line, root.adjusted(QUrl::RemoveFilename).toLocalFile(),
+ QUrl::AssumeLocalFile);
return root.resolved(url);
}
if (url.scheme().length() == 1)
@@ -98,8 +62,7 @@ protected:
return url;
}
- void newItemFound(const QVariant& content) { Q_EMIT m_parent->newItem(content); }
-
+ void newItemFound(const QVariant &content) { Q_EMIT m_parent->newItem(content); }
QPlaylistFileParser *m_parent;
bool m_aborted;
@@ -108,11 +71,7 @@ protected:
class M3UParser : public ParserBase
{
public:
- explicit M3UParser(QPlaylistFileParser *q)
- : ParserBase(q)
- , m_extendedFormat(false)
- {
- }
+ explicit M3UParser(QPlaylistFileParser *q) : ParserBase(q), m_extendedFormat(false) { }
/*
*
@@ -131,31 +90,41 @@ public:
C:\Documents and Settings\I\My Music\Greatest Hits\Example.ogg
*/
- bool parseLineImpl(int lineIndex, const QString& line, const QUrl& root) override
+ bool parseLineImpl(int lineIndex, const QString &line, const QUrl &root) override
{
- if (line[0] == u'#' ) {
+ if (line[0] == u'#') {
if (m_extendedFormat) {
if (line.startsWith(QLatin1String("#EXTINF:"))) {
m_extraInfo.clear();
int artistStart = line.indexOf(QLatin1String(","), 8);
bool ok = false;
- QStringView lineView { line };
- int length = lineView.mid(8, artistStart < 8 ? -1 : artistStart - 8).trimmed().toInt(&ok);
+ QStringView lineView{ line };
+ int length = lineView.mid(8, artistStart < 8 ? -1 : artistStart - 8)
+ .trimmed()
+ .toInt(&ok);
if (ok && length > 0) {
- //convert from second to milisecond
+ // convert from second to milisecond
m_extraInfo[QMediaMetaData::Duration] = QVariant(length * 1000);
}
if (artistStart > 0) {
int titleStart = getSplitIndex(line, artistStart);
if (titleStart > artistStart) {
- m_extraInfo[QMediaMetaData::Author] = lineView.mid(artistStart + 1,
- titleStart - artistStart - 1).trimmed().toString().
- replace(QLatin1String("--"), QLatin1String("-"));
- m_extraInfo[QMediaMetaData::Title] = lineView.mid(titleStart + 1).trimmed().toString().
- replace(QLatin1String("--"), QLatin1String("-"));
+ m_extraInfo[QMediaMetaData::Author] =
+ lineView.mid(artistStart + 1, titleStart - artistStart - 1)
+ .trimmed()
+ .toString()
+ .replace(QLatin1String("--"), QLatin1String("-"));
+ m_extraInfo[QMediaMetaData::Title] =
+ lineView.mid(titleStart + 1)
+ .trimmed()
+ .toString()
+ .replace(QLatin1String("--"), QLatin1String("-"));
} else {
- m_extraInfo[QMediaMetaData::Title] = lineView.mid(artistStart + 1).trimmed().toString().
- replace(QLatin1String("--"), QLatin1String("-"));
+ m_extraInfo[QMediaMetaData::Title] =
+ lineView.mid(artistStart + 1)
+ .trimmed()
+ .toString()
+ .replace(QLatin1String("--"), QLatin1String("-"));
}
}
}
@@ -173,11 +142,11 @@ public:
return true;
}
- int getSplitIndex(const QString& line, int startPos)
+ int getSplitIndex(const QString &line, int startPos)
{
if (startPos < 0)
startPos = 0;
- const QChar* buf = line.data();
+ const QChar *buf = line.data();
for (int i = startPos; i < line.length(); ++i) {
if (buf[i] == u'-') {
if (i == line.length() - 1)
@@ -191,56 +160,53 @@ public:
}
private:
- QMediaMetaData m_extraInfo;
- bool m_extendedFormat;
+ QMediaMetaData m_extraInfo;
+ bool m_extendedFormat;
};
class PLSParser : public ParserBase
{
public:
- explicit PLSParser(QPlaylistFileParser *q)
- : ParserBase(q)
- {
- }
+ explicit PLSParser(QPlaylistFileParser *q) : ParserBase(q) { }
-/*
- *
-The format is essentially that of an INI file structured as follows:
+ /*
+ *
+ The format is essentially that of an INI file structured as follows:
-Header
+ Header
- * [playlist] : This tag indicates that it is a Playlist File
+ * [playlist] : This tag indicates that it is a Playlist File
-Track Entry
-Assuming track entry #X
+ Track Entry
+ Assuming track entry #X
- * FileX : Variable defining location of stream.
- * TitleX : Defines track title.
- * LengthX : Length in seconds of track. Value of -1 indicates indefinite.
+ * FileX : Variable defining location of stream.
+ * TitleX : Defines track title.
+ * LengthX : Length in seconds of track. Value of -1 indicates indefinite.
-Footer
+ Footer
- * NumberOfEntries : This variable indicates the number of tracks.
- * Version : Playlist version. Currently only a value of 2 is valid.
+ * NumberOfEntries : This variable indicates the number of tracks.
+ * Version : Playlist version. Currently only a value of 2 is valid.
-[playlist]
+ [playlist]
-File1=Alternative\everclear - SMFTA.mp3
+ File1=Alternative\everclear - SMFTA.mp3
-Title1=Everclear - So Much For The Afterglow
+ Title1=Everclear - So Much For The Afterglow
-Length1=233
+ Length1=233
-File2=http://www.site.com:8000/listen.pls
+ File2=http://www.site.com:8000/listen.pls
-Title2=My Cool Stream
+ Title2=My Cool Stream
-Length5=-1
+ Length5=-1
-NumberOfEntries=2
+ NumberOfEntries=2
-Version=2
-*/
+ Version=2
+ */
bool parseLineImpl(int, const QString &line, const QUrl &root) override
{
// We ignore everything but 'File' entries, since that's the only thing we care about.
@@ -258,7 +224,8 @@ Version=2
return true;
}
- QString getValue(QStringView line) {
+ QString getValue(QStringView line)
+ {
int start = line.indexOf(u'=');
if (start < 0)
return QString();
@@ -274,13 +241,13 @@ class QPlaylistFileParserPrivate
Q_DECLARE_PUBLIC(QPlaylistFileParser)
public:
QPlaylistFileParserPrivate(QPlaylistFileParser *q)
- : q_ptr(q)
- , m_stream(nullptr)
- , m_type(QPlaylistFileParser::UNKNOWN)
- , m_scanIndex(0)
- , m_lineIndex(-1)
- , m_utf8(false)
- , m_aborted(false)
+ : q_ptr(q),
+ m_stream(nullptr),
+ m_type(QPlaylistFileParser::UNKNOWN),
+ m_scanIndex(0),
+ m_lineIndex(-1),
+ m_utf8(false),
+ m_aborted(false)
{
}
@@ -291,8 +258,8 @@ public:
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> m_source;
QScopedPointer<ParserBase> m_currentParser;
- QByteArray m_buffer;
- QUrl m_root;
+ QByteArray m_buffer;
+ QUrl m_root;
QNetworkAccessManager m_mgr;
QString m_mimeType;
QPlaylistFileParser *q_ptr;
@@ -304,7 +271,12 @@ public:
QUrl m_media;
QString m_mimeType;
[[nodiscard]] bool isValid() const { return m_stream || !m_media.isEmpty(); }
- void reset() { m_stream = nullptr; m_media = QUrl(); m_mimeType = QString(); }
+ void reset()
+ {
+ m_stream = nullptr;
+ m_media = QUrl();
+ m_mimeType = QString();
+ }
} m_pendingJob;
int m_scanIndex;
int m_lineIndex;
@@ -315,8 +287,8 @@ private:
bool processLine(int startIndex, int length);
};
-#define LINE_LIMIT 4096
-#define READ_LIMIT 64
+#define LINE_LIMIT 4096
+#define READ_LIMIT 64
bool QPlaylistFileParserPrivate::processLine(int startIndex, int length)
{
@@ -329,7 +301,9 @@ bool QPlaylistFileParserPrivate::processLine(int startIndex, int length)
QString mimeType;
if (m_source)
mimeType = m_source->header(QNetworkRequest::ContentTypeHeader).toString();
- m_type = QPlaylistFileParser::findPlaylistType(suffix, !mimeType.isEmpty() ? mimeType : m_mimeType, m_buffer.constData(), quint32(m_buffer.size()));
+ m_type = QPlaylistFileParser::findPlaylistType(
+ suffix, !mimeType.isEmpty() ? mimeType : m_mimeType, m_buffer.constData(),
+ quint32(m_buffer.size()));
switch (m_type) {
case QPlaylistFileParser::UNKNOWN:
@@ -370,8 +344,9 @@ void QPlaylistFileParserPrivate::handleData()
{
Q_Q(QPlaylistFileParser);
while (m_stream->bytesAvailable() && !m_aborted) {
- int expectedBytes = qMin(READ_LIMIT, int(qMin(m_stream->bytesAvailable(),
- qint64(LINE_LIMIT - m_buffer.size()))));
+ int expectedBytes =
+ qMin(READ_LIMIT,
+ int(qMin(m_stream->bytesAvailable(), qint64(LINE_LIMIT - m_buffer.size()))));
m_buffer.push_back(m_stream->read(expectedBytes));
int processedBytes = 0;
while (m_scanIndex < m_buffer.length() && !m_aborted) {
@@ -384,7 +359,7 @@ void QPlaylistFileParserPrivate::handleData()
}
processedBytes = m_scanIndex + 1;
if (!m_stream) {
- //some error happened, so exit parsing
+ // some error happened, so exit parsing
return;
}
}
@@ -395,13 +370,14 @@ void QPlaylistFileParserPrivate::handleData()
break;
if (m_buffer.length() - processedBytes >= LINE_LIMIT) {
- emit q->error(QMediaPlaylist::FormatError, QMediaPlaylist::tr("invalid line in playlist file"));
+ emit q->error(QMediaPlaylist::FormatError,
+ QMediaPlaylist::tr("invalid line in playlist file"));
q->abort();
break;
}
if (!m_stream->bytesAvailable() && (!m_source || !m_source->isFinished())) {
- //last line
+ // last line
processLine(processedBytes, -1);
break;
}
@@ -424,20 +400,20 @@ void QPlaylistFileParserPrivate::handleData()
}
QPlaylistFileParser::QPlaylistFileParser(QObject *parent)
- : QObject(parent)
- , d_ptr(new QPlaylistFileParserPrivate(this))
+ : QObject(parent), d_ptr(new QPlaylistFileParserPrivate(this))
{
-
}
QPlaylistFileParser::~QPlaylistFileParser() = default;
QPlaylistFileParser::FileType QPlaylistFileParser::findByMimeType(const QString &mime)
{
- if (mime == QLatin1String("text/uri-list") || mime == QLatin1String("audio/x-mpegurl") || mime == QLatin1String("audio/mpegurl"))
+ if (mime == QLatin1String("text/uri-list") || mime == QLatin1String("audio/x-mpegurl")
+ || mime == QLatin1String("audio/mpegurl"))
return QPlaylistFileParser::M3U;
- if (mime == QLatin1String("application/x-mpegURL") || mime == QLatin1String("application/vnd.apple.mpegurl"))
+ if (mime == QLatin1String("application/x-mpegURL")
+ || mime == QLatin1String("application/vnd.apple.mpegurl"))
return QPlaylistFileParser::M3U8;
if (mime == QLatin1String("audio/x-scpls"))
@@ -476,10 +452,9 @@ QPlaylistFileParser::FileType QPlaylistFileParser::findByDataHeader(const char *
return QPlaylistFileParser::UNKNOWN;
}
-QPlaylistFileParser::FileType QPlaylistFileParser::findPlaylistType(const QString& suffix,
- const QString& mime,
- const char *data,
- quint32 size)
+QPlaylistFileParser::FileType QPlaylistFileParser::findPlaylistType(const QString &suffix,
+ const QString &mime,
+ const char *data, quint32 size)
{
FileType dataHeaderType = findByDataHeader(data, size);
@@ -536,13 +511,14 @@ void QPlaylistFileParser::start(QIODevice *stream, const QString &mimeType)
d->handleData();
}
-void QPlaylistFileParser::start(const QUrl& request, const QString &mimeType)
+void QPlaylistFileParser::start(const QUrl &request, const QString &mimeType)
{
Q_D(QPlaylistFileParser);
const QUrl &url = request.url();
if (url.isLocalFile() && !QFile::exists(url.toLocalFile())) {
- emit error(QMediaPlaylist::AccessDeniedError, QString(QMediaPlaylist::tr("%1 does not exist")).arg(url.toString()));
+ emit error(QMediaPlaylist::AccessDeniedError,
+ QString(QMediaPlaylist::tr("%1 does not exist")).arg(url.toString()));
return;
}
@@ -559,7 +535,8 @@ void QPlaylistFileParser::start(const QUrl& request, const QString &mimeType)
d->m_stream = d->m_source.get();
connect(d->m_source.data(), SIGNAL(readyRead()), this, SLOT(handleData()));
connect(d->m_source.data(), SIGNAL(finished()), this, SLOT(handleData()));
- connect(d->m_source.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(handleError()));
+ connect(d->m_source.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this,
+ SLOT(handleError()));
if (url.isLocalFile())
d->handleData();
@@ -590,7 +567,8 @@ void QPlaylistFileParserPrivate::handleParserFinished()
Q_Q(QPlaylistFileParser);
const bool isParserValid = !m_currentParser.isNull();
if (!isParserValid && !m_aborted)
- emit q->error(QMediaPlaylist::FormatNotSupportedError, QMediaPlaylist::tr("Empty file provided"));
+ emit q->error(QMediaPlaylist::FormatNotSupportedError,
+ QMediaPlaylist::tr("Empty file provided"));
if (isParserValid && !m_aborted) {
m_currentParser.reset();
@@ -639,3 +617,5 @@ void QPlaylistFileParser::handleError()
}
QT_END_NAMESPACE
+
+#include "moc_qplaylistfileparser.cpp"
diff --git a/examples/multimedia/player/qplaylistfileparser.h b/examples/multimedia/player/qplaylistfileparser.h
new file mode 100644
index 000000000..0d83289c4
--- /dev/null
+++ b/examples/multimedia/player/qplaylistfileparser.h
@@ -0,0 +1,76 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PLAYLISTFILEPARSER_P_H
+#define PLAYLISTFILEPARSER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmediaplaylist.h"
+#include "qtmultimediaglobal.h"
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+class QUrl;
+class QNetworkRequest;
+
+class QPlaylistFileParserPrivate;
+
+class QPlaylistFileParser : public QObject
+{
+ Q_OBJECT
+public:
+ QPlaylistFileParser(QObject *parent = nullptr);
+ ~QPlaylistFileParser();
+
+ enum FileType {
+ UNKNOWN,
+ M3U,
+ M3U8, // UTF-8 version of M3U
+ PLS
+ };
+
+ void start(const QUrl &media, QIODevice *stream = nullptr, const QString &mimeType = QString());
+ void start(const QUrl &request, const QString &mimeType = QString());
+ void start(QIODevice *stream, const QString &mimeType = QString());
+ void abort();
+
+ QList<QUrl> playlist;
+
+signals:
+ void newItem(const QVariant &content);
+ void finished();
+ void error(QMediaPlaylist::Error err, const QString &errorMsg);
+
+private slots:
+ void handleData();
+ void handleError();
+
+private:
+ static FileType findByMimeType(const QString &mime);
+ static FileType findBySuffixType(const QString &suffix);
+ static FileType findByDataHeader(const char *data, quint32 size);
+ static FileType findPlaylistType(QIODevice *device, const QString &mime);
+ static FileType findPlaylistType(const QString &suffix, const QString &mime,
+ const char *data = nullptr, quint32 size = 0);
+
+ Q_DISABLE_COPY(QPlaylistFileParser)
+ Q_DECLARE_PRIVATE(QPlaylistFileParser)
+ QScopedPointer<QPlaylistFileParserPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // PLAYLISTFILEPARSER_P_H
diff --git a/examples/multimedia/player/videowidget.cpp b/examples/multimedia/player/videowidget.cpp
new file mode 100644
index 000000000..c09d90f37
--- /dev/null
+++ b/examples/multimedia/player/videowidget.cpp
@@ -0,0 +1,46 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "videowidget.h"
+
+#include <QKeyEvent>
+#include <QMouseEvent>
+
+VideoWidget::VideoWidget(QWidget *parent) : QVideoWidget(parent)
+{
+ setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
+
+ QPalette p = palette();
+ p.setColor(QPalette::Window, Qt::black);
+ setPalette(p);
+
+#ifndef Q_OS_ANDROID // QTBUG-95723
+ setAttribute(Qt::WA_OpaquePaintEvent);
+#endif
+}
+
+void VideoWidget::keyPressEvent(QKeyEvent *event)
+{
+ if ((event->key() == Qt::Key_Escape || event->key() == Qt::Key_Back) && isFullScreen()) {
+ setFullScreen(false);
+ event->accept();
+ } else if (event->key() == Qt::Key_Enter && event->modifiers() & Qt::Key_Alt) {
+ setFullScreen(!isFullScreen());
+ event->accept();
+ } else {
+ QVideoWidget::keyPressEvent(event);
+ }
+}
+
+void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ setFullScreen(!isFullScreen());
+ event->accept();
+}
+
+void VideoWidget::mousePressEvent(QMouseEvent *event)
+{
+ QVideoWidget::mousePressEvent(event);
+}
+
+#include "moc_videowidget.cpp"
diff --git a/examples/multimedia/player/videowidget.h b/examples/multimedia/player/videowidget.h
new file mode 100644
index 000000000..3505a3fb8
--- /dev/null
+++ b/examples/multimedia/player/videowidget.h
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef VIDEOWIDGET_H
+#define VIDEOWIDGET_H
+
+#include <QVideoWidget>
+
+class VideoWidget : public QVideoWidget
+{
+ Q_OBJECT
+
+public:
+ explicit VideoWidget(QWidget *parent = nullptr);
+
+protected:
+ void keyPressEvent(QKeyEvent *event) override;
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+};
+
+#endif // VIDEOWIDGET_H
diff --git a/examples/multimedia/screencapture/CMakeLists.txt b/examples/multimedia/screencapture/CMakeLists.txt
new file mode 100644
index 000000000..69ebf8d27
--- /dev/null
+++ b/examples/multimedia/screencapture/CMakeLists.txt
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(screencapture VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS MultimediaWidgets Widgets)
+
+set(PROJECT_SOURCES
+ screencapture.pro
+ main.cpp
+ screencapturepreview.cpp
+ screencapturepreview.h
+ screenlistmodel.h
+ screenlistmodel.cpp
+ windowlistmodel.h
+ windowlistmodel.cpp
+)
+
+qt_add_executable(screencapture
+ MANUAL_FINALIZATION
+ ${PROJECT_SOURCES}
+ )
+
+target_link_libraries(screencapture PRIVATE
+ Qt${QT_VERSION_MAJOR}::Core
+ Qt${QT_VERSION_MAJOR}::Gui
+ Qt${QT_VERSION_MAJOR}::Widgets
+ Qt${QT_VERSION_MAJOR}::Multimedia
+ Qt${QT_VERSION_MAJOR}::MultimediaWidgets)
+
+set_target_properties(screencapture PROPERTIES
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+install(TARGETS screencapture
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+qt_finalize_executable(screencapture)
diff --git a/examples/multimedia/screencapture/Info.plist.in b/examples/multimedia/screencapture/Info.plist.in
new file mode 100644
index 000000000..02c09f852
--- /dev/null
+++ b/examples/multimedia/screencapture/Info.plist.in
@@ -0,0 +1,41 @@
+<?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>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+</dict>
+</plist>
diff --git a/examples/multimedia/screencapture/doc/images/screencapture.jpg b/examples/multimedia/screencapture/doc/images/screencapture.jpg
new file mode 100644
index 000000000..fb61fcd73
--- /dev/null
+++ b/examples/multimedia/screencapture/doc/images/screencapture.jpg
Binary files differ
diff --git a/examples/multimedia/screencapture/doc/src/screencapture.qdoc b/examples/multimedia/screencapture/doc/src/screencapture.qdoc
new file mode 100644
index 000000000..405c59dbd
--- /dev/null
+++ b/examples/multimedia/screencapture/doc/src/screencapture.qdoc
@@ -0,0 +1,48 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example screencapture
+ \title Screen Capture Example
+ \ingroup multimedia_examples
+ \examplecategory {Multimedia}
+ \brief Capturing a screen or window.
+ \meta {tag} {widgets}
+
+ \e{Screen Capture} demonstrates how to capture a screen or window using
+ QScreenCapture and QWindowCapture. The example shows a list of screens and
+ windows and displays a live preview of the selected item using a
+ QMediaCaptureSession and a QVideoWidget. Capturing can be started and stopped
+ with a \l{QPushButton}{button}.
+
+ \image screencapture.jpg
+
+ \include examples-run.qdocinc
+
+ \section1 Application Structure
+
+ The example consists of three custom classes. The UI and all screen capture
+ functionality is implemented in the class ScreenCapturePreview. The classes
+ ScreenListModel and WindowListModel only serve as models behind the two
+ QListView widgets. The main function creates a ScreenCapturePreview object,
+ which in turn creates instances of QScreenCapture and QWindowCapture,
+ and a QMediaCaptureSession and QVideoWidget, in addition to all the UI widgets.
+
+ The screen and window models are populated with the return values of
+ \l QGuiApplication::screens() and \l QWindowCapture::capturableWindows(),
+ respectively.
+
+ When a list item is selected, it is connected to the QScreenCapture object
+ with \l{QScreenCapture::}{setScreen()}, or to the QWindowCapture object
+ with \l{QWindowCapture::}{setWindow()}. The capture object is connected to
+ the QMediaCaptureSession object with \l{QMediaCaptureSession::}{setScreenCapture()}
+ and \l{QMediaCaptureSession::}{setWindowCapture()}, respectively.
+ The capture session in turn is connected to the QVideoWidget object with
+ \l{QMediaCaptureSession::}{setVideoOutput()}. Thus, the capture output is
+ previewed in the video widget on the right hand side of the UI.
+
+ The start/stop button calls \l QScreenCapture::start() and \l QScreenCapture::stop(),
+ or \l QWindowCapture::start() and \l QWindowCapture::stop().
+
+ A QMessageBox pops up if the \l{QScreenCapture::}{errorOccurred} signal is emitted.
+*/
diff --git a/examples/multimedia/screencapture/main.cpp b/examples/multimedia/screencapture/main.cpp
new file mode 100644
index 000000000..6ed104f3e
--- /dev/null
+++ b/examples/multimedia/screencapture/main.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "screencapturepreview.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ ScreenCapturePreview screenCapturePreview;
+ screenCapturePreview.show();
+ return app.exec();
+}
diff --git a/examples/multimedia/screencapture/screencapture.pro b/examples/multimedia/screencapture/screencapture.pro
new file mode 100644
index 000000000..d18f556ee
--- /dev/null
+++ b/examples/multimedia/screencapture/screencapture.pro
@@ -0,0 +1,18 @@
+TEMPLATE = app
+TARGET = screencapture
+
+QT += multimedia multimediawidgets
+
+HEADERS = \
+ screencapturepreview.h \
+ screenlistmodel.h \
+ windowlistmodel.h
+
+SOURCES = \
+ main.cpp \
+ screencapturepreview.cpp \
+ screenlistmodel.cpp \
+ windowlistmodel.cpp
+
+target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/screencapture
+INSTALLS += target
diff --git a/examples/multimedia/screencapture/screencapturepreview.cpp b/examples/multimedia/screencapture/screencapturepreview.cpp
new file mode 100644
index 000000000..6eda008a8
--- /dev/null
+++ b/examples/multimedia/screencapture/screencapturepreview.cpp
@@ -0,0 +1,179 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "screencapturepreview.h"
+#include "screenlistmodel.h"
+#include "windowlistmodel.h"
+
+#include <QMediaCaptureSession>
+#include <QScreenCapture>
+#include <QVideoWidget>
+
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QListWidget>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QAction>
+
+ScreenCapturePreview::ScreenCapturePreview(QWidget *parent)
+ : QWidget(parent),
+ screenListView(new QListView(this)),
+ windowListView(new QListView(this)),
+ screenCapture(new QScreenCapture(this)),
+ windowCapture(new QWindowCapture(this)),
+ mediaCaptureSession(new QMediaCaptureSession(this)),
+ videoWidget(new QVideoWidget(this)),
+ gridLayout(new QGridLayout(this)),
+ startStopButton(new QPushButton(this)),
+ screenLabel(new QLabel(tr("Select screen to capture:"), this)),
+ windowLabel(new QLabel(tr("Select window to capture:"), this)),
+ videoWidgetLabel(new QLabel(tr("Capture output:"), this))
+{
+ // Get lists of screens and windows:
+ screenListModel = new ScreenListModel(this);
+ windowListModel = new WindowListModel(this);
+
+ // Setup QScreenCapture with initial source:
+
+ mediaCaptureSession->setScreenCapture(screenCapture);
+ mediaCaptureSession->setWindowCapture(windowCapture);
+ mediaCaptureSession->setVideoOutput(videoWidget);
+
+ // Setup UI:
+
+ screenListView->setModel(screenListModel);
+ windowListView->setModel(windowListModel);
+
+ auto updateAction = new QAction(tr("Update Windows List"), this);
+ connect(updateAction, &QAction::triggered, windowListModel, &WindowListModel::populate);
+ windowListView->addAction(updateAction);
+ windowListView->setContextMenuPolicy(Qt::ActionsContextMenu);
+
+ gridLayout->addWidget(screenLabel, 0, 0);
+ gridLayout->addWidget(screenListView, 1, 0);
+ gridLayout->addWidget(windowLabel, 2, 0);
+ gridLayout->addWidget(windowListView, 3, 0);
+ gridLayout->addWidget(startStopButton, 4, 0);
+ gridLayout->addWidget(videoWidgetLabel, 0, 1);
+ gridLayout->addWidget(videoWidget, 1, 1, 4, 1);
+
+ gridLayout->setColumnStretch(1, 1);
+ gridLayout->setRowStretch(1, 1);
+ gridLayout->setColumnMinimumWidth(0, 400);
+ gridLayout->setColumnMinimumWidth(1, 400);
+ gridLayout->setRowMinimumHeight(3, 1);
+
+ connect(screenListView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
+ &ScreenCapturePreview::onCurrentScreenSelectionChanged);
+ connect(windowListView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
+ &ScreenCapturePreview::onCurrentWindowSelectionChanged);
+ connect(startStopButton, &QPushButton::clicked, this,
+ &ScreenCapturePreview::onStartStopButtonClicked);
+ connect(screenCapture, &QScreenCapture::errorChanged, this,
+ &ScreenCapturePreview::onScreenCaptureErrorChanged, Qt::QueuedConnection);
+ connect(windowCapture, &QWindowCapture::errorChanged, this,
+ &ScreenCapturePreview::onWindowCaptureErrorChanged, Qt::QueuedConnection);
+
+ updateActive(SourceType::Screen, true);
+}
+
+ScreenCapturePreview::~ScreenCapturePreview() = default;
+
+void ScreenCapturePreview::onCurrentScreenSelectionChanged(QItemSelection selection)
+{
+ if (auto indexes = selection.indexes(); !indexes.empty()) {
+ screenCapture->setScreen(screenListModel->screen(indexes.front()));
+ updateActive(SourceType::Screen, isActive());
+
+ windowListView->clearSelection();
+ } else {
+ screenCapture->setScreen(nullptr);
+ }
+}
+
+void ScreenCapturePreview::onCurrentWindowSelectionChanged(QItemSelection selection)
+{
+ if (auto indexes = selection.indexes(); !indexes.empty()) {
+ auto window = windowListModel->window(indexes.front());
+ if (!window.isValid()) {
+ const auto questionResult = QMessageBox::question(
+ this, tr("Invalid window"),
+ tr("The window is no longer valid. Update the list of windows?"));
+ if (questionResult == QMessageBox::Yes) {
+ updateActive(SourceType::Window, false);
+
+ windowListView->clearSelection();
+ windowListModel->populate();
+ return;
+ }
+ }
+
+ windowCapture->setWindow(window);
+ updateActive(SourceType::Window, isActive());
+
+ screenListView->clearSelection();
+ } else {
+ windowCapture->setWindow({});
+ }
+}
+
+void ScreenCapturePreview::onWindowCaptureErrorChanged()
+{
+ if (windowCapture->error() == QWindowCapture::NoError)
+ return;
+
+ QMessageBox::warning(this, tr("QWindowCapture: Error occurred"), windowCapture->errorString());
+}
+
+void ScreenCapturePreview::onScreenCaptureErrorChanged()
+{
+ if (screenCapture->error() == QScreenCapture::NoError)
+ return;
+
+ QMessageBox::warning(this, tr("QScreenCapture: Error occurred"), screenCapture->errorString());
+}
+
+void ScreenCapturePreview::onStartStopButtonClicked()
+{
+ updateActive(sourceType, !isActive());
+}
+
+void ScreenCapturePreview::updateStartStopButtonText()
+{
+ switch (sourceType) {
+ case SourceType::Window:
+ startStopButton->setText(isActive() ? tr("Stop window capture")
+ : tr("Start window capture"));
+ break;
+ case SourceType::Screen:
+ startStopButton->setText(isActive() ? tr("Stop screen capture")
+ : tr("Start screen capture"));
+ break;
+ }
+}
+
+void ScreenCapturePreview::updateActive(SourceType sourceType, bool active)
+{
+ this->sourceType = sourceType;
+
+ screenCapture->setActive(active && sourceType == SourceType::Screen);
+ windowCapture->setActive(active && sourceType == SourceType::Window);
+
+ updateStartStopButtonText();
+}
+
+bool ScreenCapturePreview::isActive() const
+{
+ switch (sourceType) {
+ case SourceType::Window:
+ return windowCapture->isActive();
+ case SourceType::Screen:
+ return screenCapture->isActive();
+ default:
+ return false;
+ }
+}
+
+#include "moc_screencapturepreview.cpp"
diff --git a/examples/multimedia/screencapture/screencapturepreview.h b/examples/multimedia/screencapture/screencapturepreview.h
new file mode 100644
index 000000000..7f0e46491
--- /dev/null
+++ b/examples/multimedia/screencapture/screencapturepreview.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SCREENCAPTUREPREVIEW_H
+#define SCREENCAPTUREPREVIEW_H
+
+#include <QScreenCapture>
+#include <QWindowCapture>
+#include <QWidget>
+#include <QItemSelection>
+
+class ScreenListModel;
+class WindowListModel;
+
+QT_BEGIN_NAMESPACE
+class QListView;
+class QMediaCaptureSession;
+class QVideoWidget;
+class QGridLayout;
+class QHBoxLayout;
+class QLineEdit;
+class QPushButton;
+class QLabel;
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+class ScreenCapturePreview : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ScreenCapturePreview(QWidget *parent = nullptr);
+ ~ScreenCapturePreview() override;
+
+private slots:
+ void onCurrentScreenSelectionChanged(QItemSelection index);
+ void onCurrentWindowSelectionChanged(QItemSelection index);
+ void onWindowCaptureErrorChanged();
+ void onScreenCaptureErrorChanged();
+ void onStartStopButtonClicked();
+
+private:
+ enum class SourceType { Screen, Window };
+
+ void updateActive(SourceType sourceType, bool active);
+ void updateStartStopButtonText();
+ bool isActive() const;
+
+private:
+ ScreenListModel *screenListModel = nullptr;
+ WindowListModel *windowListModel = nullptr;
+ QListView *screenListView = nullptr;
+ QListView *windowListView = nullptr;
+ QScreenCapture *screenCapture = nullptr;
+ QWindowCapture *windowCapture = nullptr;
+ QMediaCaptureSession *mediaCaptureSession = nullptr;
+ QVideoWidget *videoWidget = nullptr;
+ QGridLayout *gridLayout = nullptr;
+ QPushButton *startStopButton = nullptr;
+ QLabel *screenLabel = nullptr;
+ QLabel *windowLabel = nullptr;
+ QLabel *videoWidgetLabel = nullptr;
+ SourceType sourceType = SourceType::Screen;
+};
+
+#endif // SCREENCAPTUREPREVIEW_H
diff --git a/examples/multimedia/screencapture/screenlistmodel.cpp b/examples/multimedia/screencapture/screenlistmodel.cpp
new file mode 100644
index 000000000..0b46560cc
--- /dev/null
+++ b/examples/multimedia/screencapture/screenlistmodel.cpp
@@ -0,0 +1,54 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "screenlistmodel.h"
+
+#include <QGuiApplication>
+#include <QScreen>
+
+#include <QTextStream>
+
+ScreenListModel::ScreenListModel(QObject *parent) :
+ QAbstractListModel(parent)
+{
+ auto *app = qApp;
+ connect(app, &QGuiApplication::screenAdded, this, &ScreenListModel::screensChanged);
+ connect(app, &QGuiApplication::screenRemoved, this, &ScreenListModel::screensChanged);
+ connect(app, &QGuiApplication::primaryScreenChanged, this, &ScreenListModel::screensChanged);
+}
+
+int ScreenListModel::rowCount(const QModelIndex &) const
+{
+ return QGuiApplication::screens().size();
+}
+
+QVariant ScreenListModel::data(const QModelIndex &index, int role) const
+{
+ const auto screenList = QGuiApplication::screens();
+ Q_ASSERT(index.isValid());
+ Q_ASSERT(index.row() <= screenList.size());
+
+ if (role == Qt::DisplayRole) {
+ auto *screen = screenList.at(index.row());
+ QString description;
+ QTextStream str(&description);
+ str << '"' << screen->name() << "\" " << screen->size().width() << 'x'
+ << screen->size().height() << ", " << screen->logicalDotsPerInch() << "DPI";
+ return description;
+ }
+
+ return {};
+}
+
+QScreen *ScreenListModel::screen(const QModelIndex &index) const
+{
+ return QGuiApplication::screens().value(index.row());
+}
+
+void ScreenListModel::screensChanged()
+{
+ beginResetModel();
+ endResetModel();
+}
+
+#include "moc_screenlistmodel.cpp"
diff --git a/examples/multimedia/screencapture/screenlistmodel.h b/examples/multimedia/screencapture/screenlistmodel.h
new file mode 100644
index 000000000..3bfa30b8f
--- /dev/null
+++ b/examples/multimedia/screencapture/screenlistmodel.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SCREENLISTMODEL_H
+#define SCREENLISTMODEL_H
+
+#include <QAbstractListModel>
+
+QT_BEGIN_NAMESPACE
+class QScreen;
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+class ScreenListModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit ScreenListModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QScreen *screen(const QModelIndex &index) const;
+
+private Q_SLOTS:
+ void screensChanged();
+};
+
+#endif // SCREENLISTMODEL_H
diff --git a/examples/multimedia/screencapture/windowlistmodel.cpp b/examples/multimedia/screencapture/windowlistmodel.cpp
new file mode 100644
index 000000000..45b8ceabd
--- /dev/null
+++ b/examples/multimedia/screencapture/windowlistmodel.cpp
@@ -0,0 +1,42 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "windowlistmodel.h"
+#include <QWindowCapture>
+
+WindowListModel::WindowListModel(QObject *parent)
+ : QAbstractListModel(parent), windowList(QWindowCapture::capturableWindows())
+{
+}
+
+int WindowListModel::rowCount(const QModelIndex &) const
+{
+ return windowList.size();
+}
+
+QVariant WindowListModel::data(const QModelIndex &index, int role) const
+{
+ Q_ASSERT(index.isValid());
+ Q_ASSERT(index.row() <= windowList.size());
+
+ if (role == Qt::DisplayRole) {
+ auto window = windowList.at(index.row());
+ return window.description();
+ }
+
+ return {};
+}
+
+QCapturableWindow WindowListModel::window(const QModelIndex &index) const
+{
+ return windowList.at(index.row());
+}
+
+void WindowListModel::populate()
+{
+ beginResetModel();
+ windowList = QWindowCapture::capturableWindows();
+ endResetModel();
+}
+
+#include "moc_windowlistmodel.cpp"
diff --git a/examples/multimedia/screencapture/windowlistmodel.h b/examples/multimedia/screencapture/windowlistmodel.h
new file mode 100644
index 000000000..caccbb0de
--- /dev/null
+++ b/examples/multimedia/screencapture/windowlistmodel.h
@@ -0,0 +1,31 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WINDOWLISTMODEL_H
+#define WINDOWLISTMODEL_H
+
+#include <QAbstractListModel>
+#include <QCapturableWindow>
+
+QT_USE_NAMESPACE
+
+class WindowListModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit WindowListModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ QCapturableWindow window(const QModelIndex &index) const;
+
+public Q_SLOTS:
+ void populate();
+
+private:
+ QList<QCapturableWindow> windowList;
+};
+
+#endif // WINDOWLISTMODEL_H
diff --git a/examples/multimedia/shared/shared.pri b/examples/multimedia/shared/shared.pri
index 61fbc79f1..16239013d 100644
--- a/examples/multimedia/shared/shared.pri
+++ b/examples/multimedia/shared/shared.pri
@@ -17,7 +17,7 @@ ios {
" <key>CFBundleExecutable</key>" \
" <string>$$TARGET</string>" \
" <key>CFBundleIdentifier</key>" \
- " <string>com.digia.$${LITERAL_DOLLAR}{PRODUCT_NAME:rfc1034identifier}</string>" \
+ " <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>" \
" <key>CFBundleDisplayName</key>" \
" <string>$$PRODUCT_NAME</string>" \
" <key>CFBundleName</key>" \
@@ -68,7 +68,7 @@ macos {
" <key>CFBundleExecutable</key>" \
" <string>$$TARGET</string>" \
" <key>CFBundleIdentifier</key>" \
- " <string>com.digia.$${LITERAL_DOLLAR}{PRODUCT_NAME:rfc1034identifier}</string>" \
+ " <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>" \
" <key>CFBundleDisplayName</key>" \
" <string>$$PRODUCT_NAME</string>" \
" <key>CFBundleName</key>" \
diff --git a/examples/multimedia/spatialaudio/doc/src/spatialaudio.qdoc b/examples/multimedia/spatialaudio/doc/src/spatialaudio.qdoc
deleted file mode 100644
index c6452a071..000000000
--- a/examples/multimedia/spatialaudio/doc/src/spatialaudio.qdoc
+++ /dev/null
@@ -1,51 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-/*!
-
-\example multimedia/spatialaudio
-\title Spatial Audio Example
-\ingroup multimedia_examples
-\ingroup spatialaudio_examples
-\meta {tag} {widgets}
-\brief Shows some of the capabilities of the spatial audio engine in Qt.
-
-The Spatial Audio Example demonstrates how you can use \l{Qt Multimedia} to
-place sound sources in 3D space and how positioning of the sound source and
-room properties affect the listening experience.
-
-The example lets you specify a sound file to be played back at a certain
-position in 3D space relative to the listener. Using sliders you can change
-the distance and the azimuth and elevation angles to the source. You can also
-change dimensions of a virtual room, the intensity of the room reflections
-and reverb.
-
-Various other properties can also be modified such as the dimensions of a virtual
-room and the intensity of the room reflections and reverb.
-
-\image spatialaudio-example.png
-
-*/
diff --git a/examples/multimedia/spatialaudio/main.cpp b/examples/multimedia/spatialaudio/main.cpp
deleted file mode 100644
index 4fcd5fe8f..000000000
--- a/examples/multimedia/spatialaudio/main.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <QtWidgets/QtWidgets>
-#include <QtMultimedia/QtMultimedia>
-
-class AudioWidget : public QWidget
-{
-public:
- AudioWidget()
- : QWidget()
- {
- setWindowTitle(tr("Spatial Audio test application"));
-
- setMinimumSize(400, 300);
- auto *grid = new QGridLayout(this);
- fileEdit = new QLineEdit;
- fileDialogButton = new QPushButton(tr("Open Filedialog"));
- grid->addWidget(fileEdit, 0, 0);
- grid->addWidget(fileDialogButton, 0, 1);
-
- azimuth = new QSlider(Qt::Horizontal);
- azimuth->setRange(-180, 180);
- grid->addWidget(new QLabel(tr("Azimuth (-180 - 180 degree):")), 1, 0);
- grid->addWidget(azimuth, 1, 1);
- elevation = new QSlider(Qt::Horizontal);
- elevation->setRange(-90, 90);
- grid->addWidget(new QLabel(tr("Elevation (-90 - 90 degree)")), 2, 0);
- grid->addWidget(elevation, 2, 1);
- distance = new QSlider(Qt::Horizontal);
- distance->setRange(0, 1000);
- distance->setValue(100);
- grid->addWidget(new QLabel(tr("Distance (0 - 10 meter):")), 3, 0);
- grid->addWidget(distance, 3, 1);
- occlusion = new QSlider(Qt::Horizontal);
- occlusion->setRange(0, 400);
- grid->addWidget(new QLabel(tr("Occlusion (0 - 4):")), 4, 0);
- grid->addWidget(occlusion, 4, 1);
-
- roomDimension = new QSlider(Qt::Horizontal);
- roomDimension->setRange(0, 10000);
- roomDimension->setValue(500);
- grid->addWidget(new QLabel(tr("Room dimension (0 - 100 meter):")), 5, 0);
- grid->addWidget(roomDimension, 5, 1);
-
- reverbGain = new QSlider(Qt::Horizontal);
- reverbGain->setRange(0, 500);
- reverbGain->setValue(100);
- grid->addWidget(new QLabel(tr("Reverb gain (0-5):")), 6, 0);
- grid->addWidget(reverbGain, 6, 1);
-
- reflectionGain = new QSlider(Qt::Horizontal);
- reflectionGain->setRange(0, 500);
- reflectionGain->setValue(100);
- grid->addWidget(new QLabel(tr("Reflection gain (0-5):")), 7, 0);
- grid->addWidget(reflectionGain, 7, 1);
-
- useHeadphone = new QCheckBox(tr("Use headphone spatialization"));
- grid->addWidget(useHeadphone, 8, 1, 1, 2);
-
- connect(fileEdit, &QLineEdit::textChanged, this, &AudioWidget::fileChanged);
- connect(fileDialogButton, &QPushButton::clicked, this, &AudioWidget::openFileDialog);
-
- connect(azimuth, &QSlider::valueChanged, this, &AudioWidget::updatePosition);
- connect(elevation, &QSlider::valueChanged, this, &AudioWidget::updatePosition);
- connect(distance, &QSlider::valueChanged, this, &AudioWidget::updatePosition);
- connect(occlusion, &QSlider::valueChanged, this, &AudioWidget::newOcclusion);
-
- connect(roomDimension, &QSlider::valueChanged, this, &AudioWidget::updateRoom);
- connect(reverbGain, &QSlider::valueChanged, this, &AudioWidget::updateRoom);
- connect(reflectionGain, &QSlider::valueChanged, this, &AudioWidget::updateRoom);
-
- connect(useHeadphone, &QCheckBox::stateChanged, this, &AudioWidget::useHeadphoneChanged);
-
- room = new QSpatialAudioRoom(&engine);
- room->setDimensions(QVector3D(5, 5, 5));
- room->setWallMaterial(QSpatialAudioRoom::BackWall, QSpatialAudioRoom::BrickBare);
- room->setWallMaterial(QSpatialAudioRoom::FrontWall, QSpatialAudioRoom::BrickBare);
- room->setWallMaterial(QSpatialAudioRoom::LeftWall, QSpatialAudioRoom::BrickBare);
- room->setWallMaterial(QSpatialAudioRoom::RightWall, QSpatialAudioRoom::BrickBare);
- room->setWallMaterial(QSpatialAudioRoom::Floor, QSpatialAudioRoom::Marble);
- room->setWallMaterial(QSpatialAudioRoom::Ceiling, QSpatialAudioRoom::WoodCeiling);
- room->setReverbGain(1);
- room->setReflectionGain(1);
-
- listener = new QSpatialAudioListener(&engine);
- listener->setPosition({});
- listener->setRotation({});
- engine.start();
-
- sound = new QSpatialAudioSoundSource(&engine);
- updatePosition();
- }
- void setFile(const QString &file) { fileEdit->setText(file); }
-private slots:
- void updatePosition()
- {
- float az = azimuth->value()/180.*M_PI;
- float el = elevation->value()/180.*M_PI;
- float d = distance->value();
-
- float x = d*sin(az)*cos(el);
- float y = d*cos(az)*cos(el);
- float z = d*sin(el);
- sound->setPosition({x, y, z});
- }
- void newOcclusion()
- {
- sound->setOcclusionIntensity(occlusion->value()/100.);
- }
- void useHeadphoneChanged(int state)
- {
- engine.setOutputMode(state ? QSpatialAudioEngine::Headphone : QSpatialAudioEngine::Normal);
- }
- void fileChanged(const QString &file)
- {
- sound->setSource(QUrl::fromLocalFile(file));
- sound->setSize(5);
- }
- void openFileDialog()
- {
- auto file = QFileDialog::getOpenFileName(this);
- fileEdit->setText(file);
- }
- void updateRoom()
- {
- float d = roomDimension->value();
- room->setDimensions(QVector3D(d, d, 400));
- room->setReflectionGain(float(reflectionGain->value())/100);
- room->setReverbGain(float(reverbGain->value())/100);
- }
-
- QLineEdit *fileEdit = nullptr;
- QPushButton *fileDialogButton = nullptr;
- QSlider *azimuth = nullptr;
- QSlider *elevation = nullptr;
- QSlider *distance = nullptr;
- QSlider *occlusion = nullptr;
- QSlider *roomDimension = nullptr;
- QSlider *reverbGain = nullptr;
- QSlider *reflectionGain = nullptr;
- QCheckBox *useHeadphone = nullptr;
-
- QSpatialAudioEngine engine;
- QSpatialAudioListener *listener = nullptr;
- QSpatialAudioSoundSource *sound = nullptr;
- QSpatialAudioRoom *room = nullptr;
-};
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
-
- AudioWidget *w = new AudioWidget;
- w->show();
- if (argc > 1) {
- auto file = QString::fromUtf8(argv[1]);
- w->setFile(file);
- }
-
- return app.exec();
-}
diff --git a/examples/multimedia/spectrum/CMakeLists.txt b/examples/multimedia/spectrum/CMakeLists.txt
index aea65446d..faabe9c27 100644
--- a/examples/multimedia/spectrum/CMakeLists.txt
+++ b/examples/multimedia/spectrum/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(spectrum LANGUAGES CXX)
@@ -33,6 +36,7 @@ qt_add_executable(spectrum
set_target_properties(spectrum PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
)
target_compile_definitions(spectrum PRIVATE
diff --git a/examples/multimedia/spectrum/Info.plist.in b/examples/multimedia/spectrum/Info.plist.in
new file mode 100644
index 000000000..d0b9ddd11
--- /dev/null
+++ b/examples/multimedia/spectrum/Info.plist.in
@@ -0,0 +1,43 @@
+<?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>Qt ${MACOSX_BUNDLE_BUNDLE_NAME} Example</string>
+ <key>CFBundleIdentifier</key>
+ <string>Qt ${MACOSX_BUNDLE_GUI_IDENTIFIER} Example</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>NSMicrophoneUsageDescription</key>
+ <string>Qt Multimedia Example</string>
+</dict>
+</plist>
diff --git a/examples/multimedia/spectrum/app.pro b/examples/multimedia/spectrum/app.pro
index 584206ab2..9688940bb 100644
--- a/examples/multimedia/spectrum/app.pro
+++ b/examples/multimedia/spectrum/app.pro
@@ -41,7 +41,8 @@ INCLUDEPATH += $${fftreal_dir}
RESOURCES = spectrum.qrc
LIBS += -L./$${spectrum_build_dir}
-LIBS += -lfftreal
+android:LIBS += -lfftreal_$$QT_ARCH
+else:LIBS += -lfftreal
target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/spectrum
INSTALLS += target
diff --git a/examples/multimedia/spectrum/doc/src/spectrum.qdoc b/examples/multimedia/spectrum/doc/src/spectrum.qdoc
index 6db616219..25e0760a4 100644
--- a/examples/multimedia/spectrum/doc/src/spectrum.qdoc
+++ b/examples/multimedia/spectrum/doc/src/spectrum.qdoc
@@ -1,34 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example multimedia/spectrum
+ \example spectrum
\title Spectrum Example
\ingroup multimedia_examples
+ \examplecategory {Multimedia}
\brief Analyzing a raw audio stream using the FFTReal library.
\e Spectrum demonstrates how the \l{Qt Multimedia} module can be used to
diff --git a/examples/multimedia/spectrum/engine.cpp b/examples/multimedia/spectrum/engine.cpp
index bbbe08a67..cb7aeadcb 100644
--- a/examples/multimedia/spectrum/engine.cpp
+++ b/examples/multimedia/spectrum/engine.cpp
@@ -1,61 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "engine.h"
#include "tonegenerator.h"
#include "utils.h"
-#include <math.h>
-
-#include <QAudioSource>
#include <QAudioSink>
+#include <QAudioSource>
#include <QCoreApplication>
#include <QDebug>
#include <QFile>
@@ -63,48 +14,51 @@
#include <QSet>
#include <QThread>
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
+
+#include <math.h>
+
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
-const qint64 BufferDurationUs = 10 * 1000000;
+const qint64 BufferDurationUs = 10 * 1000000;
// Size of the level calculation window in microseconds
-const int LevelWindowUs = 0.1 * 1000000;
+const int LevelWindowUs = 0.1 * 1000000;
//-----------------------------------------------------------------------------
// Constructor and destructor
//-----------------------------------------------------------------------------
Engine::Engine(QObject *parent)
- : QObject(parent)
- , m_mode(QAudioDevice::Input)
- , m_state(QAudio::StoppedState)
- , m_devices(new QMediaDevices(this))
- , m_generateTone(false)
- , m_file(nullptr)
- , m_analysisFile(nullptr)
- , m_availableAudioInputDevices(m_devices->audioInputs())
- , m_audioInputDevice(m_devices->defaultAudioInput())
- , m_audioInput(nullptr)
- , m_audioInputIODevice(nullptr)
- , m_recordPosition(0)
- , m_availableAudioOutputDevices(m_devices->audioOutputs())
- , m_audioOutputDevice(m_devices->defaultAudioOutput())
- , m_audioOutput(nullptr)
- , m_playPosition(0)
- , m_bufferPosition(0)
- , m_bufferLength(0)
- , m_dataLength(0)
- , m_levelBufferLength(0)
- , m_rmsLevel(0.0)
- , m_peakLevel(0.0)
- , m_spectrumBufferLength(0)
- , m_spectrumPosition(0)
- , m_count(0)
-{
- connect(&m_spectrumAnalyser, QOverload<const FrequencySpectrum&>::of(&SpectrumAnalyser::spectrumChanged),
- this, QOverload<const FrequencySpectrum&>::of(&Engine::spectrumChanged));
+ : QObject(parent),
+ m_mode(QAudioDevice::Input),
+ m_state(QAudio::StoppedState),
+ m_devices(new QMediaDevices(this)),
+ m_generateTone(false),
+ m_file(nullptr),
+ m_analysisFile(nullptr),
+ m_audioInput(nullptr),
+ m_audioInputIODevice(nullptr),
+ m_recordPosition(0),
+ m_audioOutput(nullptr),
+ m_playPosition(0),
+ m_bufferPosition(0),
+ m_bufferLength(0),
+ m_dataLength(0),
+ m_levelBufferLength(0),
+ m_rmsLevel(0.0),
+ m_peakLevel(0.0),
+ m_spectrumBufferLength(0),
+ m_spectrumPosition(0),
+ m_count(0)
+{
+ connect(&m_spectrumAnalyser,
+ QOverload<const FrequencySpectrum &>::of(&SpectrumAnalyser::spectrumChanged), this,
+ QOverload<const FrequencySpectrum &>::of(&Engine::spectrumChanged));
// This code might misinterpret things like "-something -category". But
// it's unlikely that that needs to be supported so we'll let it go.
@@ -114,6 +68,8 @@ Engine::Engine(QObject *parent)
break;
}
+ initAudioDevices();
+
initialize();
#ifdef DUMP_DATA
@@ -127,7 +83,6 @@ Engine::Engine(QObject *parent)
m_notifyTimer = new QTimer(this);
m_notifyTimer->setInterval(1000);
connect(m_notifyTimer, &QTimer::timeout, this, &Engine::audioNotify);
-
}
Engine::~Engine() = default;
@@ -143,7 +98,7 @@ bool Engine::loadFile(const QString &fileName)
Q_ASSERT(!m_generateTone);
Q_ASSERT(!m_file);
Q_ASSERT(!fileName.isEmpty());
- QIODevice* file = new QFile(fileName);
+ QIODevice *file = new QFile(fileName);
if (file->open(QIODevice::ReadOnly)) {
m_file = new QWaveDecoder(file, this);
if (m_file->open(QIODevice::ReadOnly)) {
@@ -151,10 +106,9 @@ bool Engine::loadFile(const QString &fileName)
result = initialize();
} else {
emit errorMessage(tr("Audio format not supported"),
- formatToString(m_file->audioFormat()));
+ formatToString(m_file->audioFormat()));
}
- }
- else
+ } else
emit errorMessage(tr("Could not open WAV decoder for file"), fileName);
} else {
emit errorMessage(tr("Could not open file"), fileName);
@@ -176,9 +130,8 @@ bool Engine::generateTone(const Tone &tone)
m_generateTone = true;
m_tone = tone;
ENGINE_DEBUG << "Engine::generateTone"
- << "startFreq" << m_tone.startFreq
- << "endFreq" << m_tone.endFreq
- << "amp" << m_tone.amplitude;
+ << "startFreq" << m_tone.startFreq << "endFreq" << m_tone.endFreq << "amp"
+ << m_tone.amplitude;
return initialize();
}
@@ -191,8 +144,7 @@ bool Engine::generateSweptTone(qreal amplitude)
m_tone.endFreq = 0;
m_tone.amplitude = amplitude;
ENGINE_DEBUG << "Engine::generateSweptTone"
- << "startFreq" << m_tone.startFreq
- << "amp" << m_tone.amplitude;
+ << "startFreq" << m_tone.startFreq << "amp" << m_tone.amplitude;
return initialize();
}
@@ -217,7 +169,6 @@ void Engine::setWindowFunction(WindowFunction type)
m_spectrumAnalyser.setWindowFunction(type);
}
-
//-----------------------------------------------------------------------------
// Public slots
//-----------------------------------------------------------------------------
@@ -225,26 +176,23 @@ void Engine::setWindowFunction(WindowFunction type)
void Engine::startRecording()
{
if (m_audioInput) {
- if (QAudioDevice::Input == m_mode &&
- QAudio::SuspendedState == m_state) {
+ if (QAudioDevice::Input == m_mode && QAudio::SuspendedState == m_state) {
m_audioInput->resume();
} else {
m_spectrumAnalyser.cancelCalculation();
- spectrumChanged(0, 0, FrequencySpectrum());
+ emit spectrumChanged(0, 0, FrequencySpectrum());
m_buffer.fill(0);
setRecordPosition(0, true);
stopPlayback();
m_mode = QAudioDevice::Input;
- connect(m_audioInput, &QAudioSource::stateChanged,
- this, &Engine::audioStateChanged);
+ connect(m_audioInput, &QAudioSource::stateChanged, this, &Engine::audioStateChanged);
m_count = 0;
m_dataLength = 0;
emit dataLengthChanged(0);
m_audioInputIODevice = m_audioInput->start();
- connect(m_audioInputIODevice, &QIODevice::readyRead,
- this, &Engine::audioDataReady);
+ connect(m_audioInputIODevice, &QIODevice::readyRead, this, &Engine::audioDataReady);
}
m_notifyTimer->start();
}
@@ -256,8 +204,7 @@ void Engine::startPlayback()
initialize();
if (m_audioOutput) {
- if (QAudioDevice::Output == m_mode &&
- QAudio::SuspendedState == m_state) {
+ if (QAudioDevice::Output == m_mode && QAudio::SuspendedState == m_state) {
#ifdef Q_OS_WIN
// The Windows backend seems to internally go back into ActiveState
// while still returning SuspendedState, so to ensure that it doesn't
@@ -267,12 +214,11 @@ void Engine::startPlayback()
m_audioOutput->resume();
} else {
m_spectrumAnalyser.cancelCalculation();
- spectrumChanged(0, 0, FrequencySpectrum());
+ emit spectrumChanged(0, 0, FrequencySpectrum());
setPlayPosition(0, true);
stopRecording();
m_mode = QAudioDevice::Output;
- connect(m_audioOutput, &QAudioSink::stateChanged,
- this, &Engine::audioStateChanged);
+ connect(m_audioOutput, &QAudioSink::stateChanged, this, &Engine::audioStateChanged);
m_count = 0;
if (m_file) {
@@ -293,8 +239,7 @@ void Engine::startPlayback()
void Engine::suspend()
{
- if (QAudio::ActiveState == m_state ||
- QAudio::IdleState == m_state) {
+ if (QAudio::ActiveState == m_state || QAudio::IdleState == m_state) {
switch (m_mode) {
case QAudioDevice::Input:
m_audioInput->suspend();
@@ -325,75 +270,99 @@ void Engine::setAudioOutputDevice(const QAudioDevice &device)
}
}
-
//-----------------------------------------------------------------------------
// Private slots
//-----------------------------------------------------------------------------
+void Engine::initAudioDevices()
+{
+#if QT_CONFIG(permissions)
+ QMicrophonePermission microphonePermission;
+ switch (qApp->checkPermission(microphonePermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(microphonePermission, this, &Engine::initAudioDevices);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Microphone permission is not granted!");
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+#endif
+ m_availableAudioInputDevices = m_devices->audioInputs();
+ m_audioInputDevice = m_devices->defaultAudioInput();
+ m_availableAudioOutputDevices = m_devices->audioOutputs();
+ m_audioOutputDevice = m_devices->defaultAudioOutput();
+}
+
void Engine::audioNotify()
{
switch (m_mode) {
case QAudioDevice::Input: {
- const qint64 recordPosition = qMin(m_bufferLength, m_format.bytesForDuration(m_audioInput->processedUSecs()));
- setRecordPosition(recordPosition);
- const qint64 levelPosition = m_dataLength - m_levelBufferLength;
- if (levelPosition >= 0)
- calculateLevel(levelPosition, m_levelBufferLength);
- if (m_dataLength >= m_spectrumBufferLength) {
- const qint64 spectrumPosition = m_dataLength - m_spectrumBufferLength;
- calculateSpectrum(spectrumPosition);
- }
- emit bufferChanged(0, m_dataLength, m_buffer);
+ const qint64 recordPosition =
+ qMin(m_bufferLength, m_format.bytesForDuration(m_audioInput->processedUSecs()));
+ setRecordPosition(recordPosition);
+ const qint64 levelPosition = m_dataLength - m_levelBufferLength;
+ if (levelPosition >= 0)
+ calculateLevel(levelPosition, m_levelBufferLength);
+ if (m_dataLength >= m_spectrumBufferLength) {
+ const qint64 spectrumPosition = m_dataLength - m_spectrumBufferLength;
+ calculateSpectrum(spectrumPosition);
}
- break;
+ emit bufferChanged(0, m_dataLength, m_buffer);
+ } break;
case QAudioDevice::Output: {
- const qint64 playPosition = m_format.bytesForDuration(m_audioOutput->processedUSecs());
- setPlayPosition(qMin(bufferLength(), playPosition));
- const qint64 levelPosition = playPosition - m_levelBufferLength;
- const qint64 spectrumPosition = playPosition - m_spectrumBufferLength;
- if (m_file) {
- if (levelPosition > m_bufferPosition ||
- spectrumPosition > m_bufferPosition ||
- qMax(m_levelBufferLength, m_spectrumBufferLength) > m_dataLength) {
- m_bufferPosition = 0;
- m_dataLength = 0;
- // Data needs to be read into m_buffer in order to be analysed
- const qint64 readPos = qMax(qint64(0), qMin(levelPosition, spectrumPosition));
- const qint64 readEnd = qMin(m_analysisFile->getDevice()->size(), qMax(levelPosition + m_levelBufferLength, spectrumPosition + m_spectrumBufferLength));
- const qint64 readLen = readEnd - readPos + m_format.bytesForDuration(WaveformWindowDuration);
- qDebug() << "Engine::audioNotify [1]"
- << "analysisFileSize" << m_analysisFile->getDevice()->size()
- << "readPos" << readPos
- << "readLen" << readLen;
- if (m_analysisFile->seek(readPos + m_analysisFile->headerLength())) {
- m_buffer.resize(readLen);
- m_bufferPosition = readPos;
- m_dataLength = m_analysisFile->read(m_buffer.data(), readLen);
- qDebug() << "Engine::audioNotify [2]" << "bufferPosition" << m_bufferPosition << "dataLength" << m_dataLength;
- } else {
- qDebug() << "Engine::audioNotify [2]" << "file seek error";
- }
- emit bufferChanged(m_bufferPosition, m_dataLength, m_buffer);
+ const qint64 playPosition = m_format.bytesForDuration(m_audioOutput->processedUSecs());
+ setPlayPosition(qMin(bufferLength(), playPosition));
+ const qint64 levelPosition = playPosition - m_levelBufferLength;
+ const qint64 spectrumPosition = playPosition - m_spectrumBufferLength;
+ if (m_file) {
+ if (levelPosition > m_bufferPosition || spectrumPosition > m_bufferPosition
+ || qMax(m_levelBufferLength, m_spectrumBufferLength) > m_dataLength) {
+ m_bufferPosition = 0;
+ m_dataLength = 0;
+ // Data needs to be read into m_buffer in order to be analysed
+ const qint64 readPos = qMax(qint64(0), qMin(levelPosition, spectrumPosition));
+ const qint64 readEnd = qMin(m_analysisFile->getDevice()->size(),
+ qMax(levelPosition + m_levelBufferLength,
+ spectrumPosition + m_spectrumBufferLength));
+ const qint64 readLen =
+ readEnd - readPos + m_format.bytesForDuration(WaveformWindowDuration);
+ qDebug() << "Engine::audioNotify [1]"
+ << "analysisFileSize" << m_analysisFile->getDevice()->size() << "readPos"
+ << readPos << "readLen" << readLen;
+ if (m_analysisFile->seek(readPos + m_analysisFile->headerLength())) {
+ m_buffer.resize(readLen);
+ m_bufferPosition = readPos;
+ m_dataLength = m_analysisFile->read(m_buffer.data(), readLen);
+ qDebug() << "Engine::audioNotify [2]"
+ << "bufferPosition" << m_bufferPosition << "dataLength"
+ << m_dataLength;
+ } else {
+ qDebug() << "Engine::audioNotify [2]"
+ << "file seek error";
}
- } else {
- if (playPosition >= m_dataLength)
- stopPlayback();
+ emit bufferChanged(m_bufferPosition, m_dataLength, m_buffer);
}
- if (levelPosition >= 0 && levelPosition + m_levelBufferLength < m_bufferPosition + m_dataLength)
- calculateLevel(levelPosition, m_levelBufferLength);
- if (spectrumPosition >= 0 && spectrumPosition + m_spectrumBufferLength < m_bufferPosition + m_dataLength)
- calculateSpectrum(spectrumPosition);
+ } else {
+ if (playPosition >= m_dataLength)
+ stopPlayback();
}
+ if (levelPosition >= 0
+ && levelPosition + m_levelBufferLength < m_bufferPosition + m_dataLength)
+ calculateLevel(levelPosition, m_levelBufferLength);
+ if (spectrumPosition >= 0
+ && spectrumPosition + m_spectrumBufferLength < m_bufferPosition + m_dataLength)
+ calculateSpectrum(spectrumPosition);
+ } break;
+ default:
break;
- default:
- break;
}
}
void Engine::audioStateChanged(QAudio::State state)
{
- ENGINE_DEBUG << "Engine::audioStateChanged from" << m_state
- << "to" << state;
+ ENGINE_DEBUG << "Engine::audioStateChanged from" << m_state << "to" << state;
if (QAudio::IdleState == state && m_file && m_file->pos() == m_file->getDevice()->size()) {
stopPlayback();
@@ -428,9 +397,8 @@ void Engine::audioDataReady()
const qint64 bytesSpace = m_buffer.size() - m_dataLength;
const qint64 bytesToRead = qMin(bytesReady, bytesSpace);
- const qint64 bytesRead = m_audioInputIODevice->read(
- m_buffer.data() + m_dataLength,
- bytesToRead);
+ const qint64 bytesRead =
+ m_audioInputIODevice->read(m_buffer.data() + m_dataLength, bytesToRead);
if (bytesRead) {
m_dataLength += bytesRead;
@@ -443,11 +411,11 @@ void Engine::audioDataReady()
void Engine::spectrumChanged(const FrequencySpectrum &spectrum)
{
- ENGINE_DEBUG << "Engine::spectrumChanged" << "pos" << m_spectrumPosition;
+ ENGINE_DEBUG << "Engine::spectrumChanged"
+ << "pos" << m_spectrumPosition;
emit spectrumChanged(m_spectrumPosition, m_spectrumBufferLength, spectrum);
}
-
//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------
@@ -526,17 +494,19 @@ bool Engine::initialize()
}
} else {
if (m_file)
- emit errorMessage(tr("Audio format not supported"),
- formatToString(m_format));
+ emit errorMessage(tr("Audio format not supported"), formatToString(m_format));
else if (m_generateTone)
emit errorMessage(tr("No suitable format found"), "");
else
emit errorMessage(tr("No common input / output format found"), "");
}
- ENGINE_DEBUG << "Engine::initialize" << "m_bufferLength" << m_bufferLength;
- ENGINE_DEBUG << "Engine::initialize" << "m_dataLength" << m_dataLength;
- ENGINE_DEBUG << "Engine::initialize" << "format" << m_format;
+ ENGINE_DEBUG << "Engine::initialize"
+ << "m_bufferLength" << m_bufferLength;
+ ENGINE_DEBUG << "Engine::initialize"
+ << "m_dataLength" << m_dataLength;
+ ENGINE_DEBUG << "Engine::initialize"
+ << "format" << m_format;
return result;
}
@@ -655,7 +625,7 @@ void Engine::calculateLevel(qint64 position, qint64 length)
const char *ptr = m_buffer.constData() + position - m_bufferPosition;
const char *const end = ptr + length;
while (ptr < end) {
- const qint16 value = *reinterpret_cast<const qint16*>(ptr);
+ const qint16 value = *reinterpret_cast<const qint16 *>(ptr);
const qreal fracValue = pcmToReal(value);
peakLevel = qMax(peakLevel, fracValue);
sum += fracValue * fracValue;
@@ -668,8 +638,9 @@ void Engine::calculateLevel(qint64 position, qint64 length)
rmsLevel = qMin(qreal(1.0), rmsLevel);
setLevel(rmsLevel, peakLevel, numSamples);
- ENGINE_DEBUG << "Engine::calculateLevel" << "pos" << position << "len" << length
- << "rms" << rmsLevel << "peak" << peakLevel;
+ ENGINE_DEBUG << "Engine::calculateLevel"
+ << "pos" << position << "len" << length << "rms" << rmsLevel << "peak"
+ << peakLevel;
#endif
}
@@ -683,13 +654,13 @@ void Engine::calculateSpectrum(qint64 position)
// QThread::currentThread is marked 'for internal use only', but
// we're only using it for debug output here, so it's probably OK :)
- ENGINE_DEBUG << "Engine::calculateSpectrum" << QThread::currentThread()
- << "count" << m_count << "pos" << position << "len" << m_spectrumBufferLength
+ ENGINE_DEBUG << "Engine::calculateSpectrum" << QThread::currentThread() << "count" << m_count
+ << "pos" << position << "len" << m_spectrumBufferLength
<< "spectrumAnalyser.isReady" << m_spectrumAnalyser.isReady();
if (m_spectrumAnalyser.isReady()) {
- m_spectrumBuffer = QByteArray::fromRawData(m_buffer.constData() + position - m_bufferPosition,
- m_spectrumBufferLength);
+ m_spectrumBuffer = QByteArray::fromRawData(
+ m_buffer.constData() + position - m_bufferPosition, m_spectrumBufferLength);
m_spectrumPosition = position;
m_spectrumAnalyser.calculate(m_spectrumBuffer, m_format);
}
@@ -728,11 +699,11 @@ void Engine::emitError(QAudio::Error error)
break;
case QAudio::UnderrunError:
errorString = tr("UnderrunError: Audio data is not being fed"
- "to the audio device at a fast enough rate.");
+ "to the audio device at a fast enough rate.");
break;
case QAudio::FatalError:
errorString = tr("FatalError: A non-recoverable error has occurred,"
- "the audio device is not usable at this time.");
+ "the audio device is not usable at this time.");
break;
}
@@ -762,9 +733,9 @@ void Engine::dumpData()
QFile txtFile(txtFileName);
txtFile.open(QFile::WriteOnly | QFile::Text);
QTextStream stream(&txtFile);
- const qint16 *ptr = reinterpret_cast<const qint16*>(m_buffer.constData());
+ const qint16 *ptr = reinterpret_cast<const qint16 *>(m_buffer.constData());
const int numSamples = m_dataLength / (2 * m_format.channels());
- for (int i=0; i<numSamples; ++i) {
+ for (int i = 0; i < numSamples; ++i) {
stream << i << "\t" << *ptr << "\n";
ptr += m_format.channels();
}
@@ -775,3 +746,5 @@ void Engine::dumpData()
pcmFile.write(m_buffer.constData(), m_dataLength);
}
#endif // DUMP_AUDIO
+
+#include "moc_engine.cpp"
diff --git a/examples/multimedia/spectrum/engine.h b/examples/multimedia/spectrum/engine.h
index 7f7028bab..9e8ba51d0 100644
--- a/examples/multimedia/spectrum/engine.h
+++ b/examples/multimedia/spectrum/engine.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef ENGINE_H
#define ENGINE_H
@@ -60,17 +13,17 @@
#include <QByteArray>
#include <QDir>
#include <QList>
-#include <QObject>
#include <QMediaDevices>
-#include <QWaveDecoder>
+#include <QObject>
#include <QTimer>
+#include <QWaveDecoder>
#ifdef DUMP_CAPTURED_AUDIO
-#define DUMP_DATA
+# define DUMP_DATA
#endif
#ifdef DUMP_SPECTRUM
-#define DUMP_DATA
+# define DUMP_DATA
#endif
class FrequencySpectrum;
@@ -90,14 +43,18 @@ class Engine : public QObject
Q_OBJECT
public:
- explicit Engine(QObject *parent = 0);
+ explicit Engine(QObject *parent = nullptr);
~Engine();
const QList<QAudioDevice> &availableAudioInputDevices() const
- { return m_availableAudioInputDevices; }
+ {
+ return m_availableAudioInputDevices;
+ }
const QList<QAudioDevice> &availableAudioOutputDevices() const
- { return m_availableAudioOutputDevices; }
+ {
+ return m_availableAudioOutputDevices;
+ }
QAudioDevice::Mode mode() const { return m_mode; }
QAudio::State state() const { return m_state; }
@@ -106,7 +63,7 @@ public:
* \return Current audio format
* \note May be QAudioFormat() if engine is not initialized
*/
- const QAudioFormat& format() const { return m_format; }
+ const QAudioFormat &format() const { return m_format; }
/**
* Stop any ongoing recording or playback, and reset to ground state.
@@ -246,6 +203,7 @@ signals:
void bufferChanged(qint64 position, qint64 length, const QByteArray &buffer);
private slots:
+ void initAudioDevices();
void audioNotify();
void audioStateChanged(QAudio::State state);
void audioDataReady();
@@ -269,7 +227,10 @@ private:
#ifdef DUMP_DATA
void createOutputDir();
- QString outputPath() const { return m_outputDir.path(); }
+ QString outputPath() const
+ {
+ return m_outputDir.path();
+ }
#endif
#ifdef DUMP_CAPTURED_AUDIO
@@ -277,53 +238,52 @@ private:
#endif
private:
- QAudioDevice::Mode m_mode;
- QAudio::State m_state;
- QMediaDevices *m_devices;
+ QAudioDevice::Mode m_mode;
+ QAudio::State m_state;
+ QMediaDevices *m_devices;
- bool m_generateTone;
- SweptTone m_tone;
+ bool m_generateTone;
+ SweptTone m_tone;
- QWaveDecoder* m_file;
+ QWaveDecoder *m_file;
// We need a second file handle via which to read data into m_buffer
// for analysis
- QWaveDecoder* m_analysisFile;
+ QWaveDecoder *m_analysisFile;
- QAudioFormat m_format;
+ QAudioFormat m_format;
- const QList<QAudioDevice> m_availableAudioInputDevices;
- QAudioDevice m_audioInputDevice;
- QAudioSource* m_audioInput;
- QIODevice* m_audioInputIODevice;
- qint64 m_recordPosition;
+ QList<QAudioDevice> m_availableAudioInputDevices;
+ QAudioDevice m_audioInputDevice;
+ QAudioSource *m_audioInput;
+ QIODevice *m_audioInputIODevice;
+ qint64 m_recordPosition;
- const QList<QAudioDevice> m_availableAudioOutputDevices;
- QAudioDevice m_audioOutputDevice;
- QAudioSink* m_audioOutput;
- qint64 m_playPosition;
- QBuffer m_audioOutputIODevice;
+ QList<QAudioDevice> m_availableAudioOutputDevices;
+ QAudioDevice m_audioOutputDevice;
+ QAudioSink *m_audioOutput;
+ qint64 m_playPosition;
+ QBuffer m_audioOutputIODevice;
- QByteArray m_buffer;
- qint64 m_bufferPosition;
- qint64 m_bufferLength;
- qint64 m_dataLength;
+ QByteArray m_buffer;
+ qint64 m_bufferPosition;
+ qint64 m_bufferLength;
+ qint64 m_dataLength;
- int m_levelBufferLength;
- qreal m_rmsLevel;
- qreal m_peakLevel;
+ int m_levelBufferLength;
+ qreal m_rmsLevel;
+ qreal m_peakLevel;
- int m_spectrumBufferLength;
- QByteArray m_spectrumBuffer;
- SpectrumAnalyser m_spectrumAnalyser;
- qint64 m_spectrumPosition;
+ int m_spectrumBufferLength;
+ QByteArray m_spectrumBuffer;
+ SpectrumAnalyser m_spectrumAnalyser;
+ qint64 m_spectrumPosition;
- int m_count;
+ int m_count;
QTimer *m_notifyTimer = nullptr;
#ifdef DUMP_DATA
- QDir m_outputDir;
+ QDir m_outputDir;
#endif
-
};
#endif // ENGINE_H
diff --git a/examples/multimedia/spectrum/frequencyspectrum.cpp b/examples/multimedia/spectrum/frequencyspectrum.cpp
index abec65d23..f271a04aa 100644
--- a/examples/multimedia/spectrum/frequencyspectrum.cpp
+++ b/examples/multimedia/spectrum/frequencyspectrum.cpp
@@ -1,65 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "frequencyspectrum.h"
-FrequencySpectrum::FrequencySpectrum(int numPoints)
- : m_elements(numPoints)
-{
-
-}
+FrequencySpectrum::FrequencySpectrum(int numPoints) : m_elements(numPoints) { }
void FrequencySpectrum::reset()
{
iterator i = begin();
- for ( ; i != end(); ++i)
+ for (; i != end(); ++i)
*i = Element();
}
diff --git a/examples/multimedia/spectrum/frequencyspectrum.h b/examples/multimedia/spectrum/frequencyspectrum.h
index 4b13cbb99..9f3771571 100644
--- a/examples/multimedia/spectrum/frequencyspectrum.h
+++ b/examples/multimedia/spectrum/frequencyspectrum.h
@@ -1,70 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef FREQUENCYSPECTRUM_H
#define FREQUENCYSPECTRUM_H
-#include <QtCore/QList>
+#include <QList>
/**
* Represents a frequency spectrum as a series of elements, each of which
* consists of a frequency, an amplitude and a phase.
*/
-class FrequencySpectrum {
+class FrequencySpectrum
+{
public:
FrequencySpectrum(int numPoints = 0);
- struct Element {
- Element()
- : frequency(0.0), amplitude(0.0), phase(0.0), clipped(false)
- { }
+ struct Element
+ {
+ Element() : frequency(0.0), amplitude(0.0), phase(0.0), clipped(false) { }
/**
* Frequency in Hertz
@@ -93,8 +46,8 @@ public:
void reset();
int count() const;
- Element& operator[](int index);
- const Element& operator[](int index) const;
+ Element &operator[](int index);
+ const Element &operator[](int index) const;
iterator begin();
iterator end();
const_iterator begin() const;
diff --git a/examples/multimedia/spectrum/levelmeter.cpp b/examples/multimedia/spectrum/levelmeter.cpp
index f3bd619cd..2c889c34d 100644
--- a/examples/multimedia/spectrum/levelmeter.cpp
+++ b/examples/multimedia/spectrum/levelmeter.cpp
@@ -1,84 +1,34 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "levelmeter.h"
-#include <math.h>
-
+#include <QDebug>
#include <QPainter>
#include <QTimer>
-#include <QDebug>
+#include <math.h>
// Constants
const int RedrawInterval = 100; // ms
const qreal PeakDecayRate = 0.001;
const int PeakHoldLevelDuration = 2000; // ms
-
LevelMeter::LevelMeter(QWidget *parent)
- : QWidget(parent)
- , m_rmsLevel(0.0)
- , m_peakLevel(0.0)
- , m_decayedPeakLevel(0.0)
- , m_peakDecayRate(PeakDecayRate)
- , m_peakHoldLevel(0.0)
- , m_redrawTimer(new QTimer(this))
- , m_rmsColor(Qt::red)
- , m_peakColor(255, 200, 200, 255)
+ : QWidget(parent),
+ m_rmsLevel(0.0),
+ m_peakLevel(0.0),
+ m_decayedPeakLevel(0.0),
+ m_peakDecayRate(PeakDecayRate),
+ m_peakHoldLevel(0.0),
+ m_redrawTimer(new QTimer(this)),
+ m_rmsColor(Qt::red),
+ m_peakColor(255, 200, 200, 255)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
setMinimumWidth(30);
- connect(m_redrawTimer, &QTimer::timeout,
- this, &LevelMeter::redrawTimerExpired);
+ connect(m_redrawTimer, &QTimer::timeout, this, &LevelMeter::redrawTimerExpired);
m_redrawTimer->start(RedrawInterval);
}
@@ -94,7 +44,8 @@ void LevelMeter::reset()
void LevelMeter::levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
{
// Smooth the RMS signal
- const qreal smooth = pow(qreal(0.9), static_cast<qreal>(numSamples) / 256); // TODO: remove this magic number
+ const qreal smooth =
+ pow(qreal(0.9), static_cast<qreal>(numSamples) / 256); // TODO: remove this magic number
m_rmsLevel = (m_rmsLevel * smooth) + (rmsLevel * (1.0 - smooth));
if (peakLevel > m_decayedPeakLevel) {
@@ -148,3 +99,5 @@ void LevelMeter::paintEvent(QPaintEvent *event)
bar.setTop(rect().top() + (1.0 - m_rmsLevel) * rect().height());
painter.fillRect(bar, m_rmsColor);
}
+
+#include "moc_levelmeter.cpp"
diff --git a/examples/multimedia/spectrum/levelmeter.h b/examples/multimedia/spectrum/levelmeter.h
index 987f90e8f..e131bc904 100644
--- a/examples/multimedia/spectrum/levelmeter.h
+++ b/examples/multimedia/spectrum/levelmeter.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef LEVELMETER_H
#define LEVELMETER_H
@@ -64,7 +17,7 @@ class LevelMeter : public QWidget
Q_OBJECT
public:
- explicit LevelMeter(QWidget *parent = 0);
+ explicit LevelMeter(QWidget *parent = nullptr);
~LevelMeter();
void paintEvent(QPaintEvent *event) override;
@@ -122,7 +75,6 @@ private:
QColor m_rmsColor;
QColor m_peakColor;
-
};
#endif // LEVELMETER_H
diff --git a/examples/multimedia/spectrum/main.cpp b/examples/multimedia/spectrum/main.cpp
index 7eeb94949..31913b2f2 100644
--- a/examples/multimedia/spectrum/main.cpp
+++ b/examples/multimedia/spectrum/main.cpp
@@ -1,54 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwidget.h"
+
#include <QApplication>
int main(int argc, char *argv[])
diff --git a/examples/multimedia/spectrum/mainwidget.cpp b/examples/multimedia/spectrum/mainwidget.cpp
index b72b01bac..214d317d9 100644
--- a/examples/multimedia/spectrum/mainwidget.cpp
+++ b/examples/multimedia/spectrum/mainwidget.cpp
@@ -1,102 +1,55 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include "mainwidget.h"
#include "engine.h"
#include "levelmeter.h"
-#include "mainwidget.h"
-#include "waveform.h"
#include "progressbar.h"
#include "settingsdialog.h"
#include "spectrograph.h"
#include "tonegeneratordialog.h"
#include "utils.h"
+#include "waveform.h"
+#include <QFileDialog>
+#include <QHBoxLayout>
#include <QLabel>
+#include <QMenu>
+#include <QMessageBox>
#include <QPushButton>
-#include <QHBoxLayout>
-#include <QVBoxLayout>
#include <QStyle>
-#include <QMenu>
-#include <QFileDialog>
#include <QTimerEvent>
-#include <QMessageBox>
+#include <QVBoxLayout>
const int NullTimerId = -1;
MainWidget::MainWidget(QWidget *parent)
- : QWidget(parent)
- , m_mode(NoMode)
- , m_engine(new Engine(this))
+ : QWidget(parent),
+ m_mode(NoMode),
+ m_engine(new Engine(this))
#ifndef DISABLE_WAVEFORM
- , m_waveform(new Waveform(this))
+ ,
+ m_waveform(new Waveform(this))
#endif
- , m_progressBar(new ProgressBar(this))
- , m_spectrograph(new Spectrograph(this))
- , m_levelMeter(new LevelMeter(this))
- , m_modeButton(new QPushButton(this))
- , m_recordButton(new QPushButton(this))
- , m_pauseButton(new QPushButton(this))
- , m_playButton(new QPushButton(this))
- , m_settingsButton(new QPushButton(this))
- , m_infoMessage(new QLabel(tr("Select a mode to begin"), this))
- , m_infoMessageTimerId(NullTimerId)
- , m_settingsDialog(new SettingsDialog(
- m_engine->availableAudioInputDevices(),
- m_engine->availableAudioOutputDevices(),
- this))
- , m_toneGeneratorDialog(new ToneGeneratorDialog(this))
- , m_modeMenu(new QMenu(this))
- , m_loadFileAction(nullptr)
- , m_generateToneAction(nullptr)
- , m_recordAction(nullptr)
- , m_errorOccurred(false)
+ ,
+ m_progressBar(new ProgressBar(this)),
+ m_spectrograph(new Spectrograph(this)),
+ m_levelMeter(new LevelMeter(this)),
+ m_modeButton(new QPushButton(this)),
+ m_recordButton(new QPushButton(this)),
+ m_pauseButton(new QPushButton(this)),
+ m_playButton(new QPushButton(this)),
+ m_settingsButton(new QPushButton(this)),
+ m_infoMessage(new QLabel(tr("Select a mode to begin"), this)),
+ m_infoMessageTimerId(NullTimerId),
+ m_settingsDialog(new SettingsDialog(m_engine->availableAudioInputDevices(),
+ m_engine->availableAudioOutputDevices(), this)),
+ m_toneGeneratorDialog(new ToneGeneratorDialog(this)),
+ m_modeMenu(new QMenu(this)),
+ m_loadFileAction(nullptr),
+ m_generateToneAction(nullptr),
+ m_recordAction(nullptr),
+ m_errorOccurred(false)
{
m_spectrograph->setParams(SpectrumNumBands, SpectrumLowFreq, SpectrumHighFreq);
@@ -106,7 +59,6 @@ MainWidget::MainWidget(QWidget *parent)
MainWidget::~MainWidget() = default;
-
//-----------------------------------------------------------------------------
// Public slots
//-----------------------------------------------------------------------------
@@ -117,8 +69,7 @@ void MainWidget::stateChanged(QAudioDevice::Mode mode, QAudio::State state)
updateButtonStates();
- if (QAudio::ActiveState != state &&
- QAudio::SuspendedState != state) {
+ if (QAudio::ActiveState != state && QAudio::SuspendedState != state) {
m_levelMeter->reset();
m_spectrograph->reset();
}
@@ -126,18 +77,16 @@ void MainWidget::stateChanged(QAudioDevice::Mode mode, QAudio::State state)
void MainWidget::formatChanged(const QAudioFormat &format)
{
- infoMessage(formatToString(format), NullMessageTimeout);
+ infoMessage(formatToString(format), NullMessageTimeout);
#ifndef DISABLE_WAVEFORM
if (QAudioFormat() != format) {
- m_waveform->initialize(format, WaveformTileLength,
- WaveformWindowDuration);
+ m_waveform->initialize(format, WaveformTileLength, WaveformWindowDuration);
}
#endif
}
-void MainWidget::spectrumChanged(qint64 position, qint64 length,
- const FrequencySpectrum &spectrum)
+void MainWidget::spectrumChanged(qint64 position, qint64 length, const FrequencySpectrum &spectrum)
{
m_progressBar->windowChanged(position, length);
m_spectrograph->spectrumChanged(spectrum);
@@ -186,7 +135,6 @@ void MainWidget::bufferLengthChanged(qint64 length)
m_progressBar->bufferLengthChanged(length);
}
-
//-----------------------------------------------------------------------------
// Private slots
//-----------------------------------------------------------------------------
@@ -195,7 +143,8 @@ void MainWidget::showFileDialog()
{
m_errorOccurred = false;
const QString dir;
- const QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open WAV file"), dir, "*.wav");
+ const QStringList fileNames =
+ QFileDialog::getOpenFileNames(this, tr("Open WAV file"), dir, "*.wav");
if (fileNames.count()) {
reset();
setMode(LoadFileMode);
@@ -247,7 +196,6 @@ void MainWidget::initializeRecord()
updateButtonStates();
}
-
//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------
@@ -272,9 +220,9 @@ void MainWidget::createUi()
m_waveform->setLayout(waveformLayout.release());
windowLayout->addWidget(m_waveform);
#else
-#ifndef DISABLE_WAVEFORM
+# ifndef DISABLE_WAVEFORM
windowLayout->addWidget(m_waveform);
-#endif // DISABLE_WAVEFORM
+# endif // DISABLE_WAVEFORM
windowLayout->addWidget(m_progressBar);
#endif // SUPERIMPOSE_PROGRESS_ON_WAVEFORM
@@ -338,62 +286,49 @@ void MainWidget::createUi()
void MainWidget::connectUi()
{
- connect(m_recordButton, &QPushButton::clicked,
- m_engine, &Engine::startRecording);
+ connect(m_recordButton, &QPushButton::clicked, m_engine, &Engine::startRecording);
- connect(m_pauseButton, &QPushButton::clicked,
- m_engine, &Engine::suspend);
+ connect(m_pauseButton, &QPushButton::clicked, m_engine, &Engine::suspend);
- connect(m_playButton, &QPushButton::clicked,
- m_engine, &Engine::startPlayback);
+ connect(m_playButton, &QPushButton::clicked, m_engine, &Engine::startPlayback);
- connect(m_settingsButton, &QPushButton::clicked,
- this, &MainWidget::showSettingsDialog);
+ connect(m_settingsButton, &QPushButton::clicked, this, &MainWidget::showSettingsDialog);
- connect(m_engine, &Engine::stateChanged,
- this, &MainWidget::stateChanged);
+ connect(m_engine, &Engine::stateChanged, this, &MainWidget::stateChanged);
- connect(m_engine, &Engine::formatChanged,
- this, &MainWidget::formatChanged);
+ connect(m_engine, &Engine::formatChanged, this, &MainWidget::formatChanged);
m_progressBar->bufferLengthChanged(m_engine->bufferLength());
- connect(m_engine, &Engine::bufferLengthChanged,
- this, &MainWidget::bufferLengthChanged);
+ connect(m_engine, &Engine::bufferLengthChanged, this, &MainWidget::bufferLengthChanged);
- connect(m_engine, &Engine::dataLengthChanged,
- this, &MainWidget::updateButtonStates);
+ connect(m_engine, &Engine::dataLengthChanged, this, &MainWidget::updateButtonStates);
- connect(m_engine, &Engine::recordPositionChanged,
- m_progressBar, &ProgressBar::recordPositionChanged);
+ connect(m_engine, &Engine::recordPositionChanged, m_progressBar,
+ &ProgressBar::recordPositionChanged);
- connect(m_engine, &Engine::playPositionChanged,
- m_progressBar, &ProgressBar::playPositionChanged);
+ connect(m_engine, &Engine::playPositionChanged, m_progressBar,
+ &ProgressBar::playPositionChanged);
- connect(m_engine, &Engine::recordPositionChanged,
- this, &MainWidget::audioPositionChanged);
+ connect(m_engine, &Engine::recordPositionChanged, this, &MainWidget::audioPositionChanged);
- connect(m_engine, &Engine::playPositionChanged,
- this, &MainWidget::audioPositionChanged);
+ connect(m_engine, &Engine::playPositionChanged, this, &MainWidget::audioPositionChanged);
- connect(m_engine, &Engine::levelChanged,
- m_levelMeter, &LevelMeter::levelChanged);
+ connect(m_engine, &Engine::levelChanged, m_levelMeter, &LevelMeter::levelChanged);
- connect(m_engine, QOverload<qint64, qint64, const FrequencySpectrum&>::of(&Engine::spectrumChanged),
- this, QOverload<qint64, qint64, const FrequencySpectrum&>::of(&MainWidget::spectrumChanged));
+ connect(m_engine,
+ QOverload<qint64, qint64, const FrequencySpectrum &>::of(&Engine::spectrumChanged),
+ this,
+ QOverload<qint64, qint64, const FrequencySpectrum &>::of(&MainWidget::spectrumChanged));
- connect(m_engine, &Engine::infoMessage,
- this, &MainWidget::infoMessage);
+ connect(m_engine, &Engine::infoMessage, this, &MainWidget::infoMessage);
- connect(m_engine, &Engine::errorMessage,
- this, &MainWidget::errorMessage);
+ connect(m_engine, &Engine::errorMessage, this, &MainWidget::errorMessage);
- connect(m_spectrograph, &Spectrograph::infoMessage,
- this, &MainWidget::infoMessage);
+ connect(m_spectrograph, &Spectrograph::infoMessage, this, &MainWidget::infoMessage);
#ifndef DISABLE_WAVEFORM
- connect(m_engine, &Engine::bufferChanged,
- m_waveform, &Waveform::bufferChanged);
+ connect(m_engine, &Engine::bufferChanged, m_waveform, &Waveform::bufferChanged);
#endif
}
@@ -416,20 +351,20 @@ void MainWidget::createMenus()
void MainWidget::updateButtonStates()
{
- const bool recordEnabled = ((QAudioDevice::Output == m_engine->mode() ||
- (QAudio::ActiveState != m_engine->state() &&
- QAudio::IdleState != m_engine->state())) &&
- RecordMode == m_mode);
+ const bool recordEnabled = ((QAudioDevice::Output == m_engine->mode()
+ || (QAudio::ActiveState != m_engine->state()
+ && QAudio::IdleState != m_engine->state()))
+ && RecordMode == m_mode);
m_recordButton->setEnabled(m_errorOccurred ? false : recordEnabled);
- const bool pauseEnabled = (QAudio::ActiveState == m_engine->state() ||
- QAudio::IdleState == m_engine->state());
+ const bool pauseEnabled =
+ (QAudio::ActiveState == m_engine->state() || QAudio::IdleState == m_engine->state());
m_pauseButton->setEnabled(m_errorOccurred ? false : pauseEnabled);
const bool playEnabled = (/*m_engine->dataLength() &&*/
- (QAudioDevice::Output != m_engine->mode() ||
- (QAudio::ActiveState != m_engine->state() &&
- QAudio::IdleState != m_engine->state())));
+ (QAudioDevice::Output != m_engine->mode()
+ || (QAudio::ActiveState != m_engine->state()
+ && QAudio::IdleState != m_engine->state())));
m_playButton->setEnabled(m_errorOccurred ? false : playEnabled);
}
@@ -461,3 +396,5 @@ void MainWidget::updateModeMenu()
m_generateToneAction->setChecked(GenerateToneMode == m_mode);
m_recordAction->setChecked(RecordMode == m_mode);
}
+
+#include "moc_mainwidget.cpp"
diff --git a/examples/multimedia/spectrum/mainwidget.h b/examples/multimedia/spectrum/mainwidget.h
index 0eae08af0..789e312f5 100644
--- a/examples/multimedia/spectrum/mainwidget.h
+++ b/examples/multimedia/spectrum/mainwidget.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
@@ -81,7 +34,7 @@ class MainWidget : public QWidget
Q_OBJECT
public:
- explicit MainWidget(QWidget *parent = 0);
+ explicit MainWidget(QWidget *parent = nullptr);
~MainWidget();
// QObject
@@ -90,8 +43,7 @@ public:
public slots:
void stateChanged(QAudioDevice::Mode mode, QAudio::State state);
void formatChanged(const QAudioFormat &format);
- void spectrumChanged(qint64 position, qint64 length,
- const FrequencySpectrum &spectrum);
+ void spectrumChanged(qint64 position, qint64 length, const FrequencySpectrum &spectrum);
void infoMessage(const QString &message, int timeoutMs);
void errorMessage(const QString &heading, const QString &detail);
void audioPositionChanged(qint64 position);
@@ -111,49 +63,43 @@ private:
void connectUi();
void reset();
- enum Mode {
- NoMode,
- RecordMode,
- GenerateToneMode,
- LoadFileMode
- };
+ enum Mode { NoMode, RecordMode, GenerateToneMode, LoadFileMode };
void setMode(Mode mode);
private:
- Mode m_mode;
+ Mode m_mode;
- Engine* m_engine;
+ Engine *m_engine;
#ifndef DISABLE_WAVEFORM
- Waveform* m_waveform;
+ Waveform *m_waveform;
#endif
- ProgressBar* m_progressBar;
- Spectrograph* m_spectrograph;
- LevelMeter* m_levelMeter;
-
- QPushButton* m_modeButton;
- QPushButton* m_recordButton;
- QIcon m_recordIcon;
- QPushButton* m_pauseButton;
- QIcon m_pauseIcon;
- QPushButton* m_playButton;
- QIcon m_playIcon;
- QPushButton* m_settingsButton;
- QIcon m_settingsIcon;
-
- QLabel* m_infoMessage;
- int m_infoMessageTimerId;
-
- SettingsDialog* m_settingsDialog;
- ToneGeneratorDialog* m_toneGeneratorDialog;
-
- QMenu* m_modeMenu;
- QAction* m_loadFileAction;
- QAction* m_generateToneAction;
- QAction* m_recordAction;
- bool m_errorOccurred;
-
+ ProgressBar *m_progressBar;
+ Spectrograph *m_spectrograph;
+ LevelMeter *m_levelMeter;
+
+ QPushButton *m_modeButton;
+ QPushButton *m_recordButton;
+ QIcon m_recordIcon;
+ QPushButton *m_pauseButton;
+ QIcon m_pauseIcon;
+ QPushButton *m_playButton;
+ QIcon m_playIcon;
+ QPushButton *m_settingsButton;
+ QIcon m_settingsIcon;
+
+ QLabel *m_infoMessage;
+ int m_infoMessageTimerId;
+
+ SettingsDialog *m_settingsDialog;
+ ToneGeneratorDialog *m_toneGeneratorDialog;
+
+ QMenu *m_modeMenu;
+ QAction *m_loadFileAction;
+ QAction *m_generateToneAction;
+ QAction *m_recordAction;
+ bool m_errorOccurred;
};
#endif // MAINWIDGET_H
diff --git a/examples/multimedia/spectrum/progressbar.cpp b/examples/multimedia/spectrum/progressbar.cpp
index 4ac34b866..1806097e2 100644
--- a/examples/multimedia/spectrum/progressbar.cpp
+++ b/examples/multimedia/spectrum/progressbar.cpp
@@ -1,64 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "progressbar.h"
-#include "spectrum.h"
#include <QPainter>
ProgressBar::ProgressBar(QWidget *parent)
- : QWidget(parent)
- , m_bufferLength(0)
- , m_recordPosition(0)
- , m_playPosition(0)
- , m_windowPosition(0)
- , m_windowLength(0)
+ : QWidget(parent),
+ m_bufferLength(0),
+ m_recordPosition(0),
+ m_playPosition(0),
+ m_windowPosition(0),
+ m_windowLength(0)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setMinimumHeight(30);
@@ -145,3 +97,5 @@ void ProgressBar::windowChanged(qint64 position, qint64 length)
m_windowLength = length;
repaint();
}
+
+#include "moc_progressbar.cpp"
diff --git a/examples/multimedia/spectrum/progressbar.h b/examples/multimedia/spectrum/progressbar.h
index 601900efb..8b117c7e6 100644
--- a/examples/multimedia/spectrum/progressbar.h
+++ b/examples/multimedia/spectrum/progressbar.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef PROGRESSBAR_H
#define PROGRESSBAR_H
@@ -62,7 +15,7 @@ class ProgressBar : public QWidget
Q_OBJECT
public:
- explicit ProgressBar(QWidget *parent = 0);
+ explicit ProgressBar(QWidget *parent = nullptr);
~ProgressBar();
void reset();
diff --git a/examples/multimedia/spectrum/settingsdialog.cpp b/examples/multimedia/spectrum/settingsdialog.cpp
index 3ec9c6956..bdd52e123 100644
--- a/examples/multimedia/spectrum/settingsdialog.cpp
+++ b/examples/multimedia/spectrum/settingsdialog.cpp
@@ -1,54 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "settingsdialog.h"
+
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
@@ -58,26 +12,22 @@
#include <QSpinBox>
#include <QVBoxLayout>
-SettingsDialog::SettingsDialog(
- const QList<QAudioDevice> &availableInputDevices,
- const QList<QAudioDevice> &availableOutputDevices,
- QWidget *parent)
- : QDialog(parent)
- , m_windowFunction(DefaultWindowFunction)
- , m_inputDeviceComboBox(new QComboBox(this))
- , m_outputDeviceComboBox(new QComboBox(this))
- , m_windowFunctionComboBox(new QComboBox(this))
+SettingsDialog::SettingsDialog(const QList<QAudioDevice> &availableInputDevices,
+ const QList<QAudioDevice> &availableOutputDevices, QWidget *parent)
+ : QDialog(parent),
+ m_windowFunction(DefaultWindowFunction),
+ m_inputDeviceComboBox(new QComboBox(this)),
+ m_outputDeviceComboBox(new QComboBox(this)),
+ m_windowFunctionComboBox(new QComboBox(this))
{
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
// Populate combo boxes
for (const QAudioDevice &device : availableInputDevices)
- m_inputDeviceComboBox->addItem(device.description(),
- QVariant::fromValue(device));
+ m_inputDeviceComboBox->addItem(device.description(), QVariant::fromValue(device));
for (const QAudioDevice &device : availableOutputDevices)
- m_outputDeviceComboBox->addItem(device.description(),
- QVariant::fromValue(device));
+ m_outputDeviceComboBox->addItem(device.description(), QVariant::fromValue(device));
m_windowFunctionComboBox->addItem(tr("None"), QVariant::fromValue(int(NoWindow)));
m_windowFunctionComboBox->addItem("Hann", QVariant::fromValue(int(HannWindow)));
@@ -110,12 +60,12 @@ SettingsDialog::SettingsDialog(
dialogLayout->addLayout(windowFunctionLayout.release());
// Connect
- connect(m_inputDeviceComboBox, QOverload<int>::of(&QComboBox::activated),
- this, &SettingsDialog::inputDeviceChanged);
- connect(m_outputDeviceComboBox, QOverload<int>::of(&QComboBox::activated),
- this, &SettingsDialog::outputDeviceChanged);
- connect(m_windowFunctionComboBox, QOverload<int>::of(&QComboBox::activated),
- this, &SettingsDialog::windowFunctionChanged);
+ connect(m_inputDeviceComboBox, QOverload<int>::of(&QComboBox::activated), this,
+ &SettingsDialog::inputDeviceChanged);
+ connect(m_outputDeviceComboBox, QOverload<int>::of(&QComboBox::activated), this,
+ &SettingsDialog::outputDeviceChanged);
+ connect(m_windowFunctionComboBox, QOverload<int>::of(&QComboBox::activated), this,
+ &SettingsDialog::windowFunctionChanged);
// Add standard buttons to layout
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
@@ -123,10 +73,10 @@ SettingsDialog::SettingsDialog(
dialogLayout->addWidget(buttonBox);
// Connect standard buttons
- connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
- this, &SettingsDialog::accept);
- connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
- this, &SettingsDialog::reject);
+ connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this,
+ &SettingsDialog::accept);
+ connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this,
+ &SettingsDialog::reject);
setLayout(dialogLayout);
}
@@ -135,8 +85,8 @@ SettingsDialog::~SettingsDialog() = default;
void SettingsDialog::windowFunctionChanged(int index)
{
- m_windowFunction = static_cast<WindowFunction>(
- m_windowFunctionComboBox->itemData(index).value<int>());
+ m_windowFunction =
+ static_cast<WindowFunction>(m_windowFunctionComboBox->itemData(index).value<int>());
}
void SettingsDialog::inputDeviceChanged(int index)
@@ -149,3 +99,4 @@ void SettingsDialog::outputDeviceChanged(int index)
m_outputDevice = m_outputDeviceComboBox->itemData(index).value<QAudioDevice>();
}
+#include "moc_settingsdialog.cpp"
diff --git a/examples/multimedia/spectrum/settingsdialog.h b/examples/multimedia/spectrum/settingsdialog.h
index e6563b6ce..ac877d975 100644
--- a/examples/multimedia/spectrum/settingsdialog.h
+++ b/examples/multimedia/spectrum/settingsdialog.h
@@ -1,59 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include "spectrum.h"
-#include <QDialog>
+
#include <QAudioDevice>
+#include <QDialog>
QT_BEGIN_NAMESPACE
class QComboBox;
@@ -73,8 +27,7 @@ class SettingsDialog : public QDialog
public:
SettingsDialog(const QList<QAudioDevice> &availableInputDevices,
- const QList<QAudioDevice> &availableOutputDevices,
- QWidget *parent = 0);
+ const QList<QAudioDevice> &availableOutputDevices, QWidget *parent = nullptr);
~SettingsDialog();
WindowFunction windowFunction() const { return m_windowFunction; }
@@ -87,7 +40,7 @@ private slots:
void outputDeviceChanged(int index);
private:
- WindowFunction m_windowFunction;
+ WindowFunction m_windowFunction;
QAudioDevice m_inputDevice;
QAudioDevice m_outputDevice;
diff --git a/examples/multimedia/spectrum/spectrograph.cpp b/examples/multimedia/spectrum/spectrograph.cpp
index 2ab1127d7..96274b50b 100644
--- a/examples/multimedia/spectrum/spectrograph.cpp
+++ b/examples/multimedia/spectrum/spectrograph.cpp
@@ -1,54 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "spectrograph.h"
+
#include <QDebug>
#include <QMouseEvent>
#include <QPainter>
@@ -59,17 +13,16 @@ const int NullIndex = -1;
const int BarSelectionInterval = 2000;
Spectrograph::Spectrograph(QWidget *parent)
- : QWidget(parent)
- , m_barSelected(NullIndex)
- , m_timerId(NullTimerId)
- , m_lowFreq(0.0)
- , m_highFreq(0.0)
+ : QWidget(parent),
+ m_barSelected(NullIndex),
+ m_timerId(NullTimerId),
+ m_lowFreq(0.0),
+ m_highFreq(0.0)
{
setMinimumHeight(100);
}
-Spectrograph::~Spectrograph()
-= default;
+Spectrograph::~Spectrograph() = default;
void Spectrograph::setParams(int numBars, qreal lowFreq, qreal highFreq)
{
@@ -132,8 +85,8 @@ void Spectrograph::paintEvent(QPaintEvent *event)
if (numBars) {
const int numHorizontalSections = numBars;
QLine line(rect().topLeft(), rect().bottomLeft());
- for (int i=1; i<numHorizontalSections; ++i) {
- line.translate(rect().width()/numHorizontalSections, 0);
+ for (int i = 1; i < numHorizontalSections; ++i) {
+ line.translate(rect().width() / numHorizontalSections, 0);
painter.drawLine(line);
}
}
@@ -141,8 +94,8 @@ void Spectrograph::paintEvent(QPaintEvent *event)
// Draw horizontal lines
const int numVerticalSections = 10;
QLine line(rect().topLeft(), rect().topRight());
- for (int i=1; i<numVerticalSections; ++i) {
- line.translate(0, rect().height()/numVerticalSections);
+ for (int i = 1; i < numVerticalSections; ++i) {
+ line.translate(0, rect().height() / numVerticalSections);
painter.drawLine(line);
}
@@ -161,7 +114,7 @@ void Spectrograph::paintEvent(QPaintEvent *event)
const int leftPaddingWidth = (paddingWidth + gapWidth) / 2;
const int barHeight = rect().height() - 2 * gapWidth;
- for (int i=0; i<numBars; ++i) {
+ for (int i = 0; i < numBars; ++i) {
const qreal value = m_bars[i].value;
Q_ASSERT(value >= 0.0 && value <= 1.0);
QRect bar = rect();
@@ -203,7 +156,7 @@ int Spectrograph::barIndex(qreal frequency) const
Q_ASSERT(frequency >= m_lowFreq && frequency < m_highFreq);
const qreal bandWidth = (m_highFreq - m_lowFreq) / m_bars.count();
const int index = (frequency - m_lowFreq) / bandWidth;
- if (index <0 || index >= m_bars.count())
+ if (index < 0 || index >= m_bars.count())
Q_ASSERT(false);
return index;
}
@@ -212,7 +165,7 @@ QPair<qreal, qreal> Spectrograph::barRange(int index) const
{
Q_ASSERT(index >= 0 && index < m_bars.count());
const qreal bandWidth = (m_highFreq - m_lowFreq) / m_bars.count();
- return QPair<qreal, qreal>(index * bandWidth, (index+1) * bandWidth);
+ return QPair<qreal, qreal>(index * bandWidth, (index + 1) * bandWidth);
}
void Spectrograph::updateBars()
@@ -220,7 +173,7 @@ void Spectrograph::updateBars()
m_bars.fill(Bar());
FrequencySpectrum::const_iterator i = m_spectrum.begin();
const FrequencySpectrum::const_iterator end = m_spectrum.end();
- for ( ; i != end; ++i) {
+ for (; i != end; ++i) {
const FrequencySpectrum::Element e = *i;
if (e.frequency >= m_lowFreq && e.frequency < m_highFreq) {
Bar &bar = m_bars[barIndex(e.frequency)];
@@ -231,11 +184,11 @@ void Spectrograph::updateBars()
update();
}
-void Spectrograph::selectBar(int index) {
+void Spectrograph::selectBar(int index)
+{
const QPair<qreal, qreal> frequencyRange = barRange(index);
- const QString message = QString("%1 - %2 Hz")
- .arg(frequencyRange.first)
- .arg(frequencyRange.second);
+ const QString message =
+ QStringLiteral("%1 - %2 Hz").arg(frequencyRange.first).arg(frequencyRange.second);
emit infoMessage(message, BarSelectionInterval);
if (NullTimerId != m_timerId)
@@ -246,4 +199,4 @@ void Spectrograph::selectBar(int index) {
update();
}
-
+#include "moc_spectrograph.cpp"
diff --git a/examples/multimedia/spectrum/spectrograph.h b/examples/multimedia/spectrum/spectrograph.h
index 0e7b11717..1344d3257 100644
--- a/examples/multimedia/spectrum/spectrograph.h
+++ b/examples/multimedia/spectrum/spectrograph.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SPECTROGRAPH_H
#define SPECTROGRAPH_H
@@ -64,7 +17,7 @@ class Spectrograph : public QWidget
Q_OBJECT
public:
- explicit Spectrograph(QWidget *parent = 0);
+ explicit Spectrograph(QWidget *parent = nullptr);
~Spectrograph();
void setParams(int numBars, qreal lowFreq, qreal highFreq);
@@ -91,18 +44,19 @@ private:
void selectBar(int index);
private:
- struct Bar {
+ struct Bar
+ {
Bar() : value(0.0), clipped(false) { }
- qreal value;
- bool clipped;
+ qreal value;
+ bool clipped;
};
- QList<Bar> m_bars;
- int m_barSelected;
- int m_timerId;
- qreal m_lowFreq;
- qreal m_highFreq;
- FrequencySpectrum m_spectrum;
+ QList<Bar> m_bars;
+ int m_barSelected;
+ int m_timerId;
+ qreal m_lowFreq;
+ qreal m_highFreq;
+ FrequencySpectrum m_spectrum;
};
#endif // SPECTROGRAPH_H
diff --git a/examples/multimedia/spectrum/spectrum.h b/examples/multimedia/spectrum/spectrum.h
index 687ae1e33..e416e82bd 100644
--- a/examples/multimedia/spectrum/spectrum.h
+++ b/examples/multimedia/spectrum/spectrum.h
@@ -1,75 +1,29 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SPECTRUM_H
#define SPECTRUM_H
-#include <qglobal.h>
-#include "utils.h"
#include "fftreal_wrapper.h" // For FFTLengthPowerOfTwo
+#include "utils.h"
+
+#include <QtGlobal>
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
// Number of audio samples used to calculate the frequency spectrum
-const int SpectrumLengthSamples = PowerOfTwo<FFTLengthPowerOfTwo>::Result;
+const int SpectrumLengthSamples = PowerOfTwo<FFTLengthPowerOfTwo>::Result;
// Number of bands in the frequency spectrum
-const int SpectrumNumBands = 10;
+const int SpectrumNumBands = 10;
// Lower bound of first band in the spectrum
-const qreal SpectrumLowFreq = 0.0; // Hz
+const qreal SpectrumLowFreq = 0.0; // Hz
// Upper band of last band in the spectrum
-const qreal SpectrumHighFreq = 1000.0; // Hz
+const qreal SpectrumHighFreq = 1000.0; // Hz
// Waveform window size in microseconds
const qint64 WaveformWindowDuration = 500 * 1000;
@@ -79,63 +33,59 @@ const qint64 WaveformWindowDuration = 500 * 1000;
// available until some time after QAudio*::start() has been called, and we
// need this value in order to initialize the waveform display.
// We therefore just choose a sensible value.
-const int WaveformTileLength = 4096;
+const int WaveformTileLength = 4096;
// Fudge factor used to calculate the spectrum bar heights
const qreal SpectrumAnalyserMultiplier = 0.15;
// Disable message timeout
-const int NullMessageTimeout = -1;
-
+const int NullMessageTimeout = -1;
//-----------------------------------------------------------------------------
// Types and data structures
//-----------------------------------------------------------------------------
-enum WindowFunction {
- NoWindow,
- HannWindow
-};
+enum WindowFunction { NoWindow, HannWindow };
Q_DECLARE_METATYPE(WindowFunction)
const WindowFunction DefaultWindowFunction = HannWindow;
struct Tone
{
- Tone(qreal freq = 0.0, qreal amp = 0.0)
- : frequency(freq), amplitude(amp)
- { }
+ Tone(qreal freq = 0.0, qreal amp = 0.0) : frequency(freq), amplitude(amp) { }
// Start and end frequencies for swept tone generation
- qreal frequency;
+ qreal frequency;
// Amplitude in range [0.0, 1.0]
- qreal amplitude;
+ qreal amplitude;
};
struct SweptTone
{
SweptTone(qreal start = 0.0, qreal end = 0.0, qreal amp = 0.0)
- : startFreq(start), endFreq(end), amplitude(amp)
- { Q_ASSERT(end >= start); }
+ : startFreq(start), endFreq(end), amplitude(amp)
+ {
+ Q_ASSERT(end >= start);
+ }
SweptTone(const Tone &tone)
- : startFreq(tone.frequency), endFreq(tone.frequency), amplitude(tone.amplitude)
- { }
+ : startFreq(tone.frequency), endFreq(tone.frequency), amplitude(tone.amplitude)
+ {
+ }
// Start and end frequencies for swept tone generation
- qreal startFreq;
- qreal endFreq;
+ qreal startFreq;
+ qreal endFreq;
// Amplitude in range [0.0, 1.0]
- qreal amplitude;
+ qreal amplitude;
};
// Handle some dependencies between macros defined in the .pro file
#ifdef DISABLE_WAVEFORM
-#undef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
+# undef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
#endif
#endif // SPECTRUM_H
-
diff --git a/examples/multimedia/spectrum/spectrumanalyser.cpp b/examples/multimedia/spectrum/spectrumanalyser.cpp
index 4a7b06809..c309c6a73 100644
--- a/examples/multimedia/spectrum/spectrumanalyser.cpp
+++ b/examples/multimedia/spectrum/spectrumanalyser.cpp
@@ -1,75 +1,31 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "spectrumanalyser.h"
-#include "utils.h"
#include "fftreal_wrapper.h"
+#include "utils.h"
-#include <qmath.h>
-#include <qmetatype.h>
#include <QAudioFormat>
+#include <QMetaType>
#include <QThread>
+#include <QtMath>
SpectrumAnalyserThread::SpectrumAnalyserThread(QObject *parent)
- : QObject(parent)
+ : QObject(parent)
#ifndef DISABLE_FFT
- , m_fft(new FFTRealWrapper)
+ ,
+ m_fft(new FFTRealWrapper)
#endif
- , m_numSamples(SpectrumLengthSamples)
- , m_windowFunction(DefaultWindowFunction)
- , m_window(SpectrumLengthSamples, 0.0)
- , m_input(SpectrumLengthSamples, 0.0)
- , m_output(SpectrumLengthSamples, 0.0)
- , m_spectrum(SpectrumLengthSamples)
+ ,
+ m_numSamples(SpectrumLengthSamples),
+ m_windowFunction(DefaultWindowFunction),
+ m_window(SpectrumLengthSamples, 0.0),
+ m_input(SpectrumLengthSamples, 0.0),
+ m_output(SpectrumLengthSamples, 0.0),
+ m_spectrum(SpectrumLengthSamples)
#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
- , m_thread(new QThread(this))
+ ,
+ m_thread(new QThread(this))
#endif
{
#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
@@ -96,7 +52,7 @@ void SpectrumAnalyserThread::setWindowFunction(WindowFunction type)
void SpectrumAnalyserThread::calculateWindow()
{
- for (int i=0; i<m_numSamples; ++i) {
+ for (int i = 0; i < m_numSamples; ++i) {
DataType x = 0.0;
switch (m_windowFunction) {
@@ -114,17 +70,16 @@ void SpectrumAnalyserThread::calculateWindow()
}
}
-void SpectrumAnalyserThread::calculateSpectrum(const QByteArray &buffer,
- int inputFrequency,
- int bytesPerFrame)
+void SpectrumAnalyserThread::calculateSpectrum(const QByteArray &buffer, int inputFrequency,
+ int bytesPerFrame)
{
#ifndef DISABLE_FFT
Q_ASSERT(buffer.size() == m_numSamples * bytesPerFrame);
// Initialize data array
const char *ptr = buffer.constData();
- for (int i=0; i<m_numSamples; ++i) {
- const qint16 pcmSample = *reinterpret_cast<const qint16*>(ptr);
+ for (int i = 0; i < m_numSamples; ++i) {
+ const qint16 pcmSample = *reinterpret_cast<const qint16 *>(ptr);
// Scale down to range [-1.0, 1.0]
const DataType realSample = pcmToReal(pcmSample);
const DataType windowedSample = realSample * m_window[i];
@@ -136,16 +91,16 @@ void SpectrumAnalyserThread::calculateSpectrum(const QByteArray &buffer,
m_fft->calculateFFT(m_output.data(), m_input.data());
// Analyze output to obtain amplitude and phase for each frequency
- for (int i=2; i<=m_numSamples/2; ++i) {
+ for (int i = 2; i <= m_numSamples / 2; ++i) {
// Calculate frequency of this complex sample
m_spectrum[i].frequency = qreal(i * inputFrequency) / (m_numSamples);
const qreal real = m_output[i];
qreal imag = 0.0;
- if (i>0 && i<m_numSamples/2)
- imag = m_output[m_numSamples/2 + i];
+ if (i > 0 && i < m_numSamples / 2)
+ imag = m_output[m_numSamples / 2 + i];
- const qreal magnitude = qSqrt(real*real + imag*imag);
+ const qreal magnitude = qSqrt(real * real + imag * imag);
qreal amplitude = SpectrumAnalyserMultiplier * qLn(magnitude);
// Bound amplitude to [0.0, 1.0]
@@ -159,21 +114,21 @@ void SpectrumAnalyserThread::calculateSpectrum(const QByteArray &buffer,
emit calculationComplete(m_spectrum);
}
-
//=============================================================================
// SpectrumAnalyser
//=============================================================================
SpectrumAnalyser::SpectrumAnalyser(QObject *parent)
- : QObject(parent)
- , m_thread(new SpectrumAnalyserThread(this))
- , m_state(Idle)
+ : QObject(parent),
+ m_thread(new SpectrumAnalyserThread(this)),
+ m_state(Idle)
#ifdef DUMP_SPECTRUMANALYSER
- , m_count(0)
+ ,
+ m_count(0)
#endif
{
- connect(m_thread, &SpectrumAnalyserThread::calculationComplete,
- this, &SpectrumAnalyser::calculationComplete);
+ connect(m_thread, &SpectrumAnalyserThread::calculationComplete, this,
+ &SpectrumAnalyser::calculationComplete);
}
SpectrumAnalyser::~SpectrumAnalyser() = default;
@@ -194,21 +149,18 @@ void SpectrumAnalyser::setOutputPath(const QString &outputDir)
void SpectrumAnalyser::setWindowFunction(WindowFunction type)
{
- const bool b = QMetaObject::invokeMethod(m_thread, "setWindowFunction",
- Qt::AutoConnection,
- Q_ARG(WindowFunction, type));
+ const bool b = QMetaObject::invokeMethod(m_thread, "setWindowFunction", Qt::AutoConnection,
+ Q_ARG(WindowFunction, type));
Q_ASSERT(b);
Q_UNUSED(b); // suppress warnings in release builds
}
-void SpectrumAnalyser::calculate(const QByteArray &buffer,
- const QAudioFormat &format)
+void SpectrumAnalyser::calculate(const QByteArray &buffer, const QAudioFormat &format)
{
// QThread::currentThread is marked 'for internal use only', but
// we're only using it for debug output here, so it's probably OK :)
- SPECTRUMANALYSER_DEBUG << "SpectrumAnalyser::calculate"
- << QThread::currentThread()
- << "state" << m_state;
+ SPECTRUMANALYSER_DEBUG << "SpectrumAnalyser::calculate" << QThread::currentThread() << "state"
+ << m_state;
if (isReady()) {
Q_ASSERT(format.sampleFormat() == QAudioFormat::Int16);
@@ -217,15 +169,16 @@ void SpectrumAnalyser::calculate(const QByteArray &buffer,
#ifdef DUMP_SPECTRUMANALYSER
m_count++;
- const QString pcmFileName = m_outputDir.filePath(QString("spectrum_%1.pcm").arg(m_count, 4, 10, QChar('0')));
+ const QString pcmFileName =
+ m_outputDir.filePath(QStringLiteral("spectrum_%1.pcm").arg(m_count, 4, 10, QChar('0')));
QFile pcmFile(pcmFileName);
pcmFile.open(QIODevice::WriteOnly);
const int bufferLength = m_numSamples * bytesPerFrame;
pcmFile.write(buffer, bufferLength);
m_textStream << "TimeDomain " << m_count << "\n";
- const qint16* input = reinterpret_cast<const qint16*>(buffer);
- for (int i=0; i<m_numSamples; ++i) {
+ const qint16 *input = reinterpret_cast<const qint16 *>(buffer);
+ for (int i = 0; i < m_numSamples; ++i) {
m_textStream << i << "\t" << *input << "\n";
input += format.channels();
}
@@ -238,22 +191,18 @@ void SpectrumAnalyser::calculate(const QByteArray &buffer,
// calculation will be done in the child thread.
// Once the calculation is finished, a calculationChanged signal will be
// emitted by m_thread.
- const bool b = QMetaObject::invokeMethod(m_thread, "calculateSpectrum",
- Qt::AutoConnection,
- Q_ARG(QByteArray, buffer),
- Q_ARG(int, format.sampleRate()),
- Q_ARG(int, bytesPerFrame));
+ const bool b = QMetaObject::invokeMethod(
+ m_thread, "calculateSpectrum", Qt::AutoConnection, Q_ARG(QByteArray, buffer),
+ Q_ARG(int, format.sampleRate()), Q_ARG(int, bytesPerFrame));
Q_ASSERT(b);
Q_UNUSED(b); // suppress warnings in release builds
#ifdef DUMP_SPECTRUMANALYSER
m_textStream << "FrequencySpectrum " << m_count << "\n";
FrequencySpectrum::const_iterator x = m_spectrum.begin();
- for (int i=0; i<m_numSamples; ++i, ++x)
- m_textStream << i << "\t"
- << x->frequency << "\t"
- << x->amplitude<< "\t"
- << x->phase << "\n";
+ for (int i = 0; i < m_numSamples; ++i, ++x)
+ m_textStream << i << "\t" << x->frequency << "\t" << x->amplitude << "\t" << x->phase
+ << "\n";
#endif
}
}
@@ -269,7 +218,6 @@ void SpectrumAnalyser::cancelCalculation()
m_state = Cancelled;
}
-
//-----------------------------------------------------------------------------
// Private slots
//-----------------------------------------------------------------------------
@@ -281,3 +229,5 @@ void SpectrumAnalyser::calculationComplete(const FrequencySpectrum &spectrum)
emit spectrumChanged(spectrum);
m_state = Idle;
}
+
+#include "moc_spectrumanalyser.cpp"
diff --git a/examples/multimedia/spectrum/spectrumanalyser.h b/examples/multimedia/spectrum/spectrumanalyser.h
index 799143489..202e602fe 100644
--- a/examples/multimedia/spectrum/spectrumanalyser.h
+++ b/examples/multimedia/spectrum/spectrumanalyser.h
@@ -1,71 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SPECTRUMANALYSER_H
#define SPECTRUMANALYSER_H
+#include "frequencyspectrum.h"
+#include "spectrum.h"
+
#include <QByteArray>
-#include <QObject>
#include <QList>
+#include <QObject>
#ifdef DUMP_SPECTRUMANALYSER
-#include <QDir>
-#include <QFile>
-#include <QTextStream>
+# include <QDir>
+# include <QFile>
+# include <QTextStream>
#endif
-#include "frequencyspectrum.h"
-#include "spectrum.h"
-
#ifndef DISABLE_FFT
-#include "FFTRealFixLenParam.h"
+# include "FFTRealFixLenParam.h"
#endif
QT_FORWARD_DECLARE_CLASS(QAudioFormat)
@@ -89,9 +42,7 @@ public:
public slots:
void setWindowFunction(WindowFunction type);
- void calculateSpectrum(const QByteArray &buffer,
- int inputFrequency,
- int bytesPerSample);
+ void calculateSpectrum(const QByteArray &buffer, int inputFrequency, int bytesPerSample);
signals:
void calculationComplete(const FrequencySpectrum &spectrum);
@@ -101,27 +52,27 @@ private:
private:
#ifndef DISABLE_FFT
- FFTRealWrapper* m_fft;
+ FFTRealWrapper *m_fft;
#endif
- const int m_numSamples;
+ const int m_numSamples;
- WindowFunction m_windowFunction;
+ WindowFunction m_windowFunction;
#ifdef DISABLE_FFT
- typedef qreal DataType;
+ typedef qreal DataType;
#else
- typedef FFTRealFixLenParam::DataType DataType;
+ typedef FFTRealFixLenParam::DataType DataType;
#endif
- QList<DataType> m_window;
+ QList<DataType> m_window;
- QList<DataType> m_input;
- QList<DataType> m_output;
+ QList<DataType> m_input;
+ QList<DataType> m_output;
- FrequencySpectrum m_spectrum;
+ FrequencySpectrum m_spectrum;
#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
- QThread* m_thread;
+ QThread *m_thread;
#endif
};
@@ -134,7 +85,7 @@ class SpectrumAnalyser : public QObject
Q_OBJECT
public:
- SpectrumAnalyser(QObject *parent = 0);
+ SpectrumAnalyser(QObject *parent = nullptr);
~SpectrumAnalyser();
#ifdef DUMP_SPECTRUMANALYSER
@@ -183,24 +134,18 @@ private:
void calculateWindow();
private:
+ SpectrumAnalyserThread *m_thread;
- SpectrumAnalyserThread* m_thread;
+ enum State { Idle, Busy, Cancelled };
- enum State {
- Idle,
- Busy,
- Cancelled
- };
-
- State m_state;
+ State m_state;
#ifdef DUMP_SPECTRUMANALYSER
- QDir m_outputDir;
- int m_count;
- QFile m_textFile;
- QTextStream m_textStream;
+ QDir m_outputDir;
+ int m_count;
+ QFile m_textFile;
+ QTextStream m_textStream;
#endif
};
#endif // SPECTRUMANALYSER_H
-
diff --git a/examples/multimedia/spectrum/tonegenerator.cpp b/examples/multimedia/spectrum/tonegenerator.cpp
index 3e70180fd..5df1df44d 100644
--- a/examples/multimedia/spectrum/tonegenerator.cpp
+++ b/examples/multimedia/spectrum/tonegenerator.cpp
@@ -1,59 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "spectrum.h"
#include "utils.h"
-#include <QByteArray>
+
#include <QAudioFormat>
-#include <qmath.h>
-#include <qendian.h>
+#include <QByteArray>
+#include <QtEndian>
+#include <QtMath>
void generateTone(const SweptTone &tone, const QAudioFormat &format, QByteArray &buffer)
{
@@ -86,7 +40,7 @@ void generateTone(const SweptTone &tone, const QAudioFormat &format, QByteArray
while (length) {
const qreal x = tone.amplitude * qSin(phase);
const qint16 value = realToPcm(x);
- for (int i=0; i<format.channelCount(); ++i) {
+ for (int i = 0; i < format.channelCount(); ++i) {
qToLittleEndian<qint16>(value, ptr);
ptr += channelBytes;
length -= channelBytes;
diff --git a/examples/multimedia/spectrum/tonegenerator.h b/examples/multimedia/spectrum/tonegenerator.h
index af6efade2..2c331fb3c 100644
--- a/examples/multimedia/spectrum/tonegenerator.h
+++ b/examples/multimedia/spectrum/tonegenerator.h
@@ -1,59 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef TONEGENERATOR_H
#define TONEGENERATOR_H
-#include <qglobal.h>
#include "spectrum.h"
+#include <QtGlobal>
+
QT_BEGIN_NAMESPACE
class QAudioFormat;
class QByteArray;
@@ -65,4 +19,3 @@ QT_END_NAMESPACE
void generateTone(const SweptTone &tone, const QAudioFormat &format, QByteArray &buffer);
#endif // TONEGENERATOR_H
-
diff --git a/examples/multimedia/spectrum/tonegeneratordialog.cpp b/examples/multimedia/spectrum/tonegeneratordialog.cpp
index 086eba758..1c98c2b86 100644
--- a/examples/multimedia/spectrum/tonegeneratordialog.cpp
+++ b/examples/multimedia/spectrum/tonegeneratordialog.cpp
@@ -1,62 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "tonegeneratordialog.h"
+
+#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton>
-#include <QVBoxLayout>
-#include <QCheckBox>
#include <QSlider>
#include <QSpinBox>
+#include <QVBoxLayout>
const int ToneGeneratorFreqMin = 1;
const int ToneGeneratorFreqMax = 1000;
@@ -64,15 +18,14 @@ const int ToneGeneratorFreqDefault = 440;
const int ToneGeneratorAmplitudeDefault = 75;
ToneGeneratorDialog::ToneGeneratorDialog(QWidget *parent)
- : QDialog(parent)
- , m_toneGeneratorSweepCheckBox(new QCheckBox(tr("Frequency sweep"), this))
- , m_frequencySweepEnabled(true)
- , m_toneGeneratorControl(new QWidget(this))
- , m_toneGeneratorFrequencyControl(new QWidget(this))
- , m_frequencySlider(new QSlider(Qt::Horizontal, this))
- , m_frequencySpinBox(new QSpinBox(this))
- , m_frequency(ToneGeneratorFreqDefault)
- , m_amplitudeSlider(new QSlider(Qt::Horizontal, this))
+ : QDialog(parent),
+ m_toneGeneratorSweepCheckBox(new QCheckBox(tr("Frequency sweep"), this)),
+ m_frequencySweepEnabled(true),
+ m_toneGeneratorControl(new QWidget(this)),
+ m_toneGeneratorFrequencyControl(new QWidget(this)),
+ m_frequencySlider(new QSlider(Qt::Horizontal, this)),
+ m_frequencySpinBox(new QSpinBox(this)),
+ m_amplitudeSlider(new QSlider(Qt::Horizontal, this))
{
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
@@ -106,12 +59,11 @@ ToneGeneratorDialog::ToneGeneratorDialog(QWidget *parent)
dialogLayout->addWidget(m_toneGeneratorControl);
// Connect
- connect(m_toneGeneratorSweepCheckBox, &QCheckBox::toggled,
- this, &ToneGeneratorDialog::frequencySweepEnabled);
- connect(m_frequencySlider, &QSlider::valueChanged,
- m_frequencySpinBox, &QSpinBox::setValue);
- connect(m_frequencySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
- m_frequencySlider, &QSlider::setValue);
+ connect(m_toneGeneratorSweepCheckBox, &QCheckBox::toggled, this,
+ &ToneGeneratorDialog::frequencySweepEnabled);
+ connect(m_frequencySlider, &QSlider::valueChanged, m_frequencySpinBox, &QSpinBox::setValue);
+ connect(m_frequencySpinBox, QOverload<int>::of(&QSpinBox::valueChanged), m_frequencySlider,
+ &QSlider::setValue);
// Add standard buttons to layout
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
@@ -119,10 +71,10 @@ ToneGeneratorDialog::ToneGeneratorDialog(QWidget *parent)
dialogLayout->addWidget(buttonBox);
// Connect standard buttons
- connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
- this, &ToneGeneratorDialog::accept);
- connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
- this, &ToneGeneratorDialog::reject);
+ connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this,
+ &ToneGeneratorDialog::accept);
+ connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this,
+ &ToneGeneratorDialog::reject);
setLayout(dialogLayout);
}
@@ -149,3 +101,5 @@ void ToneGeneratorDialog::frequencySweepEnabled(bool enabled)
m_frequencySweepEnabled = enabled;
m_toneGeneratorFrequencyControl->setEnabled(!enabled);
}
+
+#include "moc_tonegeneratordialog.cpp"
diff --git a/examples/multimedia/spectrum/tonegeneratordialog.h b/examples/multimedia/spectrum/tonegeneratordialog.h
index 5095cbb7d..2b0aed501 100644
--- a/examples/multimedia/spectrum/tonegeneratordialog.h
+++ b/examples/multimedia/spectrum/tonegeneratordialog.h
@@ -1,57 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef TONEGENERATORDIALOG_H
#define TONEGENERATORDIALOG_H
#include "spectrum.h"
+
#include <QAudioDevice>
#include <QDialog>
@@ -70,7 +24,7 @@ class ToneGeneratorDialog : public QDialog
Q_OBJECT
public:
- explicit ToneGeneratorDialog(QWidget *parent = 0);
+ explicit ToneGeneratorDialog(QWidget *parent = nullptr);
~ToneGeneratorDialog();
bool isFrequencySweepEnabled() const;
@@ -87,7 +41,6 @@ private:
QWidget *m_toneGeneratorFrequencyControl;
QSlider *m_frequencySlider;
QSpinBox *m_frequencySpinBox;
- qreal m_frequency;
QSlider *m_amplitudeSlider;
};
diff --git a/examples/multimedia/spectrum/utils.cpp b/examples/multimedia/spectrum/utils.cpp
index 2a6b2a33e..3b4146a51 100644
--- a/examples/multimedia/spectrum/utils.cpp
+++ b/examples/multimedia/spectrum/utils.cpp
@@ -1,55 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QAudioFormat>
#include "utils.h"
+#include <QAudioFormat>
qreal nyquistFrequency(const QAudioFormat &format)
{
@@ -81,7 +34,7 @@ QString formatToString(const QAudioFormat &format)
break;
}
- QString formatChannels = QString("%1 channels").arg(format.channelCount());
+ QString formatChannels = QStringLiteral("%1 channels").arg(format.channelCount());
switch (format.channelCount()) {
case 1:
formatChannels = "mono";
@@ -91,18 +44,18 @@ QString formatToString(const QAudioFormat &format)
break;
}
- result = QString("%1 Hz %2 bit %3 %4")
- .arg(format.sampleRate())
- .arg(format.bytesPerSample() * 8)
- .arg(formatType)
- .arg(formatChannels);
+ result = QStringLiteral("%1 Hz %2 bit %3 %4")
+ .arg(format.sampleRate())
+ .arg(format.bytesPerSample() * 8)
+ .arg(formatType)
+ .arg(formatChannels);
}
return result;
}
-const qint16 PCMS16MaxValue = 32767;
-const quint16 PCMS16MaxAmplitude = 32768; // because minimum is -32768
+const qint16 PCMS16MaxValue = 32767;
+const quint16 PCMS16MaxAmplitude = 32768; // because minimum is -32768
qreal pcmToReal(qint16 pcm)
{
diff --git a/examples/multimedia/spectrum/utils.h b/examples/multimedia/spectrum/utils.h
index d143a83f7..da0711c66 100644
--- a/examples/multimedia/spectrum/utils.h
+++ b/examples/multimedia/spectrum/utils.h
@@ -1,58 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef UTILS_H
#define UTILS_H
-#include <QtCore/qglobal.h>
#include <QDebug>
+#include <QtGlobal>
QT_FORWARD_DECLARE_CLASS(QAudioFormat)
@@ -72,12 +25,19 @@ qint16 realToPcm(qreal real);
// Compile-time calculation of powers of two
-template<int N> class PowerOfTwo
-{ public: static const int Result = PowerOfTwo<N-1>::Result * 2; };
-
-template<> class PowerOfTwo<0>
-{ public: static const int Result = 1; };
+template<int N>
+class PowerOfTwo
+{
+public:
+ static const int Result = PowerOfTwo<N - 1>::Result * 2;
+};
+template<>
+class PowerOfTwo<0>
+{
+public:
+ static const int Result = 1;
+};
//-----------------------------------------------------------------------------
// Debug output
@@ -86,28 +46,34 @@ template<> class PowerOfTwo<0>
class NullDebug
{
public:
- template <typename T>
- NullDebug& operator<<(const T&) { return *this; }
+ template<typename T>
+ NullDebug &operator<<(const T &)
+ {
+ return *this;
+ }
};
-inline NullDebug nullDebug() { return NullDebug(); }
+inline NullDebug nullDebug()
+{
+ return NullDebug();
+}
#ifdef LOG_ENGINE
-# define ENGINE_DEBUG qDebug()
+# define ENGINE_DEBUG qDebug()
#else
-# define ENGINE_DEBUG nullDebug()
+# define ENGINE_DEBUG nullDebug()
#endif
#ifdef LOG_SPECTRUMANALYSER
-# define SPECTRUMANALYSER_DEBUG qDebug()
+# define SPECTRUMANALYSER_DEBUG qDebug()
#else
-# define SPECTRUMANALYSER_DEBUG nullDebug()
+# define SPECTRUMANALYSER_DEBUG nullDebug()
#endif
#ifdef LOG_WAVEFORM
-# define WAVEFORM_DEBUG qDebug()
+# define WAVEFORM_DEBUG qDebug()
#else
-# define WAVEFORM_DEBUG nullDebug()
+# define WAVEFORM_DEBUG nullDebug()
#endif
#endif // UTILS_H
diff --git a/examples/multimedia/spectrum/waveform.cpp b/examples/multimedia/spectrum/waveform.cpp
index 5f9563a8d..a3727a2a6 100644
--- a/examples/multimedia/spectrum/waveform.cpp
+++ b/examples/multimedia/spectrum/waveform.cpp
@@ -1,76 +1,30 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "waveform.h"
#include "utils.h"
+
+#include <QDebug>
#include <QPainter>
#include <QResizeEvent>
-#include <QDebug>
//#define PAINT_EVENT_TRACE
#ifdef PAINT_EVENT_TRACE
-# define WAVEFORM_PAINT_DEBUG qDebug()
+# define WAVEFORM_PAINT_DEBUG qDebug()
#else
-# define WAVEFORM_PAINT_DEBUG nullDebug()
+# define WAVEFORM_PAINT_DEBUG nullDebug()
#endif
Waveform::Waveform(QWidget *parent)
- : QWidget(parent)
- , m_bufferPosition(0)
- , m_bufferLength(0)
- , m_audioPosition(0)
- , m_active(false)
- , m_tileLength(0)
- , m_tileArrayStart(0)
- , m_windowPosition(0)
- , m_windowLength(0)
+ : QWidget(parent),
+ m_bufferPosition(0),
+ m_bufferLength(0),
+ m_audioPosition(0),
+ m_active(false),
+ m_tileLength(0),
+ m_tileArrayStart(0),
+ m_windowPosition(0),
+ m_windowLength(0)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setMinimumHeight(50);
@@ -89,24 +43,23 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
if (m_active) {
WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
- << "windowPosition" << m_windowPosition
- << "windowLength" << m_windowLength;
+ << "windowPosition" << m_windowPosition << "windowLength"
+ << m_windowLength;
qint64 pos = m_windowPosition;
const qint64 windowEnd = m_windowPosition + m_windowLength;
int destLeft = 0;
int destRight = 0;
while (pos < windowEnd) {
const TilePoint point = tilePoint(pos);
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "pos" << pos
- << "tileIndex" << point.index
- << "positionOffset" << point.positionOffset
- << "pixelOffset" << point.pixelOffset;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "pos" << pos << "tileIndex" << point.index << "positionOffset"
+ << point.positionOffset << "pixelOffset" << point.pixelOffset;
if (point.index != NullIndex) {
const Tile &tile = m_tiles[point.index];
if (tile.painted) {
- const qint64 sectionLength = qMin((m_tileLength - point.positionOffset),
- (windowEnd - pos));
+ const qint64 sectionLength =
+ qMin((m_tileLength - point.positionOffset), (windowEnd - pos));
Q_ASSERT(sectionLength > 0);
const int sourceRight = tilePixelOffset(point.positionOffset + sectionLength);
@@ -120,9 +73,10 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
sourceRect.setLeft(point.pixelOffset);
sourceRect.setRight(sourceRight);
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "tileIndex" << point.index
- << "source" << point.pixelOffset << sourceRight
- << "dest" << destLeft << destRight;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "tileIndex" << point.index << "source"
+ << point.pixelOffset << sourceRight << "dest" << destLeft
+ << destRight;
painter.drawPixmap(destRect, *tile.pixmap, sourceRect);
@@ -130,25 +84,30 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
if (point.index < m_tiles.count()) {
pos = tilePosition(point.index + 1);
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "pos ->" << pos;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "pos ->" << pos;
} else {
// Reached end of tile array
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "reached end of tile array";
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "reached end of tile array";
break;
}
} else {
// Passed last tile which is painted
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "tile" << point.index << "not painted";
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "tile" << point.index << "not painted";
break;
}
} else {
// pos is past end of tile array
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "pos" << pos << "past end of tile array";
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "pos" << pos << "past end of tile array";
break;
}
}
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "final pos" << pos << "final x" << destRight;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "final pos" << pos << "final x" << destRight;
}
}
@@ -158,11 +117,12 @@ void Waveform::resizeEvent(QResizeEvent *event)
createPixmaps(event->size());
}
-void Waveform::initialize(const QAudioFormat &format, qint64 audioBufferSize, qint64 windowDurationUs)
+void Waveform::initialize(const QAudioFormat &format, qint64 audioBufferSize,
+ qint64 windowDurationUs)
{
WAVEFORM_DEBUG << "Waveform::initialize"
- << "audioBufferSize" << audioBufferSize
- << "windowDurationUs" << windowDurationUs;
+ << "audioBufferSize" << audioBufferSize << "windowDurationUs"
+ << windowDurationUs;
reset();
@@ -185,9 +145,8 @@ void Waveform::initialize(const QAudioFormat &format, qint64 audioBufferSize, qi
}
WAVEFORM_DEBUG << "Waveform::initialize"
- << "tileLength" << m_tileLength
- << "windowLength" << m_windowLength
- << "nTiles" << nTiles;
+ << "tileLength" << m_tileLength << "windowLength" << m_windowLength << "nTiles"
+ << nTiles;
m_pixmaps.fill(nullptr, nTiles);
m_tiles.resize(nTiles);
@@ -217,8 +176,7 @@ void Waveform::reset()
void Waveform::bufferChanged(qint64 position, qint64 length, const QByteArray &buffer)
{
WAVEFORM_DEBUG << "Waveform::bufferChanged"
- << "audioPosition" << m_audioPosition
- << "bufferPosition" << position
+ << "audioPosition" << m_audioPosition << "bufferPosition" << position
<< "bufferLength" << length;
m_bufferPosition = position;
m_bufferLength = length;
@@ -229,8 +187,7 @@ void Waveform::bufferChanged(qint64 position, qint64 length, const QByteArray &b
void Waveform::audioPositionChanged(qint64 position)
{
WAVEFORM_DEBUG << "Waveform::audioPositionChanged"
- << "audioPosition" << position
- << "bufferPosition" << m_bufferPosition
+ << "audioPosition" << position << "bufferPosition" << m_bufferPosition
<< "bufferLength" << m_bufferLength;
if (position >= m_bufferPosition) {
@@ -243,7 +200,7 @@ void Waveform::audioPositionChanged(qint64 position)
void Waveform::deletePixmaps()
{
- qDeleteAll(qExchange(m_pixmaps, {}));
+ qDeleteAll(std::exchange(m_pixmaps, {}));
}
void Waveform::createPixmaps(const QSize &widgetSize)
@@ -252,19 +209,18 @@ void Waveform::createPixmaps(const QSize &widgetSize)
m_pixmapSize.setWidth(qreal(widgetSize.width()) * m_tileLength / m_windowLength);
WAVEFORM_DEBUG << "Waveform::createPixmaps"
- << "widgetSize" << widgetSize
- << "pixmapSize" << m_pixmapSize;
+ << "widgetSize" << widgetSize << "pixmapSize" << m_pixmapSize;
Q_ASSERT(m_tiles.count() == m_pixmaps.count());
// (Re)create pixmaps
- for (auto & pixmap : m_pixmaps) {
+ for (auto &pixmap : m_pixmaps) {
delete pixmap;
pixmap = new QPixmap(m_pixmapSize);
}
// Update tile pixmap pointers, and mark for repainting
- for (int i=0; i<m_tiles.count(); ++i) {
+ for (int i = 0; i < m_tiles.count(); ++i) {
m_tiles[i].pixmap = m_pixmaps[i];
m_tiles[i].painted = false;
}
@@ -273,14 +229,14 @@ void Waveform::createPixmaps(const QSize &widgetSize)
void Waveform::setWindowPosition(qint64 position)
{
WAVEFORM_DEBUG << "Waveform::setWindowPosition"
- << "old" << m_windowPosition << "new" << position
- << "tileArrayStart" << m_tileArrayStart;
+ << "old" << m_windowPosition << "new" << position << "tileArrayStart"
+ << m_tileArrayStart;
const qint64 oldPosition = m_windowPosition;
m_windowPosition = position;
- if ((m_windowPosition >= oldPosition) &&
- (m_windowPosition - m_tileArrayStart < (m_tiles.count() * m_tileLength))) {
+ if ((m_windowPosition >= oldPosition)
+ && (m_windowPosition - m_tileArrayStart < (m_tiles.count() * m_tileLength))) {
// Work out how many tiles need to be shuffled
const qint64 offset = m_windowPosition - m_tileArrayStart;
const int nTiles = offset / m_tileLength;
@@ -335,7 +291,7 @@ bool Waveform::paintTiles()
WAVEFORM_DEBUG << "Waveform::paintTiles";
bool updateRequired = false;
- for (int i=0; i<m_tiles.count(); ++i) {
+ for (int i = 0; i < m_tiles.count(); ++i) {
const Tile &tile = m_tiles[i];
if (!tile.painted) {
const qint64 tileStart = m_tileArrayStart + i * m_tileLength;
@@ -358,11 +314,8 @@ void Waveform::paintTile(int index)
const qint64 tileStart = m_tileArrayStart + index * m_tileLength;
WAVEFORM_DEBUG << "Waveform::paintTile"
- << "index" << index
- << "bufferPosition" << m_bufferPosition
- << "bufferLength" << m_bufferLength
- << "start" << tileStart
- << "end" << tileStart + m_tileLength;
+ << "index" << index << "bufferPosition" << m_bufferPosition << "bufferLength"
+ << m_bufferLength << "start" << tileStart << "end" << tileStart + m_tileLength;
Q_ASSERT(m_bufferPosition <= tileStart);
Q_ASSERT(m_bufferPosition + m_bufferLength >= tileStart + m_tileLength);
@@ -370,8 +323,8 @@ void Waveform::paintTile(int index)
Tile &tile = m_tiles[index];
Q_ASSERT(!tile.painted);
- const qint16* base = reinterpret_cast<const qint16*>(m_buffer.constData());
- const qint16* buffer = base + ((tileStart - m_bufferPosition) / 2);
+ const qint16 *base = reinterpret_cast<const qint16 *>(m_buffer.constData());
+ const qint16 *buffer = base + ((tileStart - m_bufferPosition) / 2);
const int numSamples = m_tileLength / (2 * m_format.channelCount());
QPainter painter(tile.pixmap);
@@ -393,10 +346,10 @@ void Waveform::paintTile(int index)
QLine line(origin, origin);
- for (int i=0; i<numSamples; ++i) {
- const qint16* ptr = buffer + i * m_format.channelCount();
+ for (int i = 0; i < numSamples; ++i) {
+ const qint16 *ptr = buffer + i * m_format.channelCount();
- const int offset = reinterpret_cast<const char*>(ptr) - m_buffer.constData();
+ const int offset = reinterpret_cast<const char *>(ptr) - m_buffer.constData();
Q_ASSERT(offset >= 0);
Q_ASSERT(offset < m_bufferLength);
Q_UNUSED(offset);
@@ -417,7 +370,8 @@ void Waveform::paintTile(int index)
void Waveform::shuffleTiles(int n)
{
- WAVEFORM_DEBUG << "Waveform::shuffleTiles" << "n" << n;
+ WAVEFORM_DEBUG << "Waveform::shuffleTiles"
+ << "n" << n;
while (n--) {
Tile tile = m_tiles.first();
@@ -427,17 +381,20 @@ void Waveform::shuffleTiles(int n)
m_tileArrayStart += m_tileLength;
}
- WAVEFORM_DEBUG << "Waveform::shuffleTiles" << "tileArrayStart" << m_tileArrayStart;
+ WAVEFORM_DEBUG << "Waveform::shuffleTiles"
+ << "tileArrayStart" << m_tileArrayStart;
}
void Waveform::resetTiles(qint64 newStartPos)
{
- WAVEFORM_DEBUG << "Waveform::resetTiles" << "newStartPos" << newStartPos;
+ WAVEFORM_DEBUG << "Waveform::resetTiles"
+ << "newStartPos" << newStartPos;
QList<Tile>::iterator i = m_tiles.begin();
- for ( ; i != m_tiles.end(); ++i)
+ for (; i != m_tiles.end(); ++i)
i->painted = false;
m_tileArrayStart = newStartPos;
}
+#include "moc_waveform.cpp"
diff --git a/examples/multimedia/spectrum/waveform.h b/examples/multimedia/spectrum/waveform.h
index 8c26e99b2..86fda9962 100644
--- a/examples/multimedia/spectrum/waveform.h
+++ b/examples/multimedia/spectrum/waveform.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef WAVEFORM_H
#define WAVEFORM_H
@@ -70,7 +23,7 @@ class Waveform : public QWidget
Q_OBJECT
public:
- explicit Waveform(QWidget *parent = 0);
+ explicit Waveform(QWidget *parent = nullptr);
~Waveform();
// QWidget
@@ -115,17 +68,18 @@ private:
struct TilePoint
{
TilePoint(int idx = 0, qint64 pos = 0, qint64 pix = 0)
- : index(idx), positionOffset(pos), pixelOffset(pix)
- { }
+ : index(idx), positionOffset(pos), pixelOffset(pix)
+ {
+ }
// Index of tile
- int index;
+ int index;
// Number of bytes from start of tile
- qint64 positionOffset;
+ qint64 positionOffset;
// Number of pixels from left of corresponding pixmap
- int pixelOffset;
+ int pixelOffset;
};
/*
@@ -177,36 +131,37 @@ private:
void resetTiles(qint64 newStartPos);
private:
- qint64 m_bufferPosition;
- qint64 m_bufferLength;
- QByteArray m_buffer;
+ qint64 m_bufferPosition;
+ qint64 m_bufferLength;
+ QByteArray m_buffer;
- qint64 m_audioPosition;
- QAudioFormat m_format;
+ qint64 m_audioPosition;
+ QAudioFormat m_format;
- bool m_active;
+ bool m_active;
- QSize m_pixmapSize;
- QList<QPixmap*> m_pixmaps;
+ QSize m_pixmapSize;
+ QList<QPixmap *> m_pixmaps;
- struct Tile {
+ struct Tile
+ {
// Pointer into parent m_pixmaps array
- QPixmap* pixmap;
+ QPixmap *pixmap;
// Flag indicating whether this tile has been painted
- bool painted;
+ bool painted;
};
- QList<Tile> m_tiles;
+ QList<Tile> m_tiles;
// Length of audio data in bytes depicted by each tile
- qint64 m_tileLength;
+ qint64 m_tileLength;
// Position in bytes of the first tile, relative to m_buffer
- qint64 m_tileArrayStart;
+ qint64 m_tileArrayStart;
- qint64 m_windowPosition;
- qint64 m_windowLength;
+ qint64 m_windowPosition;
+ qint64 m_windowLength;
};
#endif // WAVEFORM_H
diff --git a/examples/multimedia/video/CMakeLists.txt b/examples/multimedia/video/CMakeLists.txt
index d3f707074..72e5d7e8a 100644
--- a/examples/multimedia/video/CMakeLists.txt
+++ b/examples/multimedia/video/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
qt_internal_add_example(qmlvideo)
if(TARGET Qt::QuickControls2)
if(TARGET Qt::Svg)
diff --git a/examples/multimedia/video/mediaplayer/AudioControl.qml b/examples/multimedia/video/mediaplayer/AudioControl.qml
deleted file mode 100644
index 7e504bc5c..000000000
--- a/examples/multimedia/video/mediaplayer/AudioControl.qml
+++ /dev/null
@@ -1,92 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
- property bool muted: false
- property real volume: volumeSlider.value/100.
-
- implicitHeight: buttons.height
-
- RowLayout {
- anchors.fill: parent
-
- Item {
- id: buttons
-
- width: muteButton.implicitWidth
- height: muteButton.implicitHeight
-
- RoundButton {
- id: muteButton
- radius: 50.0
- icon.source: muted ? "qrc:///Mute_Icon.svg" : "qrc:///Speaker_Icon.svg"
- onClicked: { muted = !muted }
- }
- }
-
- Slider {
- id: volumeSlider
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignVCenter
-
- enabled: true
- to: 100.0
- value: 100.0
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/CMakeLists.txt b/examples/multimedia/video/mediaplayer/CMakeLists.txt
index df62fbee1..e33a20ad4 100644
--- a/examples/multimedia/video/mediaplayer/CMakeLists.txt
+++ b/examples/multimedia/video/mediaplayer/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(mediaplayer LANGUAGES CXX)
@@ -10,39 +13,56 @@ endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/video/mediaplayer")
-find_package(Qt6 REQUIRED COMPONENTS Core Quick QuickControls2 Svg)
+find_package(Qt6 REQUIRED COMPONENTS Multimedia Core Quick QuickControls2 Svg)
+
+qt_standard_project_setup(REQUIRES 6.5)
-qt_add_executable(mediaplayer
+qt_add_executable(mediaplayerexample
main.cpp
)
-set(resource_files
- "main.qml"
- "PlaybackControl.qml"
- "MetadataInfo.qml"
- "AudioControl.qml"
- "PlaybackSeekControl.qml"
- "PlaybackRateControl.qml"
- "PlayerMenuBar.qml"
- "TracksInfo.qml"
- "Mute_Icon.svg"
- "Speaker_Icon.svg"
+set_target_properties(mediaplayerexample PROPERTIES
+ MACOSX_BUNDLE TRUE
)
-qt_add_resources(mediaplayer "mediaplayer"
- PREFIX
- "/"
- FILES
- ${resource_files}
+qt_add_qml_module(mediaplayerexample
+ URI mediaplayer
+ QML_FILES
+ "Main.qml"
+ "controls/PlaybackControl.qml"
+ "controls/AudioControl.qml"
+ "controls/PlaybackSeekControl.qml"
+ "controls/SettingsPopup.qml"
+ "controls/UrlPopup.qml"
+ "controls/MetadataInfo.qml"
+ "controls/TracksInfo.qml"
+ RESOURCES
+ "images/backward10.svg"
+ "images/mute.svg"
+ "images/open_new.svg"
+ "images/pause_symbol.svg"
+ "images/play_symbol.svg"
+ "images/forward10.svg"
+ "images/more.svg"
+ "images/speaker.svg"
+ "images/stop_symbol.svg"
+ "images/volume.svg"
+ "images/volume_mute.svg"
+ "images/zoom_maximize.svg"
+ "images/zoom_minimize.svg"
+ "images/link.svg"
+ "images/loop.svg"
)
-target_link_libraries(mediaplayer PRIVATE
+target_link_libraries(mediaplayerexample PRIVATE
Qt6::Core
+ Qt6::Multimedia
Qt6::Svg
Qt6::Quick
+ Qt6::Multimedia
)
-install(TARGETS mediaplayer
+install(TARGETS mediaplayerexample
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
diff --git a/examples/multimedia/video/mediaplayer/Main.qml b/examples/multimedia/video/mediaplayer/Main.qml
new file mode 100644
index 000000000..9ee9cf6c3
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/Main.qml
@@ -0,0 +1,178 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtQuick.Dialogs
+import QtMultimedia
+import "controls"
+
+//! [0]
+ApplicationWindow {
+ id: root
+ title: qsTr("Multimedia Player")
+ width: 1280
+ height: 720
+ //! [0]
+ minimumWidth: 960
+ minimumHeight: 540
+ visible: true
+ color: "black"
+
+ property alias source: mediaPlayer.source
+ property alias playbackRate: mediaPlayer.playbackRate
+ property bool fullScreen: false
+
+ MessageDialog {
+ id: mediaError
+ buttons: MessageDialog.Ok
+ }
+
+ MouseArea {
+ // an activity listener to hide the playback contols when idle
+ id: activityListener
+ anchors.fill: parent
+ z: 1
+ propagateComposedEvents: true
+ hoverEnabled: true
+
+ property bool inactiveMouse: false
+
+ Timer {
+ id: timer
+ interval: 3000 // milliseconds
+ onTriggered: activityListener.inactiveMouse = true
+ }
+
+ function activityHandler(mouse) {
+ if (activityListener.inactiveMouse)
+ activityListener.inactiveMouse = false
+ timer.restart()
+ timer.start()
+ mouse.accepted = false
+ }
+
+ onPositionChanged: mouse => activityHandler(mouse)
+ onPressed: mouse => activityHandler(mouse)
+ onDoubleClicked: mouse => mouse.accepted = false
+ }
+
+ MetadataInfo {
+ id: metadataInfo
+ }
+
+ TracksInfo {
+ id: audioTracksInfo
+ onSelectedTrackChanged: {
+ mediaPlayer.activeAudioTrack = selectedTrack
+ mediaPlayer.updateMetadata()
+ }
+ }
+
+ TracksInfo {
+ id: videoTracksInfo
+ onSelectedTrackChanged: {
+ mediaPlayer.activeVideoTrack = selectedTrack
+ mediaPlayer.updateMetadata()
+ }
+ }
+
+ TracksInfo {
+ id: subtitleTracksInfo
+ onSelectedTrackChanged: {
+ mediaPlayer.activeSubtitleTrack = selectedTrack
+ mediaPlayer.updateMetadata()
+ }
+ }
+
+ //! [1]
+ MediaPlayer {
+ id: mediaPlayer
+ //! [1]
+ function updateMetadata() {
+ metadataInfo.clear()
+ metadataInfo.read(mediaPlayer.metaData)
+ metadataInfo.read(mediaPlayer.audioTracks[mediaPlayer.activeAudioTrack])
+ metadataInfo.read(mediaPlayer.videoTracks[mediaPlayer.activeVideoTrack])
+ metadataInfo.read(mediaPlayer.subtitleTracks[mediaPlayer.activeSubtitleTrack])
+ }
+ //! [2]
+ videoOutput: videoOutput
+ audioOutput: AudioOutput {
+ id: audio
+ muted: playbackController.muted
+ volume: playbackController.volume
+ }
+ //! [2]
+ //! [4]
+ onErrorOccurred: {
+ mediaError.open()
+ mediaError.text = mediaPlayer.errorString
+ }
+ //! [4]
+ onMetaDataChanged: { updateMetadata() }
+ //! [6]
+ onTracksChanged: {
+ audioTracksInfo.read(mediaPlayer.audioTracks)
+ videoTracksInfo.read(mediaPlayer.videoTracks)
+ subtitleTracksInfo.read(mediaPlayer.subtitleTracks, 6) /* QMediaMetaData::Language = 6 */
+ updateMetadata()
+ mediaPlayer.play()
+ }
+ //! [6]
+ source: new URL("https://download.qt.io/learning/videos/media-player-example/Qt_LogoMergeEffect.mp4")
+ }
+
+ //! [3]
+ VideoOutput {
+ id: videoOutput
+ anchors.fill: parent
+ visible: mediaPlayer.mediaStatus > 0
+
+ TapHandler {
+ onDoubleTapped: {
+ root.fullScreen ? root.showNormal() : root.showFullScreen()
+ root.fullScreen = !root.fullScreen
+ }
+ }
+ }
+ //! [3]
+
+ Rectangle {
+ anchors.fill: parent
+ visible: mediaPlayer.mediaStatus === 0
+ color: "black"
+
+ TapHandler {
+ onDoubleTapped: {
+ root.fullScreen ? root.showNormal() : root.showFullScreen()
+ root.fullScreen = !root.fullScreen
+ }
+ }
+ }
+
+ //! [5]
+ PlaybackControl {
+ id: playbackController
+ //! [5]
+
+ property bool showControls: !activityListener.inactiveMouse || busy
+ opacity: showControls
+ // onOpacityChanged can't be used as it is animated and therefore not immediate
+ onShowControlsChanged: activityListener.cursorShape = showControls ?
+ Qt.ArrowCursor : Qt.BlankCursor
+
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ //! [6]
+ mediaPlayer: mediaPlayer
+ audioTracksInfo: audioTracksInfo
+ videoTracksInfo: videoTracksInfo
+ subtitleTracksInfo: subtitleTracksInfo
+ metadataInfo: metadataInfo
+ }
+ //! [6]
+}
diff --git a/examples/multimedia/video/mediaplayer/MetadataInfo.qml b/examples/multimedia/video/mediaplayer/MetadataInfo.qml
deleted file mode 100644
index 5019c1759..000000000
--- a/examples/multimedia/video/mediaplayer/MetadataInfo.qml
+++ /dev/null
@@ -1,116 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
- implicitWidth: 200
-
- function clear() {
- elements.clear();
- }
-
- function read(metadata) {
- if (metadata) {
- for (var key of metadata.keys()) {
- if (metadata.stringValue(key)) {
- elements.append(
- { name: metadata.metaDataKeyToString(key)
- , value: metadata.stringValue(key)
- })
- }
- }
- }
- }
-
- ListModel {
- id: elements
- }
-
- Frame {
- anchors.fill: parent
- padding: 15
-
- background: Rectangle {
- color: "lightgray"
- opacity: 0.7
- }
-
- ListView {
- id: metadataList
- visible: elements.count > 0
- anchors.fill: parent
- model: elements
- delegate: RowLayout {
- width: metadataList.width
- Text {
- Layout.preferredWidth: 90
- text: model.name + ":"
- horizontalAlignment: Text.AlignRight
- }
- Text {
- Layout.fillWidth: true
- text: model.value
- wrapMode: Text.WrapAnywhere
- }
- }
- }
-
- Text {
- id: metadataNoList
- visible: elements.count === 0
- text: qsTr("No metadata present")
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/PlaybackControl.qml b/examples/multimedia/video/mediaplayer/PlaybackControl.qml
deleted file mode 100644
index 50715c537..000000000
--- a/examples/multimedia/video/mediaplayer/PlaybackControl.qml
+++ /dev/null
@@ -1,197 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
- property int mediaPlayerState: mediaPlayer.playbackState
- property alias muted: audio.muted
- property alias volume: audio.volume
-
- height: frame.height
-
- opacity: 1
-
- Behavior on opacity { NumberAnimation { duration: 300 }}
-
- function updateOpacity() {
- //hover is not usable in mobile platforms
- if (Qt.platform.os == "android" || Qt.platform.os == "ios")
- return;
-
- if (playbackControlHoover.hovered || mediaPlayerState != MediaPlayer.PlayingState || !mediaPlayer.hasVideo)
- root.opacity = 1;
- else
- root.opacity = 1; // 0; TODO: enable opacity change when HoverHandle is fixed
- }
-
- Connections {
- target: mediaPlayer
- function onPlaybackStateChanged() { updateOpacity() }
- function onHasVideoChanged() { updateOpacity() }
- }
-
- HoverHandler {
- id: playbackControlHoover
- margin: 50
- onHoveredChanged: updateOpacity()
- }
-
- Frame {
- id: frame
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.bottom: parent.bottom
-
- background: Rectangle {
- color: "white"
- }
-
- ColumnLayout {
- id: playbackControlPanel
- anchors.fill: parent
- anchors.leftMargin: 10
- anchors.rightMargin: 10
-
- PlaybackSeekControl {
- Layout.fillWidth: true
- mediaPlayer: root.mediaPlayer
- }
-
- RowLayout {
- id: playerButtons
-
- Layout.fillWidth: true
-
- PlaybackRateControl {
- Layout.minimumWidth: 100
- Layout.maximumWidth: 150
- Layout.fillHeight: true
- Layout.fillWidth: true
- mediaPlayer: root.mediaPlayer
- }
-
- Item {
- Layout.fillWidth: true
- }
-
- RowLayout {
- Layout.alignment: Qt.AlignCenter
- id: controlButtons
-
- RoundButton {
- id: pauseButton
- radius: 50.0
- text: "\u2016";
- onClicked: mediaPlayer.pause()
- }
-
- RoundButton {
- id: playButton
- radius: 50.0
- text: "\u25B6";
- onClicked: mediaPlayer.play()
- }
-
- RoundButton {
- id: stopButton
- radius: 50.0
- text: "\u25A0";
- onClicked: mediaPlayer.stop()
- }
- }
-
- Item {
- Layout.fillWidth: true
- }
-
- AudioControl {
- id: audio
- Layout.minimumWidth: 100
- Layout.maximumWidth: 150
- Layout.fillWidth: true
- mediaPlayer: root.mediaPlayer
- }
- }
- }
- }
-
- states: [
- State {
- name: "playing"
- when: mediaPlayerState == MediaPlayer.PlayingState
- PropertyChanges { target: pauseButton; visible: true}
- PropertyChanges { target: playButton; visible: false}
- PropertyChanges { target: stopButton; visible: true}
- },
- State {
- name: "stopped"
- when: mediaPlayerState == MediaPlayer.StoppedState
- PropertyChanges { target: pauseButton; visible: false}
- PropertyChanges { target: playButton; visible: true}
- PropertyChanges { target: stopButton; visible: false}
- },
- State {
- name: "paused"
- when: mediaPlayerState == MediaPlayer.PausedState
- PropertyChanges { target: pauseButton; visible: false}
- PropertyChanges { target: playButton; visible: true}
- PropertyChanges { target: stopButton; visible: true}
- }
- ]
-
-}
-
diff --git a/examples/multimedia/video/mediaplayer/PlaybackRateControl.qml b/examples/multimedia/video/mediaplayer/PlaybackRateControl.qml
deleted file mode 100644
index 99c151a77..000000000
--- a/examples/multimedia/video/mediaplayer/PlaybackRateControl.qml
+++ /dev/null
@@ -1,80 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
-
- RowLayout {
- anchors.fill: parent
-
- Slider {
- id: slider
- Layout.fillWidth: true
- snapMode: Slider.SnapOnRelease
- enabled: true
- from: 0.5
- to: 2.5
- stepSize: 0.5
- value: 1.0
-
- onMoved: { mediaPlayer.setPlaybackRate(value) }
- }
- Text {
- text: "Rate " + slider.value + "x"
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/PlaybackSeekControl.qml b/examples/multimedia/video/mediaplayer/PlaybackSeekControl.qml
deleted file mode 100644
index 5c04d1848..000000000
--- a/examples/multimedia/video/mediaplayer/PlaybackSeekControl.qml
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
-
- implicitHeight: 20
-
-
- RowLayout {
- anchors.fill: parent
-
- Text {
- id: mediaTime
- Layout.minimumWidth: 50
- Layout.minimumHeight: 18
- horizontalAlignment: Text.AlignRight
- text: {
- var m = Math.floor(mediaPlayer.position / 60000)
- var ms = (mediaPlayer.position / 1000 - m * 60).toFixed(1)
- return `${m}:${ms.padStart(4, 0)}`
- }
- }
-
- Slider {
- id: mediaSlider
- Layout.fillWidth: true
- enabled: mediaPlayer.seekable
- to: 1.0
- value: mediaPlayer.position / mediaPlayer.duration
-
- onMoved: mediaPlayer.setPosition(value * mediaPlayer.duration)
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/PlayerMenuBar.qml b/examples/multimedia/video/mediaplayer/PlayerMenuBar.qml
deleted file mode 100644
index a8fabd30a..000000000
--- a/examples/multimedia/video/mediaplayer/PlayerMenuBar.qml
+++ /dev/null
@@ -1,171 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Dialogs
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
-
- required property MediaPlayer mediaPlayer
- required property VideoOutput videoOutput
- required property MetadataInfo metadataInfo
- required property TracksInfo audioTracksInfo
- required property TracksInfo videoTracksInfo
- required property TracksInfo subtitleTracksInfo
-
- height: menuBar.height
-
- signal closePlayer
-
- function loadUrl(url) {
- mediaPlayer.stop()
- mediaPlayer.source = url
- mediaPlayer.play()
- }
-
- function closeOverlays(){
- metadataInfo.visible = false;
- audioTracksInfo.visible = false;
- videoTracksInfo.visible = false;
- subtitleTracksInfo.visible = false;
- }
-
- function showOverlay(overlay){
- closeOverlays();
- overlay.visible = true;
- }
-
- Popup {
- id: urlPopup
- anchors.centerIn: Overlay.overlay
-
- RowLayout {
- id: rowOpenUrl
- Label {
- text: qsTr("URL:");
- }
-
- TextInput {
- id: urlText
- focus: true
- Layout.minimumWidth: 400
- wrapMode: TextInput.WrapAnywhere
- Keys.onReturnPressed: { loadUrl(text); urlText.text = ""; urlPopup.close() }
- }
-
- Button {
- text: "Load"
- onClicked: { loadUrl(urlText.text); urlText.text = ""; urlPopup.close() }
- }
- }
- onOpened: { urlPopup.forceActiveFocus() }
- }
-
- FileDialog {
- id: fileDialog
- title: "Please choose a file"
- onAccepted: {
- mediaPlayer.stop()
- mediaPlayer.source = fileDialog.currentFile
- mediaPlayer.play()
- }
- }
-
- MenuBar {
- id: menuBar
- anchors.left: parent.left
- anchors.right: parent.right
-
- Menu {
- title: qsTr("&File")
- Action {
- text: qsTr("&Open")
- onTriggered: fileDialog.open()
- }
- Action {
- text: qsTr("&URL");
- onTriggered: urlPopup.open()
- }
-
- Action {
- text: qsTr("&Exit");
- onTriggered: closePlayer()
- }
- }
-
- Menu {
- title: qsTr("&View")
- Action {
- text: qsTr("Metadata")
- onTriggered: showOverlay(metadataInfo)
- }
- }
-
- Menu {
- title: qsTr("&Tracks")
- Action {
- text: qsTr("Audio")
- onTriggered: showOverlay(audioTracksInfo)
- }
- Action {
- text: qsTr("Video")
- onTriggered: showOverlay(videoTracksInfo)
- }
- Action {
- text: qsTr("Subtitles")
- onTriggered: showOverlay(subtitleTracksInfo)
- }
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/TracksInfo.qml b/examples/multimedia/video/mediaplayer/TracksInfo.qml
deleted file mode 100644
index f2851228a..000000000
--- a/examples/multimedia/video/mediaplayer/TracksInfo.qml
+++ /dev/null
@@ -1,122 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-Item {
- id: root
- implicitWidth: 200
-
- property int selectedTrack: 0
-
- function read(metadataList) {
- var LanguageKey = 6;
-
- elements.clear()
-
- elements.append(
- { language: "No Selected Track"
- , trackNumber: -1
- })
-
- if (!metadataList)
- return;
-
- metadataList.forEach(function (metadata, index) {
- var language = metadata.stringValue(LanguageKey);
- var label = language ? metadata.stringValue(LanguageKey) : "track " + (index + 1)
- elements.append(
- { language: label
- , trackNumber: index
- })
- });
- }
-
- ListModel {
- id: elements
- }
-
- Frame {
- anchors.fill: parent
- padding: 15
-
- background: Rectangle {
- color: "lightgray"
- opacity: 0.7
- }
-
- ButtonGroup {id:group; }
-
- ListView {
- id: trackList
- visible: elements.count > 0
- anchors.fill: parent
- model: elements
- delegate: RowLayout {
- width: trackList.width
- RadioButton {
- checked: model.trackNumber === selectedTrack
- text: model.language
- ButtonGroup.group: group
- onClicked: selectedTrack = model.trackNumber
- }
- }
- }
-
- Text {
- id: metadataNoList
- visible: elements.count === 0
- text: qsTr("No tracks present")
- }
- }
-}
diff --git a/examples/multimedia/video/mediaplayer/controls/AudioControl.qml b/examples/multimedia/video/mediaplayer/controls/AudioControl.qml
new file mode 100644
index 000000000..81ebe7f10
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/AudioControl.qml
@@ -0,0 +1,50 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Item {
+ id: audioController
+
+ property alias busy: slider.pressed
+ //! [0]
+ property alias muted: muteButton.checked
+ property real volume: slider.value
+ //! [0]
+ property alias showSlider: slider.visible
+ property int iconDimension: 24
+
+ implicitHeight: 46
+ implicitWidth: mainLayout.width
+
+ RowLayout {
+ id: mainLayout
+ spacing: 10
+ anchors.verticalCenter: parent.verticalCenter
+
+ RoundButton {
+ id: muteButton
+ implicitHeight: 40
+ implicitWidth: 40
+ radius: 4
+ icon.source: audioController.muted ? "../images/volume_mute.svg" : "../images/volume.svg"
+ icon.width: audioController.iconDimension
+ icon.height: audioController.iconDimension
+ flat: true
+ checkable: true
+ }
+
+ Slider {
+ id: slider
+ visible: !audioController.showSlider
+ implicitWidth: 136
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+
+ enabled: !audioController.muted
+ value: 1
+ }
+ }
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/MetadataInfo.qml b/examples/multimedia/video/mediaplayer/controls/MetadataInfo.qml
new file mode 100644
index 000000000..3e68faa8e
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/MetadataInfo.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ property alias metadata: listModel
+ property alias count: listModel.count
+
+ function clear() {
+ listModel.clear()
+ }
+
+ //! [0]
+ function read(metadata) {
+ if (!metadata)
+ return
+ for (const key of metadata.keys())
+ if (metadata.stringValue(key))
+ listModel.append({
+ name: metadata.metaDataKeyToString(key),
+ value: metadata.stringValue(key)
+ })
+ }
+
+ ListModel {
+ id: listModel
+ }
+ //! [0]
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/PlaybackControl.qml b/examples/multimedia/video/mediaplayer/controls/PlaybackControl.qml
new file mode 100644
index 000000000..056cf50d0
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/PlaybackControl.qml
@@ -0,0 +1,318 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtMultimedia
+import QtQuick.Dialogs
+
+//! [0]
+Item {
+ id: playbackController
+
+ required property MediaPlayer mediaPlayer
+ required property MetadataInfo metadataInfo
+ required property TracksInfo audioTracksInfo
+ required property TracksInfo videoTracksInfo
+ required property TracksInfo subtitleTracksInfo
+ //! [0]
+
+ property alias muted: audioControl.muted
+ property alias volume: audioControl.volume
+
+ property bool landscapePlaybackControls: root.width >= 668
+ property bool busy: fileDialog.visible
+ || urlPopup.visible
+ || settingsPopup.visible
+ || audioControl.busy
+ || playbackSeekControl.busy
+ || !playbackController.mediaPlayer.playing
+
+ implicitHeight: landscapePlaybackControls ? 168 : 208
+
+ Behavior on opacity { NumberAnimation { duration: 300 } }
+
+ FileDialog {
+ id: fileDialog
+ title: "Please choose a file"
+ onAccepted: {
+ playbackController.mediaPlayer.stop()
+ playbackController.mediaPlayer.source = fileDialog.currentFile
+ playbackController.mediaPlayer.play()
+ }
+ }
+
+ UrlPopup {
+ id: urlPopup
+ anchors.centerIn: Overlay.overlay
+ mediaPlayer: playbackController.mediaPlayer
+ }
+
+ SettingsPopup {
+ id: settingsPopup
+ anchors.centerIn: Overlay.overlay
+
+ metadataInfo: playbackController.metadataInfo
+ mediaPlayer: playbackController.mediaPlayer
+ audioTracksInfo: playbackController.audioTracksInfo
+ videoTracksInfo: playbackController.videoTracksInfo
+ subtitleTracksInfo: playbackController.subtitleTracksInfo
+ }
+
+ component CustomButton: RoundButton {
+ implicitWidth: 40
+ implicitHeight: 40
+ radius: 4
+ icon.width: 24
+ icon.height: 24
+ flat: true
+ }
+
+ component CustomRoundButton: RoundButton {
+ property int diameter: 40
+ Layout.preferredWidth: diameter
+ Layout.preferredHeight: diameter
+ radius: diameter / 2
+ icon.width: 24
+ icon.height: 24
+ }
+
+ //! [1]
+ CustomButton {
+ id: fileDialogButton
+ icon.source: "../images/open_new.svg"
+ flat: false
+ onClicked: fileDialog.open()
+ }
+
+ CustomButton {
+ id: openUrlButton
+ icon.source: "../images/link.svg"
+ flat: false
+ onClicked: urlPopup.open()
+ }
+ //! [1]
+
+ CustomButton {
+ id: loopButton
+ icon.source: "../images/loop.svg"
+ icon.color: playbackController.mediaPlayer.loops === MediaPlayer.Once ? palette.buttonText : palette.accent
+ onClicked: playbackController.mediaPlayer.loops = playbackController.mediaPlayer.loops === MediaPlayer.Once
+ ? MediaPlayer.Infinite
+ : MediaPlayer.Once
+ }
+
+ CustomButton {
+ id: settingsButton
+ icon.source: "../images/more.svg"
+ onClicked: settingsPopup.open()
+ }
+
+ CustomButton {
+ id: fullScreenButton
+ icon.source: root.fullScreen ? "../images/zoom_minimize.svg"
+ : "../images/zoom_maximize.svg"
+ onClicked: {
+ root.fullScreen ? root.showNormal() : root.showFullScreen()
+ root.fullScreen = !root.fullScreen
+ }
+ }
+
+ RowLayout {
+ id: controlButtons
+ spacing: 16
+
+ CustomRoundButton {
+ id: backward10Button
+ icon.source: "../images/backward10.svg"
+ onClicked: {
+ const pos = Math.max(0, playbackController.mediaPlayer.position - 10000)
+ playbackController.mediaPlayer.setPosition(pos)
+ }
+ }
+
+ //! [2]
+ CustomRoundButton {
+ id: playButton
+ visible: playbackController.mediaPlayer.playbackState !== MediaPlayer.PlayingState
+ icon.source: "../images/play_symbol.svg"
+ onClicked: playbackController.mediaPlayer.play()
+ }
+
+ CustomRoundButton {
+ id: pauseButton
+ visible: playbackController.mediaPlayer.playbackState === MediaPlayer.PlayingState
+ icon.source: "../images/pause_symbol.svg"
+ onClicked: playbackController.mediaPlayer.pause()
+ }
+ //! [2]
+
+ //! [3]
+ CustomRoundButton {
+ id: forward10Button
+ icon.source: "../images/forward10.svg"
+ onClicked: {
+ const pos = Math.min(playbackController.mediaPlayer.duration,
+ playbackController.mediaPlayer.position + 10000)
+ playbackController.mediaPlayer.setPosition(pos)
+ }
+ }
+ //! [3]
+ } // RowLayout controlButtons
+
+ AudioControl {
+ id: audioControl
+ showSlider: root.width >= 960
+ }
+
+ PlaybackSeekControl {
+ id: playbackSeekControl
+ Layout.fillWidth: true
+ mediaPlayer: playbackController.mediaPlayer
+ }
+
+ Frame {
+ id: landscapeLayout
+ anchors.fill: parent
+ padding: 32
+ topPadding: 28
+ visible: landscapePlaybackControls
+ background: Rectangle {
+ color: "#F6F6F6"
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 16
+
+ Item {
+ Layout.fillWidth: true
+ implicitHeight: 40
+
+ LayoutItemProxy {
+ id: fdbProxy
+ target: fileDialogButton
+ anchors.left: parent.left
+ }
+
+ LayoutItemProxy {
+ target: openUrlButton
+ anchors.left: fdbProxy.right
+ anchors.leftMargin: 12
+ }
+
+ LayoutItemProxy {
+ target: controlButtons
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ LayoutItemProxy {
+ target: loopButton
+ anchors.right: acProxy.left
+ anchors.rightMargin: 12
+ }
+
+ LayoutItemProxy {
+ id: acProxy
+ target: audioControl
+ anchors.right: sbProxy.left
+ anchors.rightMargin: 30
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ LayoutItemProxy {
+ id: sbProxy
+ target: settingsButton
+ anchors.right: fbProxy.left
+ anchors.rightMargin: 12
+ }
+
+ LayoutItemProxy {
+ id: fbProxy
+ target: fullScreenButton
+ anchors.right: parent.right
+ }
+ } // Item
+
+ LayoutItemProxy {
+ target: playbackSeekControl
+ Layout.topMargin: 16
+ Layout.bottomMargin: 16
+ }
+ }
+ } // Frame frame
+
+ Frame {
+ id: portraitLayout
+ anchors.fill: parent
+ padding: 32
+ topPadding: 28
+ visible: !landscapePlaybackControls
+ background: Rectangle {
+ color: "#F6F6F6"
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 16
+
+ Item {
+ Layout.fillWidth: true
+ implicitHeight: 40
+
+ LayoutItemProxy {
+ target: loopButton
+ anchors.right: cbProxy.left
+ anchors.rightMargin: 16
+ }
+
+ LayoutItemProxy {
+ id: cbProxy
+ target: controlButtons
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ LayoutItemProxy {
+ target: audioControl
+ anchors.left: cbProxy.right
+ anchors.leftMargin: 16
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ implicitHeight: 40
+
+ LayoutItemProxy {
+ id: fdbProxy_
+ target: fileDialogButton
+ anchors.left: parent.left
+ }
+
+ LayoutItemProxy {
+ target: openUrlButton
+ anchors.left: fdbProxy_.right
+ anchors.leftMargin: 12
+ }
+
+ LayoutItemProxy {
+ target: settingsButton
+ anchors.right: fbProxy_.left
+ anchors.rightMargin: 12
+ }
+
+ LayoutItemProxy {
+ id: fbProxy_
+ target: fullScreenButton
+ anchors.right: parent.right
+ }
+ }
+
+ LayoutItemProxy {
+ target: playbackSeekControl
+ Layout.topMargin: 8
+ Layout.bottomMargin: 8
+ }
+ }
+ }
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/PlaybackSeekControl.qml b/examples/multimedia/video/mediaplayer/controls/PlaybackSeekControl.qml
new file mode 100644
index 000000000..c94d844f8
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/PlaybackSeekControl.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtMultimedia
+
+Item {
+ id: seekController
+ required property MediaPlayer mediaPlayer
+ property alias busy: slider.pressed
+
+ implicitHeight: 20
+
+ function formatToMinutes(milliseconds) {
+ const min = Math.floor(milliseconds / 60000)
+ const sec = ((milliseconds - min * 60000) / 1000).toFixed(1)
+ return `${min}:${sec.padStart(4, 0)}`
+ }
+
+ RowLayout {
+ anchors.fill: parent
+ spacing: 22
+
+ //! [0]
+ Text {
+ id: currentTime
+ Layout.preferredWidth: 45
+ text: seekController.formatToMinutes(seekController.mediaPlayer.position)
+ horizontalAlignment: Text.AlignLeft
+ font.pixelSize: 11
+ }
+ //! [0]
+
+ Slider {
+ id: slider
+ Layout.fillWidth: true
+ //! [2]
+ enabled: seekController.mediaPlayer.seekable
+ value: seekController.mediaPlayer.position / seekController.mediaPlayer.duration
+ //! [2]
+ onMoved: seekController.mediaPlayer.setPosition(value * seekController.mediaPlayer.duration)
+ }
+
+ //! [1]
+ Text {
+ id: remainingTime
+ Layout.preferredWidth: 45
+ text: seekController.formatToMinutes(seekController.mediaPlayer.duration - seekController.mediaPlayer.position)
+ horizontalAlignment: Text.AlignRight
+ font.pixelSize: 11
+ }
+ //! [1]
+ }
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/SettingsPopup.qml b/examples/multimedia/video/mediaplayer/controls/SettingsPopup.qml
new file mode 100644
index 000000000..0f24c8503
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/SettingsPopup.qml
@@ -0,0 +1,204 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtMultimedia
+
+Popup {
+ id: settingsController
+ focus: true
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
+
+ background: Rectangle {
+ color: "#F6F6F6"
+ }
+
+
+ required property MetadataInfo metadataInfo
+ required property MediaPlayer mediaPlayer
+ required property TracksInfo audioTracksInfo
+ required property TracksInfo videoTracksInfo
+ required property TracksInfo subtitleTracksInfo
+
+ property int vPadding: 20
+ property int hPadding: 26
+ property bool landscapeSettingsPopup: root.width >= settingsLayout.width + metadataLayout.width + 2 * hPadding + 20 + 24
+
+ padding: {
+ top: vPadding
+ bottom: vPadding
+ left: hPadding
+ right: hPadding
+ }
+
+ Flickable {
+ id: flickable
+ implicitWidth: mainLayout.width
+ implicitHeight: landscapeSettingsPopup ? 200 : 340
+ contentWidth: mainLayout.width
+ contentHeight: mainLayout.height
+ flickableDirection: Flickable.VerticalFlick
+ clip: true
+
+ GridLayout {
+ id: mainLayout
+
+ columns: landscapeSettingsPopup ? 2 : 1
+ columnSpacing: 24
+ rowSpacing: 24
+
+ ColumnLayout {
+ id: settingsLayout
+ spacing: 16
+ Layout.alignment: Qt.AlignTop
+
+ Label {
+ id: settingsLabel
+ text: qsTr("Settings")
+ font.pixelSize: 16
+ font.bold: true
+ }
+
+ GridLayout {
+ id: gridLayout
+ columns: 2
+ rowSpacing: 16
+ columnSpacing: 16
+
+ component CustomComboBox: ComboBox {
+ required property TracksInfo tracksInfo
+
+ model: tracksInfo.model
+ enabled: model.count > 0
+ textRole: "data"
+ currentIndex: model.count > 0 ? 0 : -1
+
+ onActivated: {
+ //! [1]
+ settingsController.mediaPlayer.pause()
+ tracksInfo.selectedTrack = currentIndex
+ settingsController.mediaPlayer.play()
+ //! [1]
+ }
+ }
+
+ Label {
+ text: qsTr("Playback Speed")
+ Layout.fillWidth: true
+ font.pixelSize: 14
+ }
+
+ ComboBox {
+ id: rateCb
+ model: ["0.25", "0.5", "0.75", "Normal", "1.25", "1.5", "1.75", "2"]
+ currentIndex: 3
+
+ onCurrentIndexChanged: {
+ //! [0]
+ settingsController.mediaPlayer.playbackRate = (currentIndex + 1) * 0.25
+ //! [0]
+ }
+ }
+
+ Label {
+ text: qsTr("Audio Tracks")
+ enabled: audioCb.enabled
+ Layout.fillWidth: true
+ font.pixelSize: 14
+ }
+
+ CustomComboBox {
+ id: audioCb
+ tracksInfo: settingsController.audioTracksInfo
+
+ }
+
+ Label {
+ text: qsTr("Video Tracks")
+ enabled: videoCb.enabled
+ Layout.fillWidth: true
+ font.pixelSize: 14
+ }
+
+ CustomComboBox {
+ id: videoCb
+ tracksInfo: settingsController.videoTracksInfo
+ }
+
+ Label {
+ text: qsTr("Subtitle Tracks")
+ enabled: subtitlesCb.enabled
+ font.pixelSize: 14
+ }
+
+ CustomComboBox {
+ id: subtitlesCb
+ tracksInfo: settingsController.subtitleTracksInfo
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: metadataLayout
+ spacing: 16
+ Layout.alignment: Qt.AlignTop
+
+ Label {
+ id: metadataLabel
+ text: qsTr("Metadata")
+ font.pixelSize: 16
+ font.bold: true
+ }
+
+ Rectangle {
+ id: metadataRect
+ implicitWidth: 240
+ implicitHeight: metadataList.height
+ border.color: "#8E8E93"
+ radius: 6
+
+ Column {
+ id: metadataList
+ visible: settingsController.metadataInfo.count > 0
+
+ padding: 10
+ Repeater {
+ Row {
+ spacing: metadataList.padding
+ Text {
+ text: model.name
+ font.bold: true
+ width: (metadataRect.width - 3 * metadataList.padding) / 2
+ horizontalAlignment: Text.AlignRight
+ wrapMode: Text.WordWrap
+ font.pixelSize: 12
+ }
+
+ Text {
+ text: model.value
+ width: (metadataRect.width - 3 * metadataList.padding) / 2
+ horizontalAlignment: Text.AlignLeft
+ anchors.verticalCenter: parent.verticalCenter
+ wrapMode: Text.WrapAnywhere
+ font.pixelSize: 12
+ }
+ }
+ model: settingsController.metadataInfo.metadata
+ }
+ }
+
+ Text {
+ id: metadataNoList
+ visible: settingsController.metadataInfo.count === 0
+ anchors.centerIn: parent
+ text: qsTr("No metadata present")
+ font.pixelSize: 14
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/TracksInfo.qml b/examples/multimedia/video/mediaplayer/controls/TracksInfo.qml
new file mode 100644
index 000000000..a2ae96f02
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/TracksInfo.qml
@@ -0,0 +1,25 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Item {
+ property alias model: model
+ property int selectedTrack: 0
+
+ function read(tracks, key = 0) {
+ // language is the 6th index in the enum QMediaMetaData::Key
+ model.clear()
+
+ if (!tracks)
+ return
+
+ tracks.forEach((metadata, index) => {
+ const data = metadata.stringValue(key)
+ const label = data ? data : qsTr("track ") + (index + 1)
+ model.append({data: label, index: index})
+ })
+ }
+
+ ListModel { id: model }
+}
diff --git a/examples/multimedia/video/mediaplayer/controls/UrlPopup.qml b/examples/multimedia/video/mediaplayer/controls/UrlPopup.qml
new file mode 100644
index 000000000..5bc304178
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/controls/UrlPopup.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtMultimedia
+
+
+Popup {
+ id: popupController
+ width: Math.min(500, root.width - 40)
+
+ required property MediaPlayer mediaPlayer
+
+ function loadUrl(url) {
+ popupController.mediaPlayer.stop()
+ popupController.mediaPlayer.source = url
+ popupController.mediaPlayer.play()
+ }
+
+ RowLayout {
+ id: rowOpenUrl
+ anchors.fill: parent
+ Label {
+ text: qsTr("URL:");
+ }
+
+ TextField {
+ id: urlText
+ Layout.fillWidth: true
+ focus: true
+
+ placeholderText: qsTr("Enter text here...")
+ wrapMode: TextInput.WrapAnywhere
+
+ Keys.onReturnPressed: {
+ popupController.loadUrl(text)
+ urlText.text = ""
+ popupController.close()
+ }
+ }
+
+ Button {
+ text: qsTr("Load")
+ enabled: urlText.text !== ""
+ onClicked: {
+ popupController.loadUrl(urlText.text)
+ urlText.text = ""
+ popupController.close()
+ }
+ }
+ }
+ onOpened: { popupController.forceActiveFocus() }
+}
diff --git a/examples/multimedia/video/mediaplayer/doc/images/OqosZsDqvzQ.jpg b/examples/multimedia/video/mediaplayer/doc/images/OqosZsDqvzQ.jpg
deleted file mode 100644
index a53e85803..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/OqosZsDqvzQ.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/PlayerMenuBar.gif b/examples/multimedia/video/mediaplayer/doc/images/PlayerMenuBar.gif
deleted file mode 100644
index 779713332..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/PlayerMenuBar.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/architecture-overview.gif b/examples/multimedia/video/mediaplayer/doc/images/architecture-overview.gif
deleted file mode 100644
index b85ce61c5..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/architecture-overview.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/audio-control.gif b/examples/multimedia/video/mediaplayer/doc/images/audio-control.gif
deleted file mode 100644
index 5ac07ccc2..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/audio-control.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/mediaplayer.png b/examples/multimedia/video/mediaplayer/doc/images/mediaplayer.png
new file mode 100644
index 000000000..857e70abd
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/doc/images/mediaplayer.png
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/meta-data.png b/examples/multimedia/video/mediaplayer/doc/images/meta-data.png
deleted file mode 100644
index a96b8f7e1..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/meta-data.png
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/nHrBbW0H-pc.jpg b/examples/multimedia/video/mediaplayer/doc/images/nHrBbW0H-pc.jpg
deleted file mode 100644
index 2d530a46d..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/nHrBbW0H-pc.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/play-pause-stop.gif b/examples/multimedia/video/mediaplayer/doc/images/play-pause-stop.gif
deleted file mode 100644
index 355f7af86..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/play-pause-stop.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/playbackControlPanel.gif b/examples/multimedia/video/mediaplayer/doc/images/playbackControlPanel.gif
deleted file mode 100644
index c71e577d7..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/playbackControlPanel.gif
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/qmlmediaplayer.jpg b/examples/multimedia/video/mediaplayer/doc/images/qmlmediaplayer.jpg
deleted file mode 100644
index 94e9c04a0..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/qmlmediaplayer.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/settings.png b/examples/multimedia/video/mediaplayer/doc/images/settings.png
new file mode 100644
index 000000000..5cf175795
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/doc/images/settings.png
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/sf_yv01UtIw.jpg b/examples/multimedia/video/mediaplayer/doc/images/sf_yv01UtIw.jpg
deleted file mode 100644
index ab7342d5d..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/sf_yv01UtIw.jpg
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/images/url.png b/examples/multimedia/video/mediaplayer/doc/images/url.png
deleted file mode 100644
index 7bd4ff9f4..000000000
--- a/examples/multimedia/video/mediaplayer/doc/images/url.png
+++ /dev/null
Binary files differ
diff --git a/examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf b/examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf
deleted file mode 100644
index bb1eab7da..000000000
--- a/examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf
+++ /dev/null
@@ -1,7 +0,0 @@
-HTML.extraimages += images/OqosZsDqvzQ.jpg \
- images/sf_yv01UtIw.jpg \
- images/nHrBbW0H-pc.jpg
-
-qhp.QtMultimedia.extraFiles += images/OqosZsDqvzQ.jpg \
- images/sf_yv01UtIw.jpg \
- images/nHrBbW0H-pc.jpg
diff --git a/examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc b/examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc
index 56cf91596..1cc681f8f 100644
--- a/examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc
+++ b/examples/multimedia/video/mediaplayer/doc/src/mediaplayer.qdoc
@@ -1,204 +1,151 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example multimedia/video/mediaplayer
+ \example video/mediaplayer
\title QML Media Player Example
\ingroup multimedia_examples
\ingroup video_examples_qml
- \brief Playing audio and video using Qt Quick.
+ \examplecategory {Graphics & Multimedia}
+ \brief Playing audio and video using the QML \c MediaPlayer type.
\meta {tag} {quick}
\meta {tag} {player}
- \image qmlmediaplayer.jpg
+ \image mediaplayer.png
This example demonstrates a simple multimedia player that can play
audio and video files using various codecs.
\include examples-run.qdocinc
- \section1 Overview
- At its core this is a QML application, see
- \l{Getting Started Programming with Qt Quick} for information specific to
- that. This documentation is focused on how this example utilizes the
- \l{Qt Multimedia QML types}.
-
- \image architecture-overview.gif
-
- \section1 Using MediaPlayer and VideoOutput
- In \c main.qml a MediaPlayer instance is connected to a VideoOutput to
- play back the video:
-
- \quotefromfile multimedia/video/mediaplayer/main.qml
- \skipto MediaPlayer
- \printuntil videoOutput: videoOutput
-
- \c videoOutput is declared like so:
-
- \skipto VideoOutput {
- \printto MetadataInfo {
-
- \section1 PlayerMenuBar
- \image PlayerMenuBar.gif
- This QML type handles media selection from a url or local file, exiting the
- application, viewing meta data, and the selection of available video, audio
- or subtitle tracks.
-
- \quotefromfile multimedia/video/mediaplayer/PlayerMenuBar.qml
- Accessing the mediaPlayer object is done through properties:
- \skipto required property
- \printuntil TracksInfo subtitleTracksInfo
-
- \section2 fileDialog
- A FileDialog, \c fileDialog, is created with an \c onAccepted function that
- will stop \c mediaPlayer, load the source by setting the
- \l{MediaPlayer::source}{source} property and then play it automatically:
- \skipto FileDialog
- \printto MenuBar
-
- This is triggered in the Menu \c File, which is a child of the MenuBar:
- \skipto MenuBar
- \printto }
-
- \section2 loadUrl
- \image url.png
- While \c urlPopup handles prompting and capturing a url, it is the \c loadUrl
- function that interacts with \c mediaPlayer like so:
- \quotefromfile multimedia/video/mediaplayer/PlayerMenuBar.qml
- \skipto function loadUrl
- \printto function closeOverlays(){
-
- \section2 Getting meta data
- \image meta-data.png
-
- In the declaration of \c mediaPlayer, in \c main.qml, there is the function
- \c updateMetadata():
-
- \quotefromfile multimedia/video/mediaplayer/main.qml
- \skipto function updateMetadata(
- \printto }
-
- It is called in the following places:
- \skipto onMetaDataChanged:
- \printto onActiveTracksChanged: { updateMetadata() }
-
- Reading MetaData is done by the \c MetadataInfo type's \c read() function
- \quotefromfile multimedia/video/mediaplayer/MetadataInfo.qml
- \skipto function read(metadata) {
- \printto ListModel
-
- The information is displayed via an \l[QML]{Overlay} item.
-
- \section2 Tracks information and control
- \youtube OqosZsDqvzQ
- This is defined in \c TracksInfo.qml and reading available tracks is done in
- a similar way to \c MetadataInfo:
- \quotefromfile multimedia/video/mediaplayer/TracksInfo.qml
- \skipto function read(metadata
- \printto ListModel
-
- To set a track, the property \c selectedTrack is set like so:
- \skipto ListView
- \printto Text
-
- The \c onSelectectedTrackChanged signal, in each relevant \c TracksInfo
- instance in \c main.qml, is what makes changes to \c mediaPlayer like so:
- \quotefromfile multimedia/video/mediaplayer/main.qml
- \skipto id: audioTracksInfo
- \printuntil audioTracksInfo.selectedTrack
-
- \section1 playbackControlPanel
- \image playbackControlPanel.gif
-
- This item has controls for \l{Playback control}, \l{Play Pause Stop},
- \l{Playback rate control} and \l{Playback seek control}.
-
- \section2 Playback control
- This qml type handles media playback and interacts with the MediaPlayer in
- \c main.qml.
-
- Here are the property definitions.
- \quotefromfile multimedia/video/mediaplayer/PlaybackControl.qml
- \skipto required property
- \printto property alias
-
- Connections:
- \skipuntil Connections
- \printto HoverHandler
-
- \section2 Play Pause Stop
- \image play-pause-stop.gif
- \l{MediaPlayer::play()}{Play}, \l{MediaPlayer::stop()}{stop} and
- \l{MediaPlayer::pause()}{pause} interactions with the MediaPlayer object
- are done like so:
- \skipto RoundButton
- \printto Item {
-
- Playback states done using \l{MediaPlayer::playbackState}{playbackstate}
- like so:
- \skipuntil states:
- \printuntil ]
-
-
- \section2 Playback seek control
-
- \youtube sf_yv01UtIw
-
- Defined in \c PlaybackSeekControl.qml, this component comprises of an item
- with a Text, \c mediaTime, and \l[QML]{Slider}, \c mediaSlider, in a RowLayout.
-
- \c mediaTime uses MediaPlayer's \l{MediaPlayer::position}{position} property
- like so:
- \quotefromfile multimedia/video/mediaplayer/PlaybackSeekControl.qml
- \skipto Text {
- \printto Slider
-
- \c mediaSlider uses the MediaPlayer \l{MediaPlayer::seekable}{seekable},
- \l{MediaPlayer::duration}{duration}, and \l{MediaPlayer::position}{position}
- properties like so:
- \skipto Slider
- \printuntil }
-
- \section2 Playback rate control
- \youtube nHrBbW0H-pc
-
- This type is defined in \c PlaybackRateControl.qml like so:
-
- \quotefromfile multimedia/video/mediaplayer/PlaybackRateControl.qml
- \skipto Slider
- \printuntil text:
+ \section1 Instantiating the MediaPlayer
+ The entry point for the QML code in this example is \c Main.qml. Here
+ an \c ApplicationWindow is created and properties such as the \c id, \c title,
+ \c width and \c height are set.
- \section2 Audio control
- \image audio-control.gif
- This type is defined in \c AudioControl.qml, and utilizes the
- \l{AudioOutput::muted}{muted} and \l{AudioOutput::volume}{volume} properties
- of the AudioOutput instantiated within the MediaPlayer, which is
- instantiated in \c{main.qml}.
-
- \quotefromfile multimedia/video/mediaplayer/AudioControl.qml
- \skipto required
- \printuntil value: 100.0
+ \snippet video/mediaplayer/Main.qml 0
+
+ Next the \c MediaPlayer is created and the two properties that are responsible for the video and
+ audio output are defined.
+ Firstly, \c videoOutput which renders the video viewfinder and secondly \c audioOutput which provides
+ the audio output for the player.
+
+ \snippet video/mediaplayer/Main.qml 1
+ \dots
+ \snippet video/mediaplayer/Main.qml 2
+ \dots
+ \snippet video/mediaplayer/Main.qml 3
+
+ The \c visible property of the \c VideoOutput type is set to \c true when the \c mediaStatus
+ property of the \c MediaPlayer is greater than 0. \c mediaStatus is of enumeration type
+ and is equal to 0 when \e {No media has been set}, and greater than 0 otherwise. Therefore,
+ the \c VideoOutput is visible when media has been set.
+
+ The \c MediaPlayer type has a signal property called \c onErrorOccurred that can be
+ overridden specifically to handle errors. In this case the signal opens a \c MessageDialog using
+ the method \c {open()} and sets its \c text property to a \c MediaPlayer property called
+ \c errorString.
+
+ \snippet video/mediaplayer/Main.qml 4
+
+ \section1 Playback Controls
+
+ In order to have a useable media player, there needs to be an interface to control the playback.
+ This is created in its own component file, \c PlaybackControl.qml, and instantiated in
+ \c Main.qml.
+
+ \snippet video/mediaplayer/Main.qml 5
+ \dots
+ \snippet video/mediaplayer/Main.qml 6
+
+ When created, objects are forwarded to this type such as track information,
+ metadata information and the \c MediaPlayer object itself. In \c PlaybackControl.qml,
+ each one of these objects have a \c {required property}, meaning that these properties must
+ be set when the \c PlaybackControl object is created.
+
+ \snippet video/mediaplayer/controls/PlaybackControl.qml 0
+
+ These playback controls can be broken down into sections. In the top left of the panel lies
+ a collection of buttons used to open a file, either by selecting a file from a file explorer or
+ entering a URL. The file is loaded into the \c MediaPlayer by setting the \c source property.
+ Both buttons are instantiated using a \c CustomButton \c {custom component}.
+
+ \snippet video/mediaplayer/controls/PlaybackControl.qml 1
+
+ Three buttons are created and centered on this panel, handling play, pause and seeking ten
+ seconds backwards or forwards. The media is played and paused using the methods \c {play()}
+ and \c {pause()}, respectively. To know when to draw a play or pause button, the \c playbackState
+ property is queried. For example, when it is equal to the enum value \c MediaPlayer.PlayingState
+ then the pause button is drawn.
+
+ \snippet video/mediaplayer/controls/PlaybackControl.qml 2
+
+ To navigate ten seconds forward or backwards, the \c position of the \c MediaPlayer type is
+ incremented by 10,000 milliseconds and set using the method \c {setPosition()}.
+
+ \snippet video/mediaplayer/controls/PlaybackControl.qml 3
+
+ \section1 Playback Seeking and Audio
+
+ In \c {PlaybackControl.qml}, an \c AudioControl and a \c PlaybackSeekControl type are instantiated.
+ These are both defined in their own component file and are responsible for volume control and
+ playback seeking, respectively. The \c AudioControl type defines a button to mute and a \c Slider,
+ from \c {QtQuick Controls}, to set the volume of the player. Both of these attributes are exposed by
+ defining a \c mute and \c volume property and are accessed from the \c AudioOutput definition in
+ \c {Main.qml}.
+
+ \snippet video/mediaplayer/controls/AudioControl.qml 0
+
+ The \c PlaybackSeekControl uses a \c RowLayout containing a \c Slider with a \c Text item either side.
+ The two \c Text items display the current time and the remaining time of the media being played. These
+ are both calculated using two properties of the \c MediaPlayer type, \c {position}, which gives the
+ current playback position in milliseconds, and \c {duration}, which gives the duration of the media
+ in milliseconds.
+
+ \snippet video/mediaplayer/controls/PlaybackSeekControl.qml 0
+ \dots
+ \snippet video/mediaplayer/controls/PlaybackSeekControl.qml 1
+
+ The \c Slider is only enabled when the media player is seekable and not, for example, live media.
+ The \c MediaPlayer type has a property for this called \c seekable. The \c value of the \c Slider
+ is calculated using the \c position and \c duration properties of the \c MediaPlayer.
+
+ \snippet video/mediaplayer/controls/PlaybackSeekControl.qml 2
+
+ \section1 Metadata and Track Information
+
+ The \c PlaybackControl type instantiates a \c SettingsPopup, which contains information about the
+ metadata of the currently loaded media and track selection, as well as the ability to update the
+ playback rate. This \c Popup is defined in \c SettingsPopup.qml.
+
+ \image settings.png
+
+ The metadata is contained in its own component file, \c MetadataInfo.qml. It contains a \c ListModel,
+ a function to clear it, \c {clear()}, and a function to populate it, \c {read(MediaMetadata metadata)}.
+ The \c {read(MediaMetadata metadata)} function takes as a parameter an object of type \c MediaMetaData,
+ and navigates its key-value structure to extract its data into the \c model of the \c {ListView}. The
+ methods used to do this are \c {keys()}, which returns all the keys of the \c MediaMetaData, and
+ {stringValue(Key key)}, which returns the \c value for a given \c key.
+
+ \snippet video/mediaplayer/controls/MetadataInfo.qml 0
+
+ The data is then displayed in \c {SettingsPopup.qml} in a \c ListView type. The \c delegate of this
+ \c ListView is a row of two \c Text items, corresponding to the key-value pairs abstracted from the
+ \c MediaMetaData item.
+
+ On the other side of the \c Popup there is playback rate controls and track selection for audio, video and
+ subtitles. The playback rate is chosen from a \c ComboBox and set using the property \c playbackRate.
+
+ \snippet video/mediaplayer/controls/SettingsPopup.qml 0
+
+ The type called \c TracksInfo, defined in \c {TracksInfo.qml}, contains the data about the tracks.
+ More specifically, a \c ListModel containing the titles of the tracks, or for subtitles specifically, the
+ langauges. This information is populated in \c Main.qml by calling the \c {read(MediaMetadata mediaMetadata)}
+ function defined in the \c TracksInfo type.
+
+ \snippet video/mediaplayer/Main.qml 6
+
+ The \c model defined in \c TracksInfo is then queried in the \c {ComboBox}es in the
+ \c SettingsPopup to select the current track.
+
+ \snippet video/mediaplayer/controls/SettingsPopup.qml 1
*/
diff --git a/examples/multimedia/video/mediaplayer/images/backward10.svg b/examples/multimedia/video/mediaplayer/images/backward10.svg
new file mode 100644
index 000000000..9166343bd
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/backward10.svg
@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4 13.2001C4 14.7428 4.46919 16.2508 5.34824 17.5335C6.22729 18.8162 7.47672 19.816 8.93853 20.4063C10.4003 20.9967 12.0089 21.1511 13.5607 20.8502C15.1126 20.5492 16.538 19.8063 17.6569 18.7155C18.7757 17.6247 19.5376 16.2348 19.8463 14.7218C20.155 13.2088 19.9965 11.6404 19.391 10.2152C18.7855 8.78993 17.7602 7.57175 16.4446 6.71468C15.129 5.85761 13.5822 5.40015 12 5.40015" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M12.6152 3L10.1537 5.39999L12.6152 7.79997" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9.16406 16V11.4766H9.09375L7.71094 12.4375V11.375L9.16797 10.3633H10.3438V16H9.16406ZM14.0469 16.1445C13.5833 16.1445 13.1836 16.0247 12.8477 15.7852C12.5143 15.543 12.2578 15.2005 12.0781 14.7578C11.8984 14.3151 11.8086 13.7904 11.8086 13.1836V13.1758C11.8086 12.5664 11.8984 12.0417 12.0781 11.6016C12.2578 11.1589 12.5143 10.8177 12.8477 10.5781C13.1836 10.3385 13.5833 10.2188 14.0469 10.2188C14.513 10.2188 14.9128 10.3385 15.2461 10.5781C15.5794 10.8177 15.8359 11.1589 16.0156 11.6016C16.1953 12.0417 16.2852 12.5664 16.2852 13.1758V13.1836C16.2852 13.7904 16.1953 14.3151 16.0156 14.7578C15.8359 15.2005 15.5794 15.543 15.2461 15.7852C14.9128 16.0247 14.513 16.1445 14.0469 16.1445ZM14.0469 15.2109C14.2708 15.2109 14.4596 15.1302 14.6133 14.9688C14.7695 14.8073 14.888 14.5755 14.9688 14.2734C15.0521 13.9714 15.0938 13.6081 15.0938 13.1836V13.1758C15.0938 12.7487 15.0521 12.3854 14.9688 12.0859C14.888 11.7839 14.7695 11.5534 14.6133 11.3945C14.4596 11.2331 14.2708 11.1523 14.0469 11.1523C13.8255 11.1523 13.6367 11.2331 13.4805 11.3945C13.3268 11.5534 13.2083 11.7839 13.125 12.0859C13.0443 12.3854 13.0039 12.7487 13.0039 13.1758V13.1836C13.0039 13.6081 13.0443 13.9714 13.125 14.2734C13.2083 14.5755 13.3268 14.8073 13.4805 14.9688C13.6367 15.1302 13.8255 15.2109 14.0469 15.2109Z" fill="black"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/ff.svg b/examples/multimedia/video/mediaplayer/images/ff.svg
new file mode 100644
index 000000000..7c907c4fa
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/ff.svg
@@ -0,0 +1,4 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.19465 16.8817C5.5198 17.0557 5.91435 17.0366 6.2212 16.832L12.2212 12.832C12.4994 12.6466 12.6665 12.3344 12.6665 12C12.6665 11.6656 12.4994 11.3534 12.2212 11.1679L6.2212 7.16795C5.91435 6.96338 5.5198 6.9443 5.19465 7.11832C4.86949 7.29234 4.6665 7.6312 4.6665 8V16C4.6665 16.3688 4.86949 16.7077 5.19465 16.8817ZM6.6665 14.1315V9.86852L9.86373 12L6.6665 14.1315Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1946 16.8817C13.5198 17.0557 13.9143 17.0366 14.2212 16.832L20.2212 12.832C20.4994 12.6466 20.6665 12.3344 20.6665 12C20.6665 11.6656 20.4994 11.3534 20.2212 11.1679L14.2212 7.16795C13.9143 6.96338 13.5198 6.9443 13.1946 7.11832C12.8695 7.29234 12.6665 7.6312 12.6665 8V16C12.6665 16.3688 12.8695 16.7077 13.1946 16.8817ZM14.6665 14.1315V9.86852L17.8637 12L14.6665 14.1315Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/forward10.svg b/examples/multimedia/video/mediaplayer/images/forward10.svg
new file mode 100644
index 000000000..190a5aaab
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/forward10.svg
@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M20 13.2001C20 14.7428 19.5308 16.2508 18.6518 17.5335C17.7727 18.8162 16.5233 19.816 15.0615 20.4063C13.5997 20.9967 11.9911 21.1511 10.4393 20.8502C8.88743 20.5492 7.46197 19.8063 6.34315 18.7155C5.22433 17.6247 4.4624 16.2348 4.15372 14.7218C3.84504 13.2088 4.00347 11.6404 4.60897 10.2152C5.21447 8.78993 6.23985 7.57175 7.55544 6.71468C8.87103 5.85761 10.4178 5.40015 12 5.40015" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M11.3838 3L13.8453 5.39999L11.3838 7.79997" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9.16406 16V11.4766H9.09375L7.71094 12.4375V11.375L9.16797 10.3633H10.3438V16H9.16406ZM14.0469 16.1445C13.5833 16.1445 13.1836 16.0247 12.8477 15.7852C12.5143 15.543 12.2578 15.2005 12.0781 14.7578C11.8984 14.3151 11.8086 13.7904 11.8086 13.1836V13.1758C11.8086 12.5664 11.8984 12.0417 12.0781 11.6016C12.2578 11.1589 12.5143 10.8177 12.8477 10.5781C13.1836 10.3385 13.5833 10.2188 14.0469 10.2188C14.513 10.2188 14.9128 10.3385 15.2461 10.5781C15.5794 10.8177 15.8359 11.1589 16.0156 11.6016C16.1953 12.0417 16.2852 12.5664 16.2852 13.1758V13.1836C16.2852 13.7904 16.1953 14.3151 16.0156 14.7578C15.8359 15.2005 15.5794 15.543 15.2461 15.7852C14.9128 16.0247 14.513 16.1445 14.0469 16.1445ZM14.0469 15.2109C14.2708 15.2109 14.4596 15.1302 14.6133 14.9688C14.7695 14.8073 14.888 14.5755 14.9688 14.2734C15.0521 13.9714 15.0938 13.6081 15.0938 13.1836V13.1758C15.0938 12.7487 15.0521 12.3854 14.9688 12.0859C14.888 11.7839 14.7695 11.5534 14.6133 11.3945C14.4596 11.2331 14.2708 11.1523 14.0469 11.1523C13.8255 11.1523 13.6367 11.2331 13.4805 11.3945C13.3268 11.5534 13.2083 11.7839 13.125 12.0859C13.0443 12.3854 13.0039 12.7487 13.0039 13.1758V13.1836C13.0039 13.6081 13.0443 13.9714 13.125 14.2734C13.2083 14.5755 13.3268 14.8073 13.4805 14.9688C13.6367 15.1302 13.8255 15.2109 14.0469 15.2109Z" fill="black"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/link.svg b/examples/multimedia/video/mediaplayer/images/link.svg
new file mode 100644
index 000000000..6ce1b983d
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/link.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11 16C11 16.5523 10.5523 17 10 17H7C5.61667 17 4.4375 16.5125 3.4625 15.5375C2.4875 14.5625 2 13.3833 2 12C2 10.6167 2.4875 9.4375 3.4625 8.4625C4.4375 7.4875 5.61667 7 7 7H10C10.5523 7 11 7.44772 11 8C11 8.55228 10.5523 9 10 9H7C6.16667 9 5.45833 9.29167 4.875 9.875C4.29167 10.4583 4 11.1667 4 12C4 12.8333 4.29167 13.5417 4.875 14.125C5.45833 14.7083 6.16667 15 7 15H10C10.5523 15 11 15.4477 11 16ZM9 13C8.44772 13 8 12.5523 8 12C8 11.4477 8.44772 11 9 11H15C15.5523 11 16 11.4477 16 12C16 12.5523 15.5523 13 15 13H9ZM14 17C13.4477 17 13 16.5523 13 16C13 15.4477 13.4477 15 14 15H17C17.8333 15 18.5417 14.7083 19.125 14.125C19.7083 13.5417 20 12.8333 20 12C20 11.1667 19.7083 10.4583 19.125 9.875C18.5417 9.29167 17.8333 9 17 9H14C13.4477 9 13 8.55228 13 8C13 7.44772 13.4477 7 14 7H17C18.3833 7 19.5625 7.4875 20.5375 8.4625C21.5125 9.4375 22 10.6167 22 12C22 13.3833 21.5125 14.5625 20.5375 15.5375C19.5625 16.5125 18.3833 17 17 17H14Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/loop.svg b/examples/multimedia/video/mediaplayer/images/loop.svg
new file mode 100644
index 000000000..8161ca867
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/loop.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7 17.5C5 17.5 7 17.5 5 17.5C3 17.5 3 17.5 3 15.5C3 13.5 3 8 3 6C3 4 3 4 5 4C7 4 17 4 19 4C21 4 21 4 21 6C21 8 21 13.5 21 15.5C21 17.5 21 17.5 19 17.5C17 17.5 11 17.5 11 17.5M11 17.5L13 15M11 17.5L13 20" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/more.svg b/examples/multimedia/video/mediaplayer/images/more.svg
new file mode 100644
index 000000000..fa8bddec8
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/more.svg
@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12Z" fill="black"/>
+<path d="M22 12C22 13.1046 21.1046 14 20 14C18.8954 14 18 13.1046 18 12C18 10.8954 18.8954 10 20 10C21.1046 10 22 10.8954 22 12Z" fill="black"/>
+<path d="M6 12C6 13.1046 5.10457 14 4 14C2.89543 14 2 13.1046 2 12C2 10.8954 2.89543 10 4 10C5.10457 10 6 10.8954 6 12Z" fill="black"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/Mute_Icon.svg b/examples/multimedia/video/mediaplayer/images/mute.svg
index b66b6d311..b66b6d311 100644
--- a/examples/multimedia/video/mediaplayer/Mute_Icon.svg
+++ b/examples/multimedia/video/mediaplayer/images/mute.svg
diff --git a/examples/multimedia/video/mediaplayer/images/open_new.svg b/examples/multimedia/video/mediaplayer/images/open_new.svg
new file mode 100644
index 000000000..6c62237f1
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/open_new.svg
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="File /open_new">
+<path id="Layer01" d="M2 5C2 3.34315 3.34315 2 5 2H11C11.5523 2 12 2.44772 12 3C12 3.55228 11.5523 4 11 4H5C4.44772 4 4 4.44772 4 5V19C4 19.5523 4.44772 20 5 20H19C19.5523 20 20 19.5523 20 19V13.125C20 12.5727 20.4477 12.125 21 12.125C21.5523 12.125 22 12.5727 22 13.125V19C22 20.6569 20.6569 22 19 22H5C3.34315 22 2 20.6569 2 19V5Z" fill="#0D0D0D"/>
+<path id="Layer02" fill-rule="evenodd" clip-rule="evenodd" d="M15 4C14.4477 4 14 3.55228 14 3C14 2.44772 14.4477 2 15 2H21C21.5523 2 22 2.44772 22 3V9C22 9.55228 21.5523 10 21 10C20.4477 10 20 9.55228 20 9V5.41421L8.76961 16.6446C8.37908 17.0351 7.74592 17.0351 7.35539 16.6446C6.96487 16.2541 6.96487 15.6209 7.35539 15.2304L18.5858 4H15Z" fill="#0D0D0D"/>
+</g>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/pause_symbol.svg b/examples/multimedia/video/mediaplayer/images/pause_symbol.svg
new file mode 100644
index 000000000..bdf6a9047
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/pause_symbol.svg
@@ -0,0 +1,4 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.3335 7C8.3335 6.44772 8.66928 6 9.0835 6H10.5835C10.9977 6 11.3335 6.44772 11.3335 7V17C11.3335 17.5523 10.9977 18 10.5835 18H9.0835C8.66928 18 8.3335 17.5523 8.3335 17V7Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.3335 7C13.3335 6.44772 13.6693 6 14.0835 6H15.5835C15.9977 6 16.3335 6.44772 16.3335 7V17C16.3335 17.5523 15.9977 18 15.5835 18H14.0835C13.6693 18 13.3335 17.5523 13.3335 17V7Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/play_symbol.svg b/examples/multimedia/video/mediaplayer/images/play_symbol.svg
new file mode 100644
index 000000000..edb567226
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/play_symbol.svg
@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.8182 12L6.89091 6.14985L6.89091 17.8501L16.8182 12ZM18.2 13.4143C19.2667 12.7857 19.2667 11.2143 18.2 10.5857L7.4 4.22123C6.33333 3.59264 5 4.37838 5 5.63555L5 18.3644C5 19.6216 6.33333 20.4074 7.4 19.7788L18.2 13.4143Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/rewind.svg b/examples/multimedia/video/mediaplayer/images/rewind.svg
new file mode 100644
index 000000000..e9b34122c
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/rewind.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.4719 16.8817C19.1467 17.0557 18.7522 17.0366 18.4453 16.832L12.4453 12.832C12.1671 12.6466 12 12.3344 12 12C12 11.6656 12.1671 11.3534 12.4453 11.1679L18.4453 7.16795C18.7522 6.96338 19.1467 6.9443 19.4719 7.11832C19.797 7.29234 20 7.6312 20 8V16C20 16.3688 19.797 16.7077 19.4719 16.8817ZM18 14.1315V9.86852L14.8028 12L18 14.1315Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.4719 16.8817C11.1467 17.0557 10.7522 17.0366 10.4453 16.832L4.4453 12.832C4.1671 12.6466 4 12.3344 4 12C4 11.6656 4.1671 11.3534 4.4453 11.1679L10.4453 7.16795C10.7522 6.96338 11.1467 6.9443 11.4719 7.11832C11.797 7.29234 12 7.6312 12 8V16C12 16.3688 11.797 16.7077 11.4719 16.8817ZM10 14.1315V9.86852L6.80278 12L10 14.1315Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/settings.svg b/examples/multimedia/video/mediaplayer/images/settings.svg
new file mode 100644
index 000000000..3a13ccae1
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/settings.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.84998 4C8.84998 2.89543 9.74541 2 10.85 2H13.1222C14.2268 2 15.1222 2.89543 15.1222 4V4.59509C15.1229 4.59732 15.125 4.6023 15.1306 4.61031C15.1434 4.62838 15.1691 4.65242 15.2077 4.66935C15.852 4.95163 16.4515 5.31609 16.9931 5.74921C17.0261 5.77554 17.0594 5.787 17.0815 5.78982C17.0912 5.79106 17.0966 5.79055 17.0989 5.79008L17.9363 5.33741C18.8691 4.8332 20.0332 5.14455 20.5897 6.04706L21.6744 7.80604C22.2777 8.78435 21.9342 10.0686 20.9231 10.6152L19.9875 11.1209C19.9859 11.1226 19.9824 11.1269 19.9781 11.1358C19.9683 11.156 19.9595 11.1904 19.9636 11.2327C19.9877 11.4855 20 11.7414 20 12C20 12.2586 19.9877 12.5145 19.9636 12.7673C19.9595 12.8096 19.9683 12.844 19.9781 12.8642C19.9824 12.8732 19.9859 12.8774 19.9875 12.8791L20.923 13.3848C21.9341 13.9314 22.2776 15.2157 21.6743 16.194L20.5897 17.9529C20.0331 18.8554 18.869 19.1668 17.9362 18.6626L17.0989 18.2099C17.0966 18.2095 17.0912 18.209 17.0815 18.2102C17.0594 18.213 17.026 18.2245 16.9931 18.2508C16.4515 18.6839 15.852 19.0484 15.2077 19.3306C15.1691 19.3476 15.1434 19.3716 15.1306 19.3897C15.125 19.3977 15.1229 19.4027 15.1222 19.4049V20C15.1222 21.1046 14.2268 22 13.1222 22H10.85C9.74541 22 8.84998 21.1046 8.84998 20V19.3928C8.84931 19.3906 8.84725 19.3857 8.84163 19.3777C8.82894 19.3597 8.80351 19.3357 8.76508 19.3187C8.12675 19.0362 7.53272 18.6729 6.99586 18.242C6.96292 18.2156 6.92951 18.2041 6.90737 18.2012C6.89759 18.2 6.8922 18.2005 6.8899 18.201L6.03592 18.6626C5.10317 19.1668 3.93904 18.8554 3.38251 17.9529L2.29784 16.194C1.69457 15.2156 2.03805 13.9314 3.04914 13.3848L4.01099 12.8649C4.01265 12.8632 4.01607 12.859 4.02042 12.85C4.03023 12.8299 4.03897 12.7956 4.03503 12.7534C4.0118 12.5051 3.99995 12.2538 3.99995 12C3.99995 11.7462 4.0118 11.4949 4.03503 11.2466C4.03897 11.2045 4.03022 11.1701 4.02042 11.15C4.01607 11.1411 4.01265 11.1368 4.01098 11.1352L3.04909 10.6152C2.038 10.0686 1.69452 8.78435 2.29779 7.80604L3.38245 6.04707C3.93898 5.14456 5.10312 4.83322 6.03587 5.33742L6.88988 5.79907C6.89218 5.79953 6.89757 5.80005 6.90735 5.79879C6.92949 5.79594 6.9629 5.78441 6.99584 5.75797C7.53271 5.32709 8.12674 4.96379 8.76508 4.68128C8.80351 4.66427 8.82894 4.64029 8.84163 4.62231C8.84725 4.61434 8.84931 4.60938 8.84998 4.60716V4ZM8.85024 4.60578C8.85029 4.60578 8.85026 4.60619 8.85001 4.60703L8.85024 4.60578ZM6.88852 5.79864C6.88854 5.7986 6.88898 5.79869 6.88977 5.79904L6.88852 5.79864ZM4.00988 11.1343C4.00991 11.1342 4.01026 11.1344 4.0109 11.1351L4.00988 11.1343ZM4.00989 12.8658C4.00988 12.8657 4.01021 12.8654 4.01091 12.865L4.00989 12.8658ZM6.88854 18.2014C6.88851 18.2013 6.8889 18.2012 6.88979 18.201L6.88854 18.2014ZM19.9886 12.88C19.9885 12.88 19.9881 12.8797 19.9876 12.8792L19.9886 12.88ZM19.9886 11.12C19.9887 11.12 19.9883 11.1204 19.9876 11.1208L19.9886 11.12ZM17.1003 5.78965C17.1003 5.78968 17.0999 5.78986 17.099 5.79006L17.1003 5.78965ZM15.122 4.5937C15.122 4.59371 15.1221 4.59414 15.1222 4.59496L15.122 4.5937ZM13.1222 4L10.85 4V4.60808C10.85 5.49562 10.2774 6.19908 9.57449 6.51017C9.09654 6.7217 8.65096 6.99408 8.24769 7.31774C7.63839 7.80676 6.73261 7.98755 5.93958 7.55888L5.08481 7.09682L4.00015 8.8558L4.9628 9.37617C5.7538 9.80375 6.09888 10.6574 6.02633 11.4329C6.00889 11.6193 5.99995 11.8085 5.99995 12C5.99995 12.1915 6.00889 12.3807 6.02633 12.5671C6.09889 13.3426 5.75381 14.1963 4.9628 14.6239L4.0002 15.1442L5.08486 16.9032L5.9396 16.4411C6.73263 16.0125 7.63841 16.1933 8.2477 16.6823C8.65097 17.0059 9.09654 17.2783 9.57449 17.4898C10.2774 17.8009 10.85 18.5044 10.85 19.3919V20H13.1222V19.404C13.1222 18.5133 13.6987 17.8083 14.4051 17.4988C14.8875 17.2874 15.3372 17.0142 15.744 16.6889C16.3532 16.2017 17.2575 16.0222 18.0492 16.4501L18.8873 16.9032L19.972 15.1442L19.0357 14.6381C18.2434 14.2098 17.8986 13.3541 17.9726 12.5776C17.9907 12.3878 18 12.1951 18 12C18 11.8049 17.9907 11.6123 17.9726 11.4224C17.8986 10.6459 18.2434 9.79023 19.0357 9.36192L19.972 8.85579L18.8874 7.09681L18.0492 7.54989C17.2575 7.97786 16.3532 7.79836 15.744 7.31116C15.3372 6.98582 14.8875 6.71259 14.4051 6.50124C13.6987 6.19173 13.1222 5.48675 13.1222 4.596L13.1222 4Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/Speaker_Icon.svg b/examples/multimedia/video/mediaplayer/images/speaker.svg
index 723e48b66..723e48b66 100644
--- a/examples/multimedia/video/mediaplayer/Speaker_Icon.svg
+++ b/examples/multimedia/video/mediaplayer/images/speaker.svg
diff --git a/examples/multimedia/video/mediaplayer/images/stop_symbol.svg b/examples/multimedia/video/mediaplayer/images/stop_symbol.svg
new file mode 100644
index 000000000..99f00162f
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/stop_symbol.svg
@@ -0,0 +1,3 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.6665 7H7.6665L7.6665 17H17.6665V7ZM7.6665 5C6.56193 5 5.6665 5.89543 5.6665 7V17C5.6665 18.1046 6.56193 19 7.6665 19H17.6665C18.7711 19 19.6665 18.1046 19.6665 17V7C19.6665 5.89543 18.7711 5 17.6665 5H7.6665Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/url.svg b/examples/multimedia/video/mediaplayer/images/url.svg
new file mode 100644
index 000000000..44942911f
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/url.svg
@@ -0,0 +1,6 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7 10C7 10.5523 6.55228 11 6 11C5.44772 11 5 10.5523 5 10C5 9.44772 5.44772 9 6 9C6.55228 9 7 9.44772 7 10Z" fill="black"/>
+<path d="M7 14C7 14.5523 6.55228 15 6 15C5.44772 15 5 14.5523 5 14C5 13.4477 5.44772 13 6 13C6.55228 13 7 13.4477 7 14Z" fill="black"/>
+<path d="M13 6L9 18" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M18 6L14 18" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/volume.svg b/examples/multimedia/video/mediaplayer/images/volume.svg
new file mode 100644
index 000000000..2cdc4df6b
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/volume.svg
@@ -0,0 +1,4 @@
+<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.98759 5.1163C11.1732 3.91192 13.2226 4.75139 13.2226 6.44139V17.5587C13.2226 19.2487 11.1732 20.0882 9.98759 18.8838L6.74406 15.5888C6.72318 15.5675 6.69465 15.5556 6.66488 15.5556H4.33371C3.29051 15.5556 2.44482 14.7099 2.44482 13.6667L2.44482 10.3334C2.44482 9.29016 3.29051 8.44448 4.33371 8.44448H6.66488C6.69465 8.44448 6.72318 8.43253 6.74406 8.41131L9.98759 5.1163ZM11.3101 6.32964C11.2992 6.3316 11.2809 6.33666 11.2545 6.36344L8.011 9.65845C7.65595 10.0191 7.171 10.2223 6.66488 10.2223H4.33371C4.27235 10.2223 4.2226 10.272 4.2226 10.3334L4.2226 13.6667C4.2226 13.7281 4.27235 13.7778 4.33371 13.7778H6.66488C7.171 13.7778 7.65595 13.9809 8.011 14.3416L11.2545 17.6366C11.2809 17.6634 11.2992 17.6685 11.3101 17.6704C11.326 17.6733 11.3496 17.6723 11.3758 17.6615C11.4021 17.6507 11.4196 17.6349 11.4289 17.6218C11.4353 17.6127 11.4448 17.5963 11.4448 17.5587V6.44139C11.4448 6.40381 11.4353 6.38737 11.4289 6.37829C11.4196 6.36513 11.4021 6.34932 11.3758 6.33857C11.3496 6.32781 11.326 6.3268 11.3101 6.32964Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.0329 4.37143C18.6857 4.0243 18.1229 4.0243 17.7758 4.37143C17.4287 4.71857 17.4287 5.28138 17.7758 5.62851C21.3339 9.18662 21.3339 14.9555 17.7758 18.5136C17.4287 18.8607 17.4287 19.4235 17.7758 19.7706C18.1229 20.1178 18.6857 20.1178 19.0329 19.7706C23.2852 15.5183 23.2852 8.62381 19.0329 4.37143ZM16.2044 7.37146C15.8573 7.02433 15.2945 7.02433 14.9474 7.37146C14.6002 7.71859 14.6002 8.28141 14.9474 8.62854C16.9434 10.6246 16.9434 13.8607 14.9474 15.8567C14.6002 16.2039 14.6002 16.7667 14.9474 17.1138C15.2945 17.461 15.8573 17.461 16.2044 17.1138C18.8947 14.4235 18.8947 10.0617 16.2044 7.37146Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/volume_mute.svg b/examples/multimedia/video/mediaplayer/images/volume_mute.svg
new file mode 100644
index 000000000..635c308e9
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/volume_mute.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.6541 5.1163C10.8397 3.91192 12.8891 4.75139 12.8891 6.44139V17.5587C12.8891 19.2487 10.8397 20.0882 9.6541 18.8838L6.41057 15.5888C6.38968 15.5675 6.36116 15.5556 6.33138 15.5556H4.00022C2.95701 15.5556 2.11133 14.7099 2.11133 13.6667L2.11133 10.3334C2.11133 9.29016 2.95701 8.44448 4.00022 8.44448H6.33138C6.36116 8.44448 6.38968 8.43253 6.41057 8.41131L9.6541 5.1163ZM10.9766 6.32964C10.9657 6.3316 10.9474 6.33666 10.921 6.36344L7.67751 9.65845C7.32245 10.0191 6.8375 10.2223 6.33138 10.2223H4.00022C3.93885 10.2223 3.88911 10.272 3.88911 10.3334L3.88911 13.6667C3.88911 13.7281 3.93885 13.7778 4.00022 13.7778H6.33138C6.8375 13.7778 7.32245 13.9809 7.67751 14.3416L10.921 17.6366C10.9474 17.6634 10.9657 17.6685 10.9766 17.6704C10.9925 17.6733 11.0161 17.6723 11.0423 17.6615C11.0686 17.6507 11.0861 17.6349 11.0954 17.6218C11.1018 17.6127 11.1113 17.5963 11.1113 17.5587V6.44139C11.1113 6.40381 11.1018 6.38737 11.0954 6.37829C11.0861 6.36513 11.0686 6.34932 11.0423 6.33857C11.0161 6.32781 10.9925 6.3268 10.9766 6.32964Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.7071 15.7071C15.3166 16.0976 14.6834 16.0976 14.2929 15.7071C13.9024 15.3166 13.9024 14.6834 14.2929 14.2929L16.5858 12L14.2929 9.70711C13.9024 9.31658 13.9024 8.68342 14.2929 8.29289C14.6834 7.90237 15.3166 7.90237 15.7071 8.29289L18 10.5858L20.2929 8.29289C20.6834 7.90237 21.3166 7.90237 21.7071 8.29289C22.0976 8.68342 22.0976 9.31658 21.7071 9.70711L19.4142 12L21.7071 14.2929C22.0976 14.6834 22.0976 15.3166 21.7071 15.7071C21.3166 16.0976 20.6834 16.0976 20.2929 15.7071L18 13.4142L15.7071 15.7071Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/zoom_maximize.svg b/examples/multimedia/video/mediaplayer/images/zoom_maximize.svg
new file mode 100644
index 000000000..9c7fb8ebb
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/zoom_maximize.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M19.071 3.92892C19.6233 3.92892 20.071 4.37664 20.071 4.92892V9.17157C20.071 9.72385 19.6233 10.1716 19.071 10.1716C18.5188 10.1716 18.071 9.72385 18.071 9.17157V7.34314L16.1716 9.24263C15.781 9.63315 15.1479 9.63315 14.7573 9.24263C14.3668 8.8521 14.3668 8.21894 14.7573 7.82841L16.6568 5.92892L14.8284 5.92892C14.2761 5.92892 13.8284 5.48121 13.8284 4.92892C13.8284 4.37664 14.2761 3.92892 14.8284 3.92892H19.071ZM4.92892 13.8284C5.48121 13.8284 5.92892 14.2761 5.92892 14.8284V16.6569L8.29289 14.2929C8.68342 13.9024 9.31658 13.9024 9.70711 14.2929C10.0976 14.6834 10.0976 15.3166 9.70711 15.7071L7.34314 18.0711L9.17157 18.0711C9.72385 18.0711 10.1716 18.5188 10.1716 19.0711C10.1716 19.6234 9.72385 20.0711 9.17157 20.0711H4.92892C4.37664 20.0711 3.92892 19.6234 3.92892 19.0711V14.8284C3.92892 14.2761 4.37664 13.8284 4.92892 13.8284Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1716 4.92892C10.1716 5.48121 9.72388 5.92892 9.1716 5.92892L7.34316 5.92892L9.24265 7.82841C9.63318 8.21894 9.63318 8.8521 9.24265 9.24263C8.85213 9.63315 8.21896 9.63315 7.82844 9.24263L5.92896 7.34314V9.17157C5.92896 9.72385 5.48124 10.1716 4.92896 10.1716C4.37667 10.1716 3.92896 9.72385 3.92896 9.17157V4.92892C3.92896 4.37664 4.37667 3.92892 4.92896 3.92892H9.1716C9.72388 3.92892 10.1716 4.37664 10.1716 4.92892ZM19.0711 13.8284C19.6234 13.8284 20.0711 14.2761 20.0711 14.8284V19.0711C20.0711 19.6234 19.6234 20.0711 19.0711 20.0711H14.8284C14.2761 20.0711 13.8284 19.6234 13.8284 19.0711C13.8284 18.5188 14.2761 18.0711 14.8284 18.0711H16.6569L14.2929 15.7071C13.9024 15.3166 13.9024 14.6834 14.2929 14.2929C14.6834 13.9024 15.3166 13.9024 15.7071 14.2929L18.0711 16.6569V14.8284C18.0711 14.2761 18.5188 13.8284 19.0711 13.8284Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/images/zoom_minimize.svg b/examples/multimedia/video/mediaplayer/images/zoom_minimize.svg
new file mode 100644
index 000000000..3d6ae65aa
--- /dev/null
+++ b/examples/multimedia/video/mediaplayer/images/zoom_minimize.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.1213 10C14.502 10 14 9.49797 14 8.87868L14 4.12132C14 3.50203 14.502 3 15.1213 3C15.7406 3 16.2426 3.50203 16.2426 4.12132V6.17158L18.3726 4.04163C18.8105 3.60372 19.5205 3.60372 19.9584 4.04163C20.3963 4.47953 20.3963 5.18951 19.9584 5.62741L17.8284 7.75736L19.8787 7.75736C20.498 7.75736 21 8.25939 21 8.87868C21 9.49797 20.498 10 19.8787 10L15.1213 10ZM8.87868 21C8.25939 21 7.75736 20.498 7.75736 19.8787V17.8284L5.10659 20.4792C4.66869 20.9171 3.95871 20.9171 3.52081 20.4792C3.0829 20.0413 3.0829 19.3313 3.52081 18.8934L6.17157 16.2426H4.12132C3.50203 16.2426 3 15.7406 3 15.1213C3 14.502 3.50203 14 4.12132 14L8.87868 14C9.49797 14 10 14.502 10 15.1213L10 19.8787C10 20.498 9.49797 21 8.87868 21Z" fill="#0D0D0D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3.00002 8.87867C3.00002 8.25939 3.50205 7.75735 4.12134 7.75735H6.1716L4.04166 5.62741C3.60375 5.1895 3.60375 4.47952 4.04166 4.04162C4.47956 3.60372 5.18954 3.60372 5.62744 4.04162L7.75737 6.17155V4.12131C7.75737 3.50203 8.25941 2.99999 8.87869 2.99999C9.49798 2.99999 10 3.50203 10 4.12131L10 8.87867C10 9.49796 9.49798 9.99999 8.87869 9.99999L4.12134 9.99999C3.50205 9.99999 3.00002 9.49796 3.00002 8.87867ZM15.1213 21C14.502 21 14 20.498 14 19.8787V15.1213C14 14.502 14.502 14 15.1213 14L19.8787 14C20.498 14 21 14.502 21 15.1213C21 15.7406 20.498 16.2426 19.8787 16.2426H17.8284L20.4792 18.8934C20.9171 19.3313 20.9171 20.0413 20.4792 20.4792C20.0413 20.9171 19.3313 20.9171 18.8934 20.4792L16.2426 17.8284V19.8787C16.2426 20.498 15.7406 21 15.1213 21Z" fill="#0D0D0D"/>
+</svg>
diff --git a/examples/multimedia/video/mediaplayer/main.cpp b/examples/multimedia/video/mediaplayer/main.cpp
index 957083235..3df4d6027 100644
--- a/examples/multimedia/video/mediaplayer/main.cpp
+++ b/examples/multimedia/video/mediaplayer/main.cpp
@@ -1,57 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
#include <QCommandLineParser>
#include <QDir>
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
@@ -68,17 +21,18 @@ int main(int argc, char *argv[])
parser.process(app);
QQmlApplicationEngine engine;
- const QUrl url(QStringLiteral("qrc:/main.qml"));
+ const QUrl url(QStringLiteral("qrc:/Main.qml"));
if (!parser.positionalArguments().isEmpty()) {
QUrl source = QUrl::fromUserInput(parser.positionalArguments().at(0), QDir::currentPath());
- QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, [source](QObject *object, const QUrl &){
- qDebug() << "setting source";
- object->setProperty("source", source);
- });
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ &engine, [source](QObject *object, const QUrl &) {
+ qDebug() << "setting source";
+ object->setProperty("source", source);
+ });
}
- engine.load(url);
+ engine.loadFromModule("mediaplayer", "Main");
return app.exec();
}
diff --git a/examples/multimedia/video/mediaplayer/main.qml b/examples/multimedia/video/mediaplayer/main.qml
deleted file mode 100644
index ea31ba2ed..000000000
--- a/examples/multimedia/video/mediaplayer/main.qml
+++ /dev/null
@@ -1,200 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtMultimedia
-
-
-Window {
- id: root
- width: 640
- height: 480
- visible: true
- title: qsTr("Multimedia Player")
- property alias source: mediaPlayer.source
-
- Popup {
- id: mediaError
- anchors.centerIn: Overlay.overlay
- Text {
- id: mediaErrorText
- }
- }
-
- MediaPlayer {
- id: mediaPlayer
-
- function updateMetadata() {
- metadataInfo.clear();
- metadataInfo.read(mediaPlayer.metaData);
- metadataInfo.read(mediaPlayer.audioTracks[mediaPlayer.activeAudioTrack]);
- metadataInfo.read(mediaPlayer.videoTracks[mediaPlayer.activeVideoTrack]);
- }
-
- videoOutput: videoOutput
- audioOutput: AudioOutput {
- id: audio
- muted: playbackControl.muted
- volume: playbackControl.volume
- }
-
- onErrorOccurred: { mediaErrorText.text = mediaPlayer.errorString; mediaError.open() }
- onMetaDataChanged: { updateMetadata() }
- onTracksChanged: {
- audioTracksInfo.read(mediaPlayer.audioTracks);
- audioTracksInfo.selectedTrack = mediaPlayer.activeAudioTrack;
- videoTracksInfo.read(mediaPlayer.videoTracks);
- videoTracksInfo.selectedTrack = mediaPlayer.activeVideoTrack;
- subtitleTracksInfo.read(mediaPlayer.subtitleTracks);
- subtitleTracksInfo.selectedTrack = mediaPlayer.activeSubtitleTrack;
- updateMetadata()
- }
- onActiveTracksChanged: { updateMetadata() }
- }
-
- PlayerMenuBar {
- id: menuBar
-
- anchors.left: parent.left
- anchors.right: parent.right
-
- visible: !videoOutput.fullScreen
-
- mediaPlayer: mediaPlayer
- videoOutput: videoOutput
- metadataInfo: metadataInfo
- audioTracksInfo: audioTracksInfo
- videoTracksInfo: videoTracksInfo
- subtitleTracksInfo: subtitleTracksInfo
-
- onClosePlayer: root.close()
- }
-
-
- VideoOutput {
- id: videoOutput
-
- property bool fullScreen: false
-
- anchors.top: fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.top
- anchors.left: parent.left
- anchors.right: parent.right
-
- TapHandler {
- onDoubleTapped: {
- parent.fullScreen ? showNormal() : showFullScreen()
- parent.fullScreen = !parent.fullScreen
- }
- onTapped: {
- metadataInfo.visible = false
- audioTracksInfo.visible = false
- videoTracksInfo.visible = false
- subtitleTracksInfo.visible = false
- }
- }
- }
-
- MetadataInfo {
- id: metadataInfo
-
- anchors.right: parent.right
- anchors.top: videoOutput.fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.opacity ? playbackControl.bottom : parent.bottom
-
- visible: false
- }
-
- TracksInfo {
- id: audioTracksInfo
-
- anchors.right: parent.right
- anchors.top: videoOutput.fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.opacity ? playbackControl.bottom : parent.bottom
-
- visible: false
- onSelectedTrackChanged: mediaPlayer.activeAudioTrack = audioTracksInfo.selectedTrack
- }
-
- TracksInfo {
- id: videoTracksInfo
-
- anchors.right: parent.right
- anchors.top: videoOutput.fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.opacity ? playbackControl.bottom : parent.bottom
-
- visible: false
- onSelectedTrackChanged: mediaPlayer.activeVideoTrack = videoTracksInfo.selectedTrack
- }
-
- TracksInfo {
- id: subtitleTracksInfo
-
- anchors.right: parent.right
- anchors.top: videoOutput.fullScreen ? parent.top : menuBar.bottom
- anchors.bottom: playbackControl.opacity ? playbackControl.bottom : parent.bottom
-
- visible: false
- onSelectedTrackChanged: mediaPlayer.activeSubtitleTrack = subtitleTracksInfo.selectedTrack
- }
-
- PlaybackControl {
- id: playbackControl
-
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
-
- mediaPlayer: mediaPlayer
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/CMakeLists.txt b/examples/multimedia/video/qmlvideo/CMakeLists.txt
index 906ba61cf..43975d584 100644
--- a/examples/multimedia/video/qmlvideo/CMakeLists.txt
+++ b/examples/multimedia/video/qmlvideo/CMakeLists.txt
@@ -1,5 +1,8 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
-project(qmlvideo LANGUAGES CXX)
+project(qmlvideoexample LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
@@ -11,7 +14,9 @@ set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/video/qmlvideo")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia Qml Quick)
-qt_add_executable(qmlvideo
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(qmlvideoexample
frequencymonitor.cpp frequencymonitor.h
frequencymonitordeclarative.cpp
performancemonitor.cpp performancemonitor.h
@@ -20,98 +25,33 @@ qt_add_executable(qmlvideo
trace.h
)
-set_target_properties(qmlvideo PROPERTIES
+set_target_properties(qmlvideoexample PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
)
-target_compile_definitions(qmlvideo PUBLIC
+target_compile_definitions(qmlvideoexample PUBLIC
FREQUENCYMONITOR_SUPPORT
PERFORMANCEMONITOR_SUPPORT
)
-target_link_libraries(qmlvideo PUBLIC
+add_subdirectory(qmlvideo)
+add_subdirectory(performancemonitor)
+add_subdirectory(frequencymonitor)
+
+target_link_libraries(qmlvideoexample PRIVATE
Qt::Core
Qt::Gui
Qt::Multimedia
Qt::Qml
Qt::Quick
+ qmlvideo
+ performancemonitor
+ frequencymonitor
)
-# Resources:
-set_source_files_properties("images/folder.png"
- PROPERTIES QT_RESOURCE_ALIAS "folder.png"
-)
-
-set_source_files_properties("images/leaves.jpg"
- PROPERTIES QT_RESOURCE_ALIAS "leaves.jpg"
-)
-
-set_source_files_properties("images/up.png"
- PROPERTIES QT_RESOURCE_ALIAS "up.png"
-)
-
-set(qmlvideo_resource_files
- "images/folder.png"
- "images/leaves.jpg"
- "images/up.png"
- "qml/frequencymonitor/FrequencyItem.qml"
- "qml/performancemonitor/PerformanceItem.qml"
- "qml/qmlvideo/Button.qml"
- "qml/qmlvideo/CameraBasic.qml"
- "qml/qmlvideo/CameraDrag.qml"
- "qml/qmlvideo/CameraDummy.qml"
- "qml/qmlvideo/CameraFullScreen.qml"
- "qml/qmlvideo/CameraFullScreenInverted.qml"
- "qml/qmlvideo/CameraItem.qml"
- "qml/qmlvideo/CameraMove.qml"
- "qml/qmlvideo/CameraOverlay.qml"
- "qml/qmlvideo/CameraResize.qml"
- "qml/qmlvideo/CameraRotate.qml"
- "qml/qmlvideo/CameraSpin.qml"
- "qml/qmlvideo/Content.qml"
- "qml/qmlvideo/ErrorDialog.qml"
- "qml/qmlvideo/FileBrowser.qml"
- "qml/qmlvideo/Scene.qml"
- "qml/qmlvideo/SceneBasic.qml"
- "qml/qmlvideo/SceneDrag.qml"
- "qml/qmlvideo/SceneFullScreen.qml"
- "qml/qmlvideo/SceneFullScreenInverted.qml"
- "qml/qmlvideo/SceneMove.qml"
- "qml/qmlvideo/SceneMulti.qml"
- "qml/qmlvideo/SceneOverlay.qml"
- "qml/qmlvideo/SceneResize.qml"
- "qml/qmlvideo/SceneRotate.qml"
- "qml/qmlvideo/SceneSelectionPanel.qml"
- "qml/qmlvideo/SceneSpin.qml"
- "qml/qmlvideo/SeekControl.qml"
- "qml/qmlvideo/VideoBasic.qml"
- "qml/qmlvideo/VideoDrag.qml"
- "qml/qmlvideo/VideoDummy.qml"
- "qml/qmlvideo/VideoFillMode.qml"
- "qml/qmlvideo/VideoFullScreen.qml"
- "qml/qmlvideo/VideoFullScreenInverted.qml"
- "qml/qmlvideo/VideoItem.qml"
- "qml/qmlvideo/VideoMetadata.qml"
- "qml/qmlvideo/VideoMove.qml"
- "qml/qmlvideo/VideoOverlay.qml"
- "qml/qmlvideo/VideoPlaybackRate.qml"
- "qml/qmlvideo/VideoResize.qml"
- "qml/qmlvideo/VideoRotate.qml"
- "qml/qmlvideo/VideoSeek.qml"
- "qml/qmlvideo/VideoSpin.qml"
- "qml/qmlvideo/main.qml"
-)
-
-qt_add_resources(qmlvideo "qmlvideo"
- PREFIX
- "/"
- FILES
- ${qmlvideo_resource_files}
-)
-
-install(TARGETS qmlvideo
+install(TARGETS qmlvideoexample
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
diff --git a/examples/multimedia/video/doc/images/qmlvideo-menu.jpg b/examples/multimedia/video/qmlvideo/doc/images/qmlvideo-menu.jpg
index 54ab877a1..54ab877a1 100644
--- a/examples/multimedia/video/doc/images/qmlvideo-menu.jpg
+++ b/examples/multimedia/video/qmlvideo/doc/images/qmlvideo-menu.jpg
Binary files differ
diff --git a/examples/multimedia/video/doc/images/qmlvideo-overlay.jpg b/examples/multimedia/video/qmlvideo/doc/images/qmlvideo-overlay.jpg
index 6a0d48ae6..6a0d48ae6 100644
--- a/examples/multimedia/video/doc/images/qmlvideo-overlay.jpg
+++ b/examples/multimedia/video/qmlvideo/doc/images/qmlvideo-overlay.jpg
Binary files differ
diff --git a/examples/multimedia/video/doc/src/qmlvideo.qdoc b/examples/multimedia/video/qmlvideo/doc/src/qmlvideo.qdoc
index b8ab25d03..81f7cf05c 100644
--- a/examples/multimedia/video/doc/src/qmlvideo.qdoc
+++ b/examples/multimedia/video/qmlvideo/doc/src/qmlvideo.qdoc
@@ -1,34 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
-\example multimedia/video/qmlvideo
+\example video/qmlvideo
\title QML Video Example
\ingroup multimedia_examples
+\examplecategory {Multimedia}
\brief Transforming video and camera viewfinder content.
\e{QML Video} demonstrates the various transformations (move; resize; rotate;
@@ -50,11 +27,11 @@ which moves across the \l{VideoOutput} item.
\section1 Application Structure
-The \c main.qml file creates a UI which includes the following items:
+The \c Main.qml file creates a UI which includes the following items:
\list
\li Two \c Button instances, each of which displays a filename, and can be
- used to launch a \c FileBrowser.
+ used to launch a \c FileDialog.
\li An exit \c Button.
\li A \c SceneSelectionPanel, which is a flickable list displaying the
available scenes.
@@ -71,13 +48,13 @@ in the center of the screen) is implemented in the \c VideoBasic.qml file. As
you can see from the code, this makes use of a type of inheritance; a
\c VideoBasic item ...
-\quotefromfile multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml
-\skipto import
+\quotefromfile video/qmlvideo/qmlvideo/VideoBasic.qml
+\skipto SceneBasic
\printuntil /^\}/
... is of type \c SceneBasic ...
-\quotefromfile multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml
+\quotefromfile video/qmlvideo/qmlvideo/SceneBasic.qml
\skipto import
\printuntil contentType
\dots
@@ -89,7 +66,7 @@ you can see from the code, this makes use of a type of inheritance; a
... which itself is a \c Scene:
-\quotefromfile multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml
+\quotefromfile video/qmlvideo/qmlvideo/Scene.qml
\skipto import
\printuntil root
\dots
@@ -122,8 +99,11 @@ All that remains is to connect the afterRendering() signal of the QQuickView
object to a JavaScript function, which will eventually call
\c frequencyItem.notify():
-\quotefromfile multimedia/video/qmlvideo/main.cpp
-\skipto QGuiApplication
+\quotefromfile video/qmlvideo/main.cpp
+\skipto int main(
+\printuntil ;
+\dots
+\skipto QQuickView
\printuntil ;
\dots
\skipto QQuickItem
diff --git a/examples/multimedia/video/qmlvideo/frequencymonitor.cpp b/examples/multimedia/video/qmlvideo/frequencymonitor.cpp
index 5f48cd35e..d6f1953f6 100644
--- a/examples/multimedia/video/qmlvideo/frequencymonitor.cpp
+++ b/examples/multimedia/video/qmlvideo/frequencymonitor.cpp
@@ -1,54 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "frequencymonitor.h"
+
#include <QDebug>
#include <QElapsedTimer>
#include <QString>
@@ -57,11 +11,20 @@
//#define VERBOSE_TRACE
-inline QDebug qtTrace() { return qDebug() << "[frequencymonitor]"; }
+inline QDebug qtTrace()
+{
+ return qDebug() << "[frequencymonitor]";
+}
#ifdef VERBOSE_TRACE
-inline QDebug qtVerboseTrace() { return qtTrace(); }
+inline QDebug qtVerboseTrace()
+{
+ return qtTrace();
+}
#else
-inline QNoDebug qtVerboseTrace() { return QNoDebug(); }
+inline QNoDebug qtVerboseTrace()
+{
+ return QNoDebug();
+}
#endif
static const int DefaultSamplingInterval = 100;
@@ -94,29 +57,27 @@ public:
};
FrequencyMonitorPrivate::FrequencyMonitorPrivate(FrequencyMonitor *parent)
-: QObject(parent)
-, q_ptr(parent)
-, m_active(false)
-, m_instantaneousFrequency(0)
-, m_averageTimer(new QTimer(this))
-, m_count(0)
-, m_averageFrequency(0)
-, m_traceTimer(new QTimer(this))
-, m_stalledTimer(new QTimer(this))
+ : QObject(parent),
+ q_ptr(parent),
+ m_active(false),
+ m_instantaneousFrequency(0),
+ m_averageTimer(new QTimer(this)),
+ m_count(0),
+ m_averageFrequency(0),
+ m_traceTimer(new QTimer(this)),
+ m_stalledTimer(new QTimer(this))
{
m_instantaneousElapsed.start();
- connect(m_averageTimer, &QTimer::timeout,
- this, &FrequencyMonitorPrivate::calculateAverageFrequency);
+ connect(m_averageTimer, &QTimer::timeout, this,
+ &FrequencyMonitorPrivate::calculateAverageFrequency);
if (DefaultSamplingInterval)
m_averageTimer->start(DefaultSamplingInterval);
m_averageElapsed.start();
- connect(m_traceTimer, &QTimer::timeout,
- q_ptr, &FrequencyMonitor::trace);
+ connect(m_traceTimer, &QTimer::timeout, q_ptr, &FrequencyMonitor::trace);
if (DefaultTraceInterval)
m_traceTimer->start(DefaultTraceInterval);
m_stalledTimer->setSingleShot(true);
- connect(m_stalledTimer, &QTimer::timeout,
- this, &FrequencyMonitorPrivate::stalled);
+ connect(m_stalledTimer, &QTimer::timeout, this, &FrequencyMonitorPrivate::stalled);
}
void FrequencyMonitorPrivate::calculateInstantaneousFrequency()
@@ -148,8 +109,7 @@ void FrequencyMonitorPrivate::stalled()
}
}
-FrequencyMonitor::FrequencyMonitor(QObject *parent)
-: QObject(parent)
+FrequencyMonitor::FrequencyMonitor(QObject *parent) : QObject(parent)
{
d_ptr = new FrequencyMonitorPrivate(this);
}
@@ -196,13 +156,14 @@ void FrequencyMonitor::notify()
void FrequencyMonitor::trace()
{
Q_D(FrequencyMonitor);
- const QString value = QString::fromLatin1("instant %1 average %2")
- .arg(d->m_instantaneousFrequency, 0, 'f', 2)
- .arg(d->m_averageFrequency, 0, 'f', 2);
+ const QString value = QStringLiteral("instant %1 average %2")
+ .arg(d->m_instantaneousFrequency, 0, 'f', 2)
+ .arg(d->m_averageFrequency, 0, 'f', 2);
if (d->m_label.isEmpty())
qtTrace() << "FrequencyMonitor::trace" << value;
else
- qtTrace() << "FrequencyMonitor::trace" << "label" << d->m_label << value;
+ qtTrace() << "FrequencyMonitor::trace"
+ << "label" << d->m_label << value;
}
void FrequencyMonitor::setLabel(const QString &value)
@@ -252,3 +213,4 @@ void FrequencyMonitor::setTraceInterval(int value)
}
#include "frequencymonitor.moc"
+#include "moc_frequencymonitor.cpp"
diff --git a/examples/multimedia/video/qmlvideo/frequencymonitor.h b/examples/multimedia/video/qmlvideo/frequencymonitor.h
index efeb78589..a1756dc88 100644
--- a/examples/multimedia/video/qmlvideo/frequencymonitor.h
+++ b/examples/multimedia/video/qmlvideo/frequencymonitor.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef FREQUENCYMONITOR_H
#define FREQUENCYMONITOR_H
@@ -69,13 +22,16 @@ class FrequencyMonitor : public QObject
Q_DECLARE_PRIVATE(FrequencyMonitor)
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
- Q_PROPERTY(int samplingInterval READ samplingInterval WRITE setSamplingInterval NOTIFY samplingIntervalChanged)
- Q_PROPERTY(int traceInterval READ traceInterval WRITE setTraceInterval NOTIFY traceIntervalChanged)
- Q_PROPERTY(qreal instantaneousFrequency READ instantaneousFrequency NOTIFY instantaneousFrequencyChanged)
+ Q_PROPERTY(int samplingInterval READ samplingInterval WRITE setSamplingInterval NOTIFY
+ samplingIntervalChanged)
+ Q_PROPERTY(
+ int traceInterval READ traceInterval WRITE setTraceInterval NOTIFY traceIntervalChanged)
+ Q_PROPERTY(qreal instantaneousFrequency READ instantaneousFrequency NOTIFY
+ instantaneousFrequencyChanged)
Q_PROPERTY(qreal averageFrequency READ averageFrequency NOTIFY averageFrequencyChanged)
public:
- FrequencyMonitor(QObject *parent = 0);
+ FrequencyMonitor(QObject *parent = nullptr);
~FrequencyMonitor();
static void qmlRegisterType();
diff --git a/examples/multimedia/video/qmlvideo/frequencymonitor/CMakeLists.txt b/examples/multimedia/video/qmlvideo/frequencymonitor/CMakeLists.txt
new file mode 100644
index 000000000..4a205fa28
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/frequencymonitor/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ message(FATAL_ERROR "This module is part of the 'qmlvideo' example, and should not be built independently.")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/video/qmlvideo/frequencymonitor")
+
+qt_add_qml_module(frequencymonitor
+ URI frequencymonitor
+ QML_FILES
+ "FrequencyItem.qml"
+)
+
+target_link_libraries(frequencymonitor PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+)
+
+install(TARGETS frequencymonitor
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
+ DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/multimedia/video/qmlvideo/frequencymonitor/FrequencyItem.qml b/examples/multimedia/video/qmlvideo/frequencymonitor/FrequencyItem.qml
new file mode 100644
index 000000000..49a7eea9b
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/frequencymonitor/FrequencyItem.qml
@@ -0,0 +1,67 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import FrequencyMonitor 1.0
+
+Rectangle {
+ id: root
+ property bool logging: true
+ property bool displayed: true
+ property bool enabled: logging || displayed
+ property alias active: monitor.active
+ property int samplingInterval: 500
+ property color textColor: "yellow"
+ property int textSize: 20
+ property alias label: monitor.label
+
+ border.width: 1
+ border.color: "yellow"
+ width: 5.5 * root.textSize
+ height: 3.0 * root.textSize
+ color: "black"
+ opacity: 0.5
+ radius: 10
+ visible: displayed && active
+
+ // This should ensure that the monitor is on top of all other content
+ z: 999
+
+ function notify() {
+ monitor.notify()
+ }
+
+ FrequencyMonitor {
+ id: monitor
+ samplingInterval: root.enabled ? root.samplingInterval : 0
+ onAverageFrequencyChanged: {
+ if (root.logging) trace()
+ averageFrequencyText.text = monitor.averageFrequency.toFixed(2)
+ }
+ }
+
+ Text {
+ id: labelText
+ anchors {
+ left: parent.left
+ top: parent.top
+ margins: 10
+ }
+ color: root.textColor
+ font.pixelSize: 0.6 * root.textSize
+ text: root.label
+ width: root.width - 2*anchors.margins
+ elide: Text.ElideRight
+ }
+
+ Text {
+ id: averageFrequencyText
+ anchors {
+ right: parent.right
+ bottom: parent.bottom
+ margins: 10
+ }
+ color: root.textColor
+ font.pixelSize: root.textSize
+ }
+}
diff --git a/examples/multimedia/video/qmlvideo/frequencymonitor/qmldir b/examples/multimedia/video/qmlvideo/frequencymonitor/qmldir
new file mode 100644
index 000000000..a935d2c53
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/frequencymonitor/qmldir
@@ -0,0 +1,3 @@
+module frequencymonitor
+
+FrequencyItem 1.0 FrequencyItem.qml
diff --git a/examples/multimedia/video/qmlvideo/frequencymonitordeclarative.cpp b/examples/multimedia/video/qmlvideo/frequencymonitordeclarative.cpp
index 0994ae213..6ed2019ff 100644
--- a/examples/multimedia/video/qmlvideo/frequencymonitordeclarative.cpp
+++ b/examples/multimedia/video/qmlvideo/frequencymonitordeclarative.cpp
@@ -1,55 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "frequencymonitor.h"
-#include <QtQml/qqml.h>
+#include <qqml.h>
void FrequencyMonitor::qmlRegisterType()
{
diff --git a/examples/multimedia/video/qmlvideo/main.cpp b/examples/multimedia/video/qmlvideo/main.cpp
index e288c0ae4..94c5bfea0 100644
--- a/examples/multimedia/video/qmlvideo/main.cpp
+++ b/examples/multimedia/video/qmlvideo/main.cpp
@@ -1,65 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QtCore/QStandardPaths>
-#include <QtCore/QString>
-#include <QtCore/QStringList>
-#include <QtQml/QQmlContext>
-#include <QtQml/QQmlEngine>
-#include <QtGui/QGuiApplication>
-#include <QtQuick/QQuickItem>
-#include <QtQuick/QQuickView>
+#include "performancemonitor.h"
#include "trace.h"
-
+#include "qmlvideo/videosingleton.h"
#ifdef PERFORMANCEMONITOR_SUPPORT
-#include "performancemonitordeclarative.h"
+# include "performancemonitordeclarative.h"
+#endif
+
+#include <QGuiApplication>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQuickItem>
+#include <QQuickView>
+#include <QStandardPaths>
+#include <QString>
+#include <QStringList>
+
+#if QT_CONFIG(permissions)
+ #include <QPermission>
#endif
static const QString DefaultFileName1 = "";
@@ -84,7 +43,7 @@ int main(int argc, char *argv[])
const QByteArray arg = args.at(i).toUtf8();
if (arg.startsWith('-')) {
if ("-volume" == arg) {
- if (i+1 < args.count())
+ if (i + 1 < args.count())
volume = 0.01 * args.at(++i).toInt();
else
qtTrace() << "Option \"-volume\" takes a value";
@@ -120,36 +79,47 @@ int main(int argc, char *argv[])
url2 = QUrl::fromLocalFile(source2);
}
+ const QStringList moviesLocation = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
+ const QUrl videoPath = QUrl::fromLocalFile(moviesLocation.isEmpty() ? app.applicationDirPath()
+ : moviesLocation.front());
+
QQuickView viewer;
- viewer.setSource(QUrl("qrc:///qml/qmlvideo/main.qml"));
+ VideoSingleton* singleton = viewer.engine()->singletonInstance<VideoSingleton*>("qmlvideo", "VideoSingleton");
+ singleton->setVideoPath(videoPath);
+ singleton->setSource1(source1);
+ singleton->setSource2(source2);
+ singleton->setVolume(volume);
+ viewer.loadFromModule("qmlvideo", "Main");
QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QQuickView::close);
- QQuickItem *rootObject = viewer.rootObject();
- rootObject->setProperty("source1", url1);
- rootObject->setProperty("source2", url2);
- rootObject->setProperty("volume", volume);
-
#ifdef PERFORMANCEMONITOR_SUPPORT
+ QQuickItem *rootObject = viewer.rootObject();
if (performanceMonitorState.valid) {
rootObject->setProperty("perfMonitorsLogging", performanceMonitorState.logging);
rootObject->setProperty("perfMonitorsVisible", performanceMonitorState.visible);
}
- QObject::connect(&viewer, SIGNAL(afterRendering()),
- rootObject, SLOT(qmlFramePainted()));
+ QObject::connect(&viewer, SIGNAL(afterRendering()), rootObject, SLOT(qmlFramePainted()));
#endif
- const QStringList moviesLocation = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
- const QUrl videoPath =
- QUrl::fromLocalFile(moviesLocation.isEmpty() ?
- app.applicationDirPath() :
- moviesLocation.front());
- viewer.rootContext()->setContextProperty("videoPath", videoPath);
-
QMetaObject::invokeMethod(rootObject, "init");
- viewer.setMinimumSize(QSize(640, 360));
- viewer.show();
+ auto setupView = [&viewer]() {
+ viewer.setMinimumSize(QSize(640, 360));
+ viewer.show();
+ };
+
+#if QT_CONFIG(permissions)
+ QCameraPermission cameraPermission;
+ qApp->requestPermission(cameraPermission, [&setupView](const QPermission &permission) {
+ // Show UI in any case. If there is no permission, the UI will just
+ // be disabled.
+ if (permission.status() != Qt::PermissionStatus::Granted)
+ qWarning("Camera permission is not granted! Camera will not be available.");
+ setupView();
+ });
+#else
+ setupView();
+#endif
return app.exec();
}
-
diff --git a/examples/multimedia/video/qmlvideo/performancemonitor.cpp b/examples/multimedia/video/qmlvideo/performancemonitor.cpp
index 204eabc5a..26f0fae2a 100644
--- a/examples/multimedia/video/qmlvideo/performancemonitor.cpp
+++ b/examples/multimedia/video/qmlvideo/performancemonitor.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "performancemonitor.h"
diff --git a/examples/multimedia/video/qmlvideo/performancemonitor.h b/examples/multimedia/video/qmlvideo/performancemonitor.h
index 97b76caf3..683468619 100644
--- a/examples/multimedia/video/qmlvideo/performancemonitor.h
+++ b/examples/multimedia/video/qmlvideo/performancemonitor.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef PERFORMANCEMONITOR_H
#define PERFORMANCEMONITOR_H
@@ -60,9 +13,13 @@ struct State
State() : valid(true), logging(false), visible(true) { }
State(bool l, bool v) : valid(true), logging(l), visible(v) { }
bool operator==(const State &other) const
- { return logging == other.logging && visible == other.visible; }
+ {
+ return logging == other.logging && visible == other.visible;
+ }
bool operator!=(const State &other) const
- { return logging != other.logging || visible != other.visible; }
+ {
+ return logging != other.logging || visible != other.visible;
+ }
bool parseArgument(const QByteArray &arg);
@@ -74,4 +31,3 @@ struct State
} // namespace PerformanceMonitor
#endif // PERFORMANCEMONITOR_H
-
diff --git a/examples/multimedia/video/qmlvideo/performancemonitor/CMakeLists.txt b/examples/multimedia/video/qmlvideo/performancemonitor/CMakeLists.txt
new file mode 100644
index 000000000..beb9e2e08
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/performancemonitor/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ message(FATAL_ERROR "This module is part of the 'qmlvideo' example, and should not be built independently.")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/video/qmlvideo/performancemonitor")
+
+qt_add_qml_module(performancemonitor
+ URI performancemonitor
+ QML_FILES
+ "PerformanceItem.qml"
+)
+
+target_link_libraries(performancemonitor PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+)
+
+install(TARGETS performancemonitor
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
+ DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+
diff --git a/examples/multimedia/video/qmlvideo/performancemonitor/PerformanceItem.qml b/examples/multimedia/video/qmlvideo/performancemonitor/PerformanceItem.qml
new file mode 100644
index 000000000..5b501b104
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/performancemonitor/PerformanceItem.qml
@@ -0,0 +1,100 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: root
+ property bool logging: true
+ property bool displayed: true
+ property bool videoActive
+ property int margins: 5
+ property bool enabled: true
+
+ color: "transparent"
+
+ // This should ensure that the monitor is on top of all other content
+ z: 999
+
+ Column {
+ id: column
+ anchors {
+ fill: root
+ margins: 10
+ }
+ spacing: 10
+ }
+
+ QtObject {
+ id: d
+ property Item qmlFrameRateItem: null
+ property Item videoFrameRateItem: null
+ }
+
+ Connections {
+ id: videoFrameRateActiveConnections
+ ignoreUnknownSignals: true
+ function onActiveChanged() { root.videoActive = videoFrameRateActiveConnections.target.active }
+ }
+
+ states: [
+ State {
+ name: "hidden"
+ PropertyChanges {
+ root.opacity: 0
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "*"
+ to: "*"
+ NumberAnimation {
+ properties: "opacity"
+ easing.type: Easing.OutQuart
+ duration: 500
+ }
+ }
+ ]
+
+ state: enabled ? "baseState" : "hidden"
+
+ function createQmlFrameRateItem() {
+ let component = Qt.createComponent("frequencymonitor", "FrequencyItem")
+ if (component.status === Component.Ready)
+ d.qmlFrameRateItem = component.createObject(column, { label: qsTr("QML frame rate"),
+ displayed: root.displayed,
+ logging: root.logging
+ });
+ }
+
+ function createVideoFrameRateItem() {
+ let component = Qt.createComponent("frequencymonitor", "FrequencyItem")
+ if (component.status === Component.Ready)
+ d.videoFrameRateItem = component.createObject(column, { label: qsTr("Video frame rate"),
+ displayed: root.displayed,
+ logging: root.logging
+ });
+ videoFrameRateActiveConnections.target = d.videoFrameRateItem
+ }
+
+
+ function init() {
+ createQmlFrameRateItem()
+ createVideoFrameRateItem()
+ }
+
+ function videoFramePainted() {
+ d.videoFrameRateItem?.notify()
+ }
+
+ function qmlFramePainted() {
+ d.qmlFrameRateItem?.notify()
+ }
+
+ onVideoActiveChanged: {
+ if (d.videoFrameRateItem)
+ d.videoFrameRateItem.active = root.videoActive
+ }
+}
diff --git a/examples/multimedia/video/qmlvideo/performancemonitor/qmldir b/examples/multimedia/video/qmlvideo/performancemonitor/qmldir
new file mode 100644
index 000000000..636abaf99
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/performancemonitor/qmldir
@@ -0,0 +1,3 @@
+module performancemonitor
+
+PerformanceItem 1.0 PerformanceItem.qml
diff --git a/examples/multimedia/video/qmlvideo/performancemonitordeclarative.cpp b/examples/multimedia/video/qmlvideo/performancemonitordeclarative.cpp
index 0783fdbd9..a2ad1ca82 100644
--- a/examples/multimedia/video/qmlvideo/performancemonitordeclarative.cpp
+++ b/examples/multimedia/video/qmlvideo/performancemonitordeclarative.cpp
@@ -1,60 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "frequencymonitor.h"
-#include "performancemonitor.h"
namespace PerformanceMonitor {
- void qmlRegisterTypes()
- {
- FrequencyMonitor::qmlRegisterType();
- }
+void qmlRegisterTypes()
+{
+ FrequencyMonitor::qmlRegisterType();
+}
}
diff --git a/examples/multimedia/video/qmlvideo/performancemonitordeclarative.h b/examples/multimedia/video/qmlvideo/performancemonitordeclarative.h
index ca70657ce..4514d5041 100644
--- a/examples/multimedia/video/qmlvideo/performancemonitordeclarative.h
+++ b/examples/multimedia/video/qmlvideo/performancemonitordeclarative.h
@@ -1,60 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef PERFORMANCEMONITORDECLARATIVE_H
#define PERFORMANCEMONITORDECLARATIVE_H
-#include "performancemonitor.h"
-
namespace PerformanceMonitor {
- void qmlRegisterTypes();
+void qmlRegisterTypes();
}
#endif // PERFORMANCEMONITORDECLARATIVE_H
diff --git a/examples/multimedia/video/qmlvideo/qml/frequencymonitor/FrequencyItem.qml b/examples/multimedia/video/qmlvideo/qml/frequencymonitor/FrequencyItem.qml
deleted file mode 100644
index ec1bab299..000000000
--- a/examples/multimedia/video/qmlvideo/qml/frequencymonitor/FrequencyItem.qml
+++ /dev/null
@@ -1,114 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import FrequencyMonitor 1.0
-
-Rectangle {
- id: root
- property bool logging: true
- property bool displayed: true
- property bool enabled: logging || displayed
- property alias active: monitor.active
- property int samplingInterval: 500
- property color textColor: "yellow"
- property int textSize: 20
- property alias label: monitor.label
-
- border.width: 1
- border.color: "yellow"
- width: 5.5 * root.textSize
- height: 3.0 * root.textSize
- color: "black"
- opacity: 0.5
- radius: 10
- visible: displayed && active
-
- // This should ensure that the monitor is on top of all other content
- z: 999
-
- function notify() {
- monitor.notify()
- }
-
- FrequencyMonitor {
- id: monitor
- samplingInterval: root.enabled ? root.samplingInterval : 0
- onAverageFrequencyChanged: {
- if (root.logging) trace()
- averageFrequencyText.text = monitor.averageFrequency.toFixed(2)
- }
- }
-
- Text {
- id: labelText
- anchors {
- left: parent.left
- top: parent.top
- margins: 10
- }
- color: root.textColor
- font.pixelSize: 0.6 * root.textSize
- text: root.label
- width: root.width - 2*anchors.margins
- elide: Text.ElideRight
- }
-
- Text {
- id: averageFrequencyText
- anchors {
- right: parent.right
- bottom: parent.bottom
- margins: 10
- }
- color: root.textColor
- font.pixelSize: root.textSize
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/performancemonitor/PerformanceItem.qml b/examples/multimedia/video/qmlvideo/qml/performancemonitor/PerformanceItem.qml
deleted file mode 100644
index 5621450cb..000000000
--- a/examples/multimedia/video/qmlvideo/qml/performancemonitor/PerformanceItem.qml
+++ /dev/null
@@ -1,150 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Rectangle {
- id: root
- property bool logging: true
- property bool displayed: true
- property bool videoActive
- property int margins: 5
- property bool enabled: true
-
- color: "transparent"
-
- // This should ensure that the monitor is on top of all other content
- z: 999
-
- Column {
- id: column
- anchors {
- fill: root
- margins: 10
- }
- spacing: 10
- }
-
- QtObject {
- id: d
- property Item qmlFrameRateItem: null
- property Item videoFrameRateItem: null
- }
-
- Connections {
- id: videoFrameRateActiveConnections
- ignoreUnknownSignals: true
- function onActiveChanged() { root.videoActive = videoFrameRateActiveConnections.target.active }
- }
-
- states: [
- State {
- name: "hidden"
- PropertyChanges {
- target: root
- opacity: 0
- }
- }
- ]
-
- transitions: [
- Transition {
- from: "*"
- to: "*"
- NumberAnimation {
- properties: "opacity"
- easing.type: Easing.OutQuart
- duration: 500
- }
- }
- ]
-
- state: enabled ? "baseState" : "hidden"
-
- function createQmlFrameRateItem() {
- var component = Qt.createComponent("../frequencymonitor/FrequencyItem.qml")
- if (component.status == Component.Ready)
- d.qmlFrameRateItem = component.createObject(column, { label: "QML frame rate",
- displayed: root.displayed,
- logging: root.logging
- })
- }
-
- function createVideoFrameRateItem() {
- var component = Qt.createComponent("../frequencymonitor/FrequencyItem.qml")
- if (component.status == Component.Ready)
- d.videoFrameRateItem = component.createObject(column, { label: "Video frame rate",
- displayed: root.displayed,
- logging: root.logging
- })
- videoFrameRateActiveConnections.target = d.videoFrameRateItem
- }
-
-
- function init() {
- createQmlFrameRateItem()
- createVideoFrameRateItem()
- }
-
- function videoFramePainted() {
- if (d.videoFrameRateItem)
- d.videoFrameRateItem.notify()
- }
-
- function qmlFramePainted() {
- if (d.qmlFrameRateItem)
- d.qmlFrameRateItem.notify()
- }
-
- onVideoActiveChanged: {
- if (d.videoFrameRateItem)
- d.videoFrameRateItem.active = root.videoActive
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Button.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Button.qml
deleted file mode 100644
index b06db6119..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Button.qml
+++ /dev/null
@@ -1,90 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Item {
- id: root
-
- property string text
- property color bgColor: "#757575"
- property color bgColorSelected: "#bdbdbd"
- property color textColor: "white"
- property color textColorSelected: "black"
- property alias enabled: mouseArea.enabled
- property alias radius: bgr.radius
-
- signal clicked
-
- Rectangle {
- id: bgr
- anchors.fill: parent
- color: mouseArea.pressed ? bgColorSelected : bgColor
- radius: height / 15
-
- Text {
- id: text
- anchors.centerIn: parent
- text: root.text
- font.pixelSize: 0.4 * parent.height
- color: mouseArea.pressed ? textColorSelected : textColor
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
-
- MouseArea {
- id: mouseArea
- anchors.fill: parent
- onClicked: {
- root.clicked()
- }
- }
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraBasic.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraBasic.qml
deleted file mode 100644
index c30344d2a..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraBasic.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneBasic {
- contentType: "camera"
- started: true
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraDrag.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraDrag.qml
deleted file mode 100644
index c8b7e3d16..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraDrag.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneDrag {
- contentType: "camera"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraDummy.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraDummy.qml
deleted file mode 100644
index 6b3bac9b2..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraDummy.qml
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-// Item which is loaded by CameraItem if Qt Multimedia is not available
-Rectangle {
- id: root
- color: "grey"
- height: width
-
- signal fatalError
- signal sizeChanged
- signal framePainted
-
- Text {
- anchors.fill: parent
- anchors.margins: 10
- color: "white"
- horizontalAlignment: Text.AlignHCenter
- text: "Failed to create Camera item\n\nCheck that Qt Multimedia is installed"
- verticalAlignment: Text.AlignVCenter
- wrapMode: Text.Wrap
- }
-
- onWidthChanged: height = width
- onHeightChanged: root.sizeChanged()
-
- function start() { }
- function stop() { }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraFullScreen.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraFullScreen.qml
deleted file mode 100644
index a1ce00176..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraFullScreen.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneFullScreen {
- contentType: "camera"
-}
-
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraFullScreenInverted.qml
deleted file mode 100644
index 9b0cb80c8..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraFullScreenInverted.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneFullScreenInverted {
- contentType: "camera"
-}
-
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraItem.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraItem.qml
deleted file mode 100644
index 26ffcc352..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraItem.qml
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtMultimedia
-
-Item {
- id: root
- height: width
-
- signal fatalError
- signal sizeChanged
-
- onHeightChanged: root.sizeChanged()
-
- CaptureSession {
- camera: Camera {
- id: camera
-
- onErrorOccurred: function(error, errorString) {
- if (Camera.NoError !== error) {
- console.log("[qmlvideo] CameraItem.onError error " + error + " errorString " + errorString)
- root.fatalError()
- }
- }
- }
- imageCapture: ImageCapture {
- id: imageCapture
- }
-
- recorder: MediaRecorder {
- id: recorder
-// resolution: "640x480"
-// frameRate: 30
- }
- videoOutput: videoOutput
- }
-
- VideoOutput {
- id: videoOutput
- anchors.fill: parent
- }
-
-
- function start() { camera.start() }
- function stop() { camera.stop() }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraMove.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraMove.qml
deleted file mode 100644
index 0f9f55171..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraMove.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneMove {
- contentType: "camera"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraOverlay.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraOverlay.qml
deleted file mode 100644
index 15dc85255..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraOverlay.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneOverlay {
- contentType: "camera"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraResize.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraResize.qml
deleted file mode 100644
index 1fee96489..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraResize.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneResize {
- contentType: "camera"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraRotate.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraRotate.qml
deleted file mode 100644
index 680f4d998..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraRotate.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneRotate {
- contentType: "camera"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraSpin.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraSpin.qml
deleted file mode 100644
index 5ae981350..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/CameraSpin.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneSpin {
- contentType: "camera"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Content.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Content.qml
deleted file mode 100644
index 803f7fff3..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Content.qml
+++ /dev/null
@@ -1,165 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Rectangle {
- id: root
- border.color: "white"
- border.width: showBorder ? 1 : 0
- color: "transparent"
- property string contentType // "camera" or "video"
- property string source
- property real volume
- property bool dummy: false
- property bool autoStart: true
- property bool started: false
- property bool showFrameRate: false
- property bool showBorder: false
-
- signal initialized
- signal error
- signal videoFramePainted
-
- Loader {
- id: contentLoader
- }
-
- Connections {
- id: framePaintedConnection
- function onFramePainted() {
- if (frameRateLoader.item)
- frameRateLoader.item.notify()
- root.videoFramePainted()
- }
- ignoreUnknownSignals: true
- }
-
- Connections {
- id: errorConnection
- function onFatalError() {
- console.log("[qmlvideo] Content.onFatalError")
- stop()
- root.error()
- }
- ignoreUnknownSignals: true
- }
-
- Loader {
- id: frameRateLoader
- source: root.showFrameRate ? "../frequencymonitor/FrequencyItem.qml" : ""
- onLoaded: {
- item.parent = root
- item.anchors.top = root.top
- item.anchors.right = root.right
- item.anchors.margins = 10
- }
- }
-
- onWidthChanged: {
- if (contentItem())
- contentItem().width = width
- }
-
- onHeightChanged: {
- if (contentItem())
- contentItem().height = height
- }
-
- function initialize() {
- if ("video" == contentType) {
- contentLoader.source = "VideoItem.qml"
- if (Loader.Error == contentLoader.status) {
- contentLoader.source = "VideoDummy.qml"
- dummy = true
- }
- contentLoader.item.volume = volume
- } else if ("camera" == contentType) {
- contentLoader.source = "CameraItem.qml"
- if (Loader.Error == contentLoader.status) {
- contentLoader.source = "CameraDummy.qml"
- dummy = true
- }
- } else {
- console.log("[qmlvideo] Content.initialize: error: invalid contentType")
- }
- if (contentLoader.item) {
- contentLoader.item.sizeChanged.connect(updateRootSize)
- contentLoader.item.parent = root
- contentLoader.item.width = root.width
- framePaintedConnection.target = contentLoader.item
- errorConnection.target = contentLoader.item
- if (root.autoStart)
- root.start()
- }
- root.initialized()
- }
-
- function start() {
- if (contentLoader.item) {
- if (root.contentType == "video")
- contentLoader.item.mediaSource = root.source
- contentLoader.item.start()
- root.started = true
- }
- }
-
- function stop() {
- if (contentLoader.item) {
- contentLoader.item.stop()
- if (root.contentType == "video")
- contentLoader.item.mediaSource = ""
- root.started = false
- }
- }
-
- function contentItem() { return contentLoader.item }
- function updateRootSize() { root.height = contentItem().height }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/ErrorDialog.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/ErrorDialog.qml
deleted file mode 100644
index cfe719f3a..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/ErrorDialog.qml
+++ /dev/null
@@ -1,118 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Rectangle {
- id: root
- color: "transparent"
- opacity: 0.0
- property alias enabled: mouseArea.enabled
- property int dialogWidth: 300
- property int dialogHeight: 200
- state: enabled ? "on" : "baseState"
-
- states: [
- State {
- name: "on"
- PropertyChanges {
- target: root
- opacity: 1.0
- }
- }
- ]
-
- transitions: [
- Transition {
- from: "*"
- to: "*"
- NumberAnimation {
- properties: "opacity"
- easing.type: Easing.OutQuart
- duration: 500
- }
- }
- ]
-
- Rectangle {
- anchors.fill: parent
- color: "black"
- opacity: 0.75
- }
-
- Rectangle {
- anchors.centerIn: parent
- width: dialogWidth
- height: dialogHeight
- radius: 5
- color: "white"
-
- Text {
- id: text
- anchors.fill: parent
- anchors.margins: 10
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- color: "black"
- wrapMode: Text.WordWrap
- }
- }
-
- MouseArea {
- id: mouseArea
- anchors.fill: parent
- onClicked: root.enabled = false
- }
-
- function show(msg) {
- text.text = "<b>Error</b><br><br>" + msg
- root.enabled = true
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml
deleted file mode 100644
index 31684368e..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/FileBrowser.qml
+++ /dev/null
@@ -1,419 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import Qt.labs.folderlistmodel
-
-Rectangle {
- id: fileBrowser
- color: "transparent"
-
- property string folder
-
- property int itemHeight: Math.min(parent.width, parent.height) / 15
- property int buttonHeight: Math.min(parent.width, parent.height) / 12
-
- signal fileSelected(string file)
-
- function selectFile(file) {
- if (file !== "") {
- folder = loader.item.folders.folder
- fileBrowser.fileSelected(file)
- }
- loader.sourceComponent = undefined
- }
-
- Loader {
- id: loader
- }
-
- function show() {
- loader.sourceComponent = fileBrowserComponent
- loader.item.parent = fileBrowser
- loader.item.anchors.fill = fileBrowser
- loader.item.folder = fileBrowser.folder
- }
-
- Component {
- id: fileBrowserComponent
-
- Rectangle {
- id: root
- color: "black"
- property bool showFocusHighlight: false
- property variant folders: folders1
- property variant view: view1
- property alias folder: folders1.folder
- property color textColor: "white"
-
- FolderListModel {
- id: folders1
- folder: folder
- }
-
- FolderListModel {
- id: folders2
- folder: folder
- }
-
- SystemPalette {
- id: palette
- }
-
- Component {
- id: folderDelegate
-
- Rectangle {
- id: wrapper
- function launch() {
- var path = "file://";
- if (filePath.length > 2 && filePath[1] === ':') // Windows drive logic, see QUrl::fromLocalFile()
- path += '/';
- path += filePath;
- if (folders.isFolder(index))
- down(path);
- else
- fileBrowser.selectFile(path)
- }
- width: root.width
- height: folderImage.height
- color: "transparent"
-
- Rectangle {
- id: highlight
- visible: false
- anchors.fill: parent
- anchors.leftMargin: 5
- anchors.rightMargin: 5
- color: "#212121"
- }
-
- Item {
- id: folderImage
- width: itemHeight
- height: itemHeight
- Image {
- id: folderPicture
- source: "qrc:/folder.png"
- width: itemHeight * 0.9
- height: itemHeight * 0.9
- anchors.left: parent.left
- anchors.margins: 5
- visible: folders.isFolder(index)
- }
- }
-
- Text {
- id: nameText
- anchors.fill: parent;
- verticalAlignment: Text.AlignVCenter
- text: fileName
- anchors.leftMargin: itemHeight + 10
- color: (wrapper.ListView.isCurrentItem && root.showFocusHighlight) ? palette.highlightedText : textColor
- elide: Text.ElideRight
- }
-
- MouseArea {
- id: mouseRegion
- anchors.fill: parent
- onPressed: {
- root.showFocusHighlight = false;
- wrapper.ListView.view.currentIndex = index;
- }
- onClicked: { if (folders === wrapper.ListView.view.model) launch() }
- }
-
- states: [
- State {
- name: "pressed"
- when: mouseRegion.pressed
- PropertyChanges { target: highlight; visible: true }
- PropertyChanges { target: nameText; color: palette.highlightedText }
- }
- ]
- }
- }
-
- ListView {
- id: view1
- anchors.top: titleBar.bottom
- anchors.bottom: cancelButton.top
- width: parent.width
- model: folders1
- delegate: folderDelegate
- highlight: Rectangle {
- color: "#212121"
- visible: root.showFocusHighlight && view1.count != 0
- width: view1.currentItem == null ? 0 : view1.currentItem.width
- }
- highlightMoveVelocity: 1000
- pressDelay: 100
- focus: true
- state: "current"
- states: [
- State {
- name: "current"
- PropertyChanges { target: view1; x: 0 }
- },
- State {
- name: "exitLeft"
- PropertyChanges { target: view1; x: -root.width }
- },
- State {
- name: "exitRight"
- PropertyChanges { target: view1; x: root.width }
- }
- ]
- transitions: [
- Transition {
- to: "current"
- SequentialAnimation {
- NumberAnimation { properties: "x"; duration: 250 }
- }
- },
- Transition {
- NumberAnimation { properties: "x"; duration: 250 }
- NumberAnimation { properties: "x"; duration: 250 }
- }
- ]
- Keys.onPressed: root.keyPressed(event.key)
- }
-
- ListView {
- id: view2
- anchors.top: titleBar.bottom
- anchors.bottom: parent.bottom
- x: parent.width
- width: parent.width
- model: folders2
- delegate: folderDelegate
- highlight: Rectangle {
- color: "#212121"
- visible: root.showFocusHighlight && view2.count != 0
- width: view1.currentItem == null ? 0 : view1.currentItem.width
- }
- highlightMoveVelocity: 1000
- pressDelay: 100
- states: [
- State {
- name: "current"
- PropertyChanges { target: view2; x: 0 }
- },
- State {
- name: "exitLeft"
- PropertyChanges { target: view2; x: -root.width }
- },
- State {
- name: "exitRight"
- PropertyChanges { target: view2; x: root.width }
- }
- ]
- transitions: [
- Transition {
- to: "current"
- SequentialAnimation {
- NumberAnimation { properties: "x"; duration: 250 }
- }
- },
- Transition {
- NumberAnimation { properties: "x"; duration: 250 }
- }
- ]
- Keys.onPressed: root.keyPressed(event.key)
- }
-
- Rectangle {
- width: parent.width
- height: buttonHeight + 10
- anchors.bottom: parent.bottom
- color: "black"
- }
-
- Rectangle {
- id: cancelButton
- width: parent.width
- height: buttonHeight
- color: "#212121"
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.margins: 5
- radius: buttonHeight / 15
-
- Text {
- anchors.fill: parent
- text: "Cancel"
- color: "white"
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: fileBrowser.selectFile("")
- }
- }
-
- Keys.onPressed: {
- root.keyPressed(event.key);
- if (event.key === Qt.Key_Return || event.key === Qt.Key_Select || event.key === Qt.Key_Right) {
- view.currentItem.launch();
- event.accepted = true;
- } else if (event.key === Qt.Key_Left) {
- up();
- }
- }
-
-
- Rectangle {
- id: titleBar
- width: parent.width
- height: buttonHeight + 10
- anchors.top: parent.top
- color: "black"
-
- Rectangle {
- width: parent.width;
- height: buttonHeight
- color: "#212121"
- anchors.margins: 5
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.right: parent.right
- radius: buttonHeight / 15
-
- Rectangle {
- id: upButton
- width: buttonHeight
- height: buttonHeight
- color: "transparent"
- Image {
- width: itemHeight
- height: itemHeight
- anchors.centerIn: parent
- source: "qrc:/up.png"
- }
- MouseArea { id: upRegion; anchors.centerIn: parent
- width: buttonHeight
- height: buttonHeight
- onClicked: up()
- }
- states: [
- State {
- name: "pressed"
- when: upRegion.pressed
- PropertyChanges { target: upButton; color: palette.highlight }
- }
- ]
- }
-
- Text {
- anchors.left: upButton.right; anchors.right: parent.right; height: parent.height
- anchors.leftMargin: 5; anchors.rightMargin: 5
- text: folders.folder
- color: "white"
- elide: Text.ElideLeft;
- horizontalAlignment: Text.AlignLeft;
- verticalAlignment: Text.AlignVCenter
- }
- }
- }
-
- function down(path) {
- if (folders == folders1) {
- view = view2
- folders = folders2;
- view1.state = "exitLeft";
- } else {
- view = view1
- folders = folders1;
- view2.state = "exitLeft";
- }
- view.x = root.width;
- view.state = "current";
- view.focus = true;
- folders.folder = path;
- }
-
- function up() {
- var path = folders.parentFolder;
- if (path.toString().length === 0 || path.toString() === 'file:')
- return;
- if (folders == folders1) {
- view = view2
- folders = folders2;
- view1.state = "exitRight";
- } else {
- view = view1
- folders = folders1;
- view2.state = "exitRight";
- }
- view.x = -root.width;
- view.state = "current";
- view.focus = true;
- folders.folder = path;
- }
-
- function keyPressed(key) {
- switch (key) {
- case Qt.Key_Up:
- case Qt.Key_Down:
- case Qt.Key_Left:
- case Qt.Key_Right:
- root.showFocusHighlight = true;
- break;
- default:
- // do nothing
- break;
- }
- }
- }
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml
deleted file mode 100644
index 6a3efd4cb..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/Scene.qml
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Rectangle {
- id: root
- color: "black"
- property alias buttonHeight: closeButton.height
- property string source1
- property string source2
- property int contentWidth: parent.width / 2
- property real volume: 0.25
- property int margins: 5
- property QtObject content
-
- signal close
- signal videoFramePainted
-
- Button {
- id: closeButton
- anchors {
- top: parent.top
- right: parent.right
- margins: root.margins
- }
- width: Math.max(parent.width, parent.height) / 12
- height: Math.min(parent.width, parent.height) / 12
- z: 2.0
- bgColor: "#212121"
- bgColorSelected: "#757575"
- textColorSelected: "white"
- text: "Back"
- onClicked: root.close()
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml
deleted file mode 100644
index 7e215cac6..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneBasic.qml
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property string contentType
- property bool autoStart: false
- property bool started: false
-
- Content {
- id: content
- autoStart: parent.autoStart
- started: parent.started
- anchors.fill: parent
- width: parent.contentWidth
- contentType: parent.contentType
- source: parent.source1
- volume: parent.volume
- onVideoFramePainted: root.videoFramePainted()
- }
-
- Text {
- anchors {
- horizontalCenter: parent.horizontalCenter
- bottom: parent.bottom
- margins: 20
- }
- text: content.started ? "Tap the screen to stop content"
- : "Tap the screen to start content"
- color: "#e0e0e0"
- z: 2.0
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- if (content.started)
- content.stop()
- else
- content.start()
- }
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneDrag.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneDrag.qml
deleted file mode 100644
index 433afacd3..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneDrag.qml
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property int margin: 20
- property string contentType
-
- Image {
- id: background
- source: "qrc:/images/leaves.jpg"
- x: (parent.width - width) / 2
- y: (parent.height - height) / 2
-
- Content {
- id: content
- anchors.centerIn: parent
- width: root.contentWidth
- contentType: root.contentType
- source: root.source1
- volume: root.volume
- onVideoFramePainted: root.videoFramePainted()
- }
- }
-
- MouseArea {
- anchors.fill: parent
- drag.target: background
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreen.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreen.qml
deleted file mode 100644
index 5fab6e013..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreen.qml
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property string contentType
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: root.contentType
- source: parent.source1
- volume: parent.volume
- state: "left"
-
- states: [
- State {
- name: "fullScreen"
- PropertyChanges { target: content; width: content.parent.width }
- PropertyChanges { target: content; height: content.parent.height }
- }
- ]
-
- transitions: [
- Transition {
- ParallelAnimation {
- PropertyAnimation {
- property: "width"
- easing.type: Easing.Linear
- duration: 250
- }
- PropertyAnimation {
- property: "height"
- easing.type: Easing.Linear
- duration: 250
- }
- }
- }
- ]
-
- MouseArea {
- anchors.fill: parent
- onClicked: content.state = (content.state == "fullScreen") ? "baseState" : "fullScreen"
- }
-
- onVideoFramePainted: root.videoFramePainted()
- }
-
- Text {
- anchors {
- horizontalCenter: parent.horizontalCenter
- bottom: parent.bottom
- margins: 20
- }
- text: "Tap on the content to toggle full-screen mode"
- color: "#e0e0e0"
- z: 2.0
- }
-
- Component.onCompleted: root.content = content
-}
-
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreenInverted.qml
deleted file mode 100644
index 7a70d8a0f..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneFullScreenInverted.qml
+++ /dev/null
@@ -1,117 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property string contentType
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.width
- height: parent.height
- contentType: root.contentType
- source: parent.source1
- volume: parent.volume
- state: "left"
-
- states: [
- State {
- name: "nonFullScreen"
- PropertyChanges { target: content; width: content.parent.contentWidth }
- }
- ]
-
- transitions: [
- Transition {
- ParallelAnimation {
- PropertyAnimation {
- property: "width"
- easing.type: Easing.Linear
- duration: 250
- }
- PropertyAnimation {
- property: "height"
- easing.type: Easing.Linear
- duration: 250
- }
- }
- }
- ]
-
- MouseArea {
- anchors.fill: parent
- onClicked: content.state = (content.state == "nonFullScreen") ? "baseState" : "nonFullScreen"
- }
-
- onVideoFramePainted: root.videoFramePainted()
-
- onInitialized: {
- width = parent.width
- height = parent.height
- }
- }
-
- Text {
- anchors {
- horizontalCenter: parent.horizontalCenter
- bottom: parent.bottom
- margins: 20
- }
- text: "Tap on the content to toggle full-screen mode"
- color: "#e0e0e0"
- z: 2.0
- }
-
- Component.onCompleted: root.content = content
-}
-
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMove.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMove.qml
deleted file mode 100644
index 45595bbdb..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMove.qml
+++ /dev/null
@@ -1,96 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property int margin: 20
- property string contentType
-
- Content {
- id: content
- anchors.verticalCenter: parent.verticalCenter
- width: parent.contentWidth
- contentType: root.contentType
- source: parent.source1
- volume: parent.volume
-
- SequentialAnimation on x {
- id: animation
- loops: Animation.Infinite
- property int from: margin
- property int to: 100
- property int duration: 1500
- running: false
- PropertyAnimation {
- from: animation.from
- to: animation.to
- duration: animation.duration
- easing.type: Easing.InOutCubic
- }
- PropertyAnimation {
- from: animation.to
- to: animation.from
- duration: animation.duration
- easing.type: Easing.InOutCubic
- }
- }
-
- onVideoFramePainted: root.videoFramePainted()
- }
-
- onWidthChanged: {
- animation.to = root.width - content.width - margin
- animation.start()
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneOverlay.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneOverlay.qml
deleted file mode 100644
index d650cc0f7..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneOverlay.qml
+++ /dev/null
@@ -1,130 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property int margin: 20
- property string contentType
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: root.contentType
- source: parent.source1
- volume: parent.volume
- onVideoFramePainted: root.videoFramePainted()
- }
-
- Rectangle {
- id: overlay
- y: 0.5 * parent.height
- width: content.width
- height: content.height
- color: "#e0e0e0"
- opacity: 0.5
-
- SequentialAnimation on x {
- id: xAnimation
- loops: Animation.Infinite
- property int from: margin
- property int to: 100
- property int duration: 1500
- running: false
- PropertyAnimation {
- from: xAnimation.from
- to: xAnimation.to
- duration: xAnimation.duration
- easing.type: Easing.InOutCubic
- }
- PropertyAnimation {
- from: xAnimation.to
- to: xAnimation.from
- duration: xAnimation.duration
- easing.type: Easing.InOutCubic
- }
- }
-
- SequentialAnimation on y {
- id: yAnimation
- loops: Animation.Infinite
- property int from: margin
- property int to: 180
- property int duration: 1500
- running: false
- PropertyAnimation {
- from: yAnimation.from
- to: yAnimation.to
- duration: yAnimation.duration
- easing.type: Easing.InOutCubic
- }
- PropertyAnimation {
- from: yAnimation.to
- to: yAnimation.from
- duration: yAnimation.duration
- easing.type: Easing.InOutCubic
- }
- }
- }
-
- onWidthChanged: {
- xAnimation.to = root.width - content.width - margin
- xAnimation.start()
- }
-
- onHeightChanged: {
- //yAnimation.to = root.height - content.height - margin
- yAnimation.start()
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneResize.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneResize.qml
deleted file mode 100644
index 45331d15d..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneResize.qml
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property string contentType
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: root.contentType
- source: parent.source1
- volume: parent.volume
-
- SequentialAnimation on scale {
- id: animation
- loops: Animation.Infinite
- property int duration: 1500
- running: true
- PropertyAnimation {
- from: 1.5
- to: 0.5
- duration: animation.duration
- easing.type: Easing.InOutCubic
- }
- PropertyAnimation {
- from: 0.5
- to: 1.5
- duration: animation.duration
- easing.type: Easing.InOutCubic
- }
- }
-
- onVideoFramePainted: root.videoFramePainted()
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneRotate.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneRotate.qml
deleted file mode 100644
index 4e1363c03..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneRotate.qml
+++ /dev/null
@@ -1,109 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property int margin: 20
- property int delta: 30
- property string contentType
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: root.contentType
- source: parent.source1
- volume: parent.volume
- onVideoFramePainted: root.videoFramePainted()
- }
-
- Button {
- id: rotatePositiveButton
- anchors {
- right: parent.right
- bottom: rotateNegativeButton.top
- margins: parent.margins
- }
- width: Math.max(parent.width, parent.height) / 10
- height: root.buttonHeight
- text: "Rotate +" + delta
- onClicked: content.rotation = content.rotation + delta
- }
-
- Button {
- id: rotateNegativeButton
- anchors {
- right: parent.right
- verticalCenter: parent.verticalCenter
- margins: parent.margins
- }
- width: Math.max(parent.width, parent.height) / 10
- height: root.buttonHeight
- text: "Rotate -" + delta
- onClicked: content.rotation = content.rotation - delta
- }
-
- Button {
- id: rotateValueButton
- anchors {
- left: parent.left
- verticalCenter: parent.verticalCenter
- margins: parent.margins
- }
- width: Math.max(parent.width, parent.height) / 25
- height: root.buttonHeight
- enabled: false
- text: content.rotation % 360
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml
deleted file mode 100644
index f8583a983..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSelectionPanel.qml
+++ /dev/null
@@ -1,151 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Rectangle {
- id: root
- property int itemHeight: 25
- property string sceneSource: ""
-
- ListModel {
- id: videolist
- ListElement { name: "Multi"; source: "SceneMulti.qml" }
- ListElement { name: "Video"; source: "VideoBasic.qml" }
- ListElement { name: "Drag"; source: "VideoDrag.qml" }
- ListElement { name: "Fillmode"; source: "VideoFillMode.qml" }
- ListElement { name: "Fullscreen"; source: "VideoFullScreen.qml" }
- ListElement { name: "Fullscreen-inverted"; source: "VideoFullScreenInverted.qml" }
- ListElement { name: "Metadata"; source: "VideoMetadata.qml" }
- ListElement { name: "Move"; source: "VideoMove.qml" }
- ListElement { name: "Overlay"; source: "VideoOverlay.qml" }
- ListElement { name: "Playback Rate"; source: "VideoPlaybackRate.qml" }
- ListElement { name: "Resize"; source: "VideoResize.qml" }
- ListElement { name: "Rotate"; source: "VideoRotate.qml" }
- ListElement { name: "Spin"; source: "VideoSpin.qml" }
- ListElement { name: "Seek"; source: "VideoSeek.qml" }
- }
-
- ListModel {
- id: cameralist
- ListElement { name: "Camera"; source: "CameraBasic.qml" }
- ListElement { name: "Drag"; source: "CameraDrag.qml" }
- ListElement { name: "Fullscreen"; source: "CameraFullScreen.qml" }
- ListElement { name: "Fullscreen-inverted"; source: "CameraFullScreenInverted.qml" }
- ListElement { name: "Move"; source: "CameraMove.qml" }
- ListElement { name: "Overlay"; source: "CameraOverlay.qml" }
- ListElement { name: "Resize"; source: "CameraResize.qml" }
- ListElement { name: "Rotate"; source: "CameraRotate.qml" }
- ListElement { name: "Spin"; source: "CameraSpin.qml" }
- }
-
- Component {
- id: leftDelegate
- Item {
- width: root.width / 2
- height: 0.8 * itemHeight
-
- Button {
- anchors.fill: parent
- anchors.margins: 5
- anchors.rightMargin: 2.5
- anchors.bottomMargin: 0
- text: name
- onClicked: root.sceneSource = source
- }
- }
- }
-
- Component {
- id: rightDelegate
- Item {
- width: root.width / 2
- height: 0.8 * itemHeight
-
- Button {
- anchors.fill: parent
- anchors.margins: 5
- anchors.leftMargin: 2.5
- anchors.bottomMargin: 0
- text: name
- onClicked: root.sceneSource = source
- }
- }
- }
-
- Flickable {
- anchors.fill: parent
- contentHeight: (itemHeight * videolist.count) + 10
- clip: true
-
- Row {
- id: layout
- anchors {
- fill: parent
- topMargin: 5
- bottomMargin: 5
- }
-
- Column {
- Repeater {
- model: videolist
- delegate: leftDelegate
- }
- }
-
- Column {
- Repeater {
- model: cameralist
- delegate: rightDelegate
- }
- }
- }
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSpin.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSpin.qml
deleted file mode 100644
index 14c14df05..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneSpin.qml
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property int margin: 20
- property string contentType
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: root.contentType
- source: parent.source1
- volume: parent.volume
-
- PropertyAnimation on rotation {
- id: animation
- loops: Animation.Infinite
- running: true
- from: 0
- to: 360
- duration: 3000
- easing.type: Easing.Linear
- }
-
- onVideoFramePainted: root.videoFramePainted()
- }
-
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml
deleted file mode 100644
index fa6f07532..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoBasic.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneBasic {
- contentType: "video"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoDrag.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoDrag.qml
deleted file mode 100644
index 1295c15f2..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoDrag.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneDrag {
- contentType: "video"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoDummy.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoDummy.qml
deleted file mode 100644
index d36f179ee..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoDummy.qml
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-// Item which is loaded by VideoItem if Qt Multimedia is not available
-Rectangle {
- id: root
- color: "grey"
- height: width
- property int duration: 0
- property int position: 0
- property string source
- property real volume: 1.0
- property real playbackRate: 1.0
-
- signal fatalError
- signal sizeChanged
- signal framePainted
-
- Text {
- anchors.fill: parent
- anchors.margins: 10
- color: "white"
- horizontalAlignment: Text.AlignHCenter
- text: "Failed to create Video item\n\nCheck that Qt Multimedia is installed"
- verticalAlignment: Text.AlignVCenter
- wrapMode: Text.Wrap
- }
-
- onWidthChanged: height = width
- onHeightChanged: root.sizeChanged()
-
- function start() { }
- function stop() { }
- function seek() { }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFillMode.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFillMode.qml
deleted file mode 100644
index 7fbae4241..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFillMode.qml
+++ /dev/null
@@ -1,95 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtMultimedia
-
-Scene {
- id: root
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: "video"
- source: parent.source1
- volume: parent.volume
- onVideoFramePainted: root.videoFramePainted()
- }
-
- Button {
- id: button
- anchors {
- right: parent.right
- verticalCenter: parent.verticalCenter
- margins: parent.margins
- }
- width: Math.max(parent.width, parent.height) / 5
- height: root.buttonHeight
- text: "PreserveAspectFit"
- onClicked: {
- if (!content.dummy) {
- var video = content.contentItem()
- if (video.fillMode === VideoOutput.Stretch) {
- video.fillMode = VideoOutput.PreserveAspectFit
- text = "PreserveAspectFit"
- } else if (video.fillMode === VideoOutput.PreserveAspectFit) {
- video.fillMode = VideoOutput.PreserveAspectCrop
- text = "PreserveAspectCrop"
- } else {
- video.fillMode = VideoOutput.Stretch
- text = "Stretch"
- }
- }
- }
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFullScreen.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFullScreen.qml
deleted file mode 100644
index 12973fb15..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFullScreen.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneFullScreen {
- contentType: "video"
-}
-
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFullScreenInverted.qml
deleted file mode 100644
index 213ae59d1..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoFullScreenInverted.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneFullScreenInverted {
- contentType: "video"
-}
-
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoItem.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoItem.qml
deleted file mode 100644
index 56ee16f0f..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoItem.qml
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtMultimedia
-
-VideoOutput {
- id: root
- height: width
-
- property alias duration: mediaPlayer.duration
- property alias mediaSource: mediaPlayer.source
- property alias metaData: mediaPlayer.metaData
- property alias playbackRate: mediaPlayer.playbackRate
- property alias position: mediaPlayer.position
- property alias seekable: mediaPlayer.seekable
- property alias volume: audioOutput.volume
-
- signal sizeChanged
- signal fatalError
-
- onHeightChanged: root.sizeChanged()
-
- MediaPlayer {
- id: mediaPlayer
- videoOutput: root;
- audioOutput: AudioOutput {
- id: audioOutput
- }
-
- onErrorOccurred: function(error, errorString) {
- if (MediaPlayer.NoError !== error) {
- console.log("[qmlvideo] VideoItem.onError error " + error + " errorString " + errorString)
- root.fatalError()
- }
- }
- }
-
- function start() { mediaPlayer.play() }
- function stop() { mediaPlayer.stop() }
- function seek(position) { mediaPlayer.setPosition(position); }
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMetadata.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMetadata.qml
deleted file mode 100644
index 4af841454..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMetadata.qml
+++ /dev/null
@@ -1,137 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtMultimedia
-
-Scene {
- id: root
- property string contentType: "video"
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: "video"
- source: parent.source1
- volume: parent.volume
- onInitialized: {
- if (!dummy)
- metadata.createObject(root)
- }
- onVideoFramePainted: root.videoFramePainted()
- }
-
- Component {
- id: metadata
- Column {
- anchors.fill: parent
- property var videoMetaData: content.contentItem().metaData
- Text {
- color: "#e0e0e0"
- text: "Title:" + videoMetaData.value(MediaMetaData.Title)
- }
- Text {
- color: "#e0e0e0"
- text: "Size:" + videoMetaData.value(MediaMetaData.Size)
- }
- Text {
- color: "#e0e0e0"
- text: "Resolution:" + videoMetaData.value(MediaMetaData.Resolution)
- }
- Text {
- color: "#e0e0e0"
- text: "Media type:" + videoMetaData.value(MediaMetaData.MediaType)
- }
- Text {
- color: "#e0e0e0"
- text: "Video codec:" + videoMetaData.value(MediaMetaData.VideoCodec)
- }
- Text {
- color: "#e0e0e0"
- text: "Video bit rate:" + videoMetaData.value(MediaMetaData.VideoBitRate)
- }
- Text {
- color: "#e0e0e0"
- text: "Video frame rate:" +videoMetaData.value(MediaMetaData.VideoFrameRate)
- }
- Text {
- color: "#e0e0e0"
- text: "Audio codec:" + videoMetaData.value(MediaMetaData.AudioCodec)
- }
- Text {
- color: "#e0e0e0"
- text: "Audio bit rate:" + videoMetaData.value(MediaMetaData.AudioBitRate)
- }
- Text {
- color: "#e0e0e0"
- text: "Date:" + videoMetaData.value(MediaMetaData.Date)
- }
- Text {
- color: "#e0e0e0"
- text: "Description:" + videoMetaData.value(MediaMetaData.Description)
- }
- Text {
- color: "#e0e0e0"
- text: "Copyright:" + videoMetaData.value(MediaMetaData.Copyright)
- }
- Text {
- color: "#e0e0e0"
- text: "Seekable:" + content.contentItem().seekable
- }
- Text {
- color: "#e0e0e0"
- text: "Orientation:" + videoMetaData.value(MediaMetaData.Orientation)
- }
- }
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMove.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMove.qml
deleted file mode 100644
index 8fab32a23..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoMove.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneMove {
- contentType: "video"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoOverlay.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoOverlay.qml
deleted file mode 100644
index abbefa937..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoOverlay.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneOverlay {
- contentType: "video"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoPlaybackRate.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoPlaybackRate.qml
deleted file mode 100644
index 0fdefc0f4..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoPlaybackRate.qml
+++ /dev/null
@@ -1,115 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property int margin: 20
- property real delta: 0.1
- property string contentType: "video"
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: "video"
- source: parent.source1
- volume: parent.volume
- onVideoFramePainted: root.videoFramePainted()
- }
-
- Button {
- id: increaseButton
- anchors {
- right: parent.right
- bottom: decreaseButton.top
- margins: parent.margins
- }
- width: Math.max(parent.width, parent.height) / 10
- height: root.buttonHeight
- text: "Increase"
- onClicked: {
- var video = content.contentItem()
- video.playbackRate += delta
- }
- }
-
- Button {
- id: decreaseButton
- anchors {
- right: parent.right
- verticalCenter: parent.verticalCenter
- margins: parent.margins
- }
- width: Math.max(parent.width, parent.height) / 10
- height: root.buttonHeight
- text: "Decrease"
- onClicked: {
- var video = content.contentItem()
- video.playbackRate -= delta
- }
- }
-
- Button {
- id: valueButton
- anchors {
- left: parent.left
- verticalCenter: parent.verticalCenter
- margins: parent.margins
- }
- width: Math.max(parent.width, parent.height) / 25
- height: root.buttonHeight
- enabled: false
- text: Math.round(10 * content.contentItem().playbackRate) / 10
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoResize.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoResize.qml
deleted file mode 100644
index 85961ea36..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoResize.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneResize {
- contentType: "video"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoRotate.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoRotate.qml
deleted file mode 100644
index 1406ccdea..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoRotate.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneRotate {
- contentType: "video"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSeek.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSeek.qml
deleted file mode 100644
index f17fa0a5c..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSeek.qml
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Scene {
- id: root
- property string contentType: "video"
- contentWidth: parent.width
-
- Content {
- id: content
- anchors.centerIn: parent
- width: parent.contentWidth
- contentType: "video"
- source: parent.source1
- volume: parent.volume
- onVideoFramePainted: root.videoFramePainted()
- }
-
- SeekControl {
- anchors {
- left: parent.left
- right: parent.right
- margins: 10
- bottom: parent.bottom
- }
- duration: content.contentItem() ? content.contentItem().duration : 0
- playPosition: content.contentItem() ? content.contentItem().position : 0
- onSeekPositionChanged: content.contentItem().seek(seekPosition);
- }
-
- Component.onCompleted: root.content = content
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSpin.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSpin.qml
deleted file mode 100644
index 7c8b2e84f..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/VideoSpin.qml
+++ /dev/null
@@ -1,55 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-SceneSpin {
- contentType: "video"
-}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/main.qml b/examples/multimedia/video/qmlvideo/qml/qmlvideo/main.qml
deleted file mode 100644
index 4289f79f1..000000000
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/main.qml
+++ /dev/null
@@ -1,285 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-
-Rectangle {
- id: root
- anchors.fill: parent
- color: "black"
-
- property string source1
- property string source2
- property color bgColor: "black"
- property real volume: 0.25
- property bool perfMonitorsLogging: false
- property bool perfMonitorsVisible: false
-
- QtObject {
- id: d
- property int itemHeight: root.height > root.width ? root.width / 10 : root.height / 10
- property int buttonHeight: 0.8 * itemHeight
- property int margins: 5
- }
-
- Loader {
- id: performanceLoader
-
- Connections {
- target: inner
- function onVisibleChanged() {
- if (performanceLoader.item)
- performanceLoader.item.enabled = !inner.visible
- }
- ignoreUnknownSignals: true
- }
-
- function init() {
- var enabled = root.perfMonitorsLogging || root.perfMonitorsVisible
- source = enabled ? "../performancemonitor/PerformanceItem.qml" : ""
- }
-
- onLoaded: {
- item.parent = root
- item.anchors.fill = root
- item.logging = root.perfMonitorsLogging
- item.displayed = root.perfMonitorsVisible
- item.enabled = false
- item.init()
- }
- }
-
- Rectangle {
- id: inner
- anchors.fill: parent
- color: root.bgColor
-
- Button {
- id: openFile1Button
- anchors {
- top: parent.top
- left: parent.left
- right: exitButton.left
- margins: d.margins
- }
- bgColor: "#212121"
- bgColorSelected: "#757575"
- textColorSelected: "white"
- height: d.buttonHeight
- text: (root.source1 == "") ? "Select file 1" : root.source1
- onClicked: fileBrowser1.show()
- }
-
- Button {
- id: openFile2Button
- anchors {
- top: openFile1Button.bottom
- left: parent.left
- right: exitButton.left
- margins: d.margins
- }
- bgColor: "#212121"
- bgColorSelected: "#757575"
- textColorSelected: "white"
- height: d.buttonHeight
- text: (root.source2 == "") ? "Select file 2" : root.source2
- onClicked: fileBrowser2.show()
- }
-
- Button {
- id: exitButton
- anchors {
- top: parent.top
- right: parent.right
- margins: d.margins
- }
- bgColor: "#212121"
- bgColorSelected: "#757575"
- textColorSelected: "white"
- width: parent.width / 10
- height: d.buttonHeight
- text: "Exit"
- onClicked: Qt.quit()
- }
-
- Row {
- id: modes
- anchors.top: openFile2Button.bottom
- anchors.margins: 0
- anchors.topMargin: 5
- Button {
- width: root.width / 2
- height: 0.8 * d.itemHeight
- bgColor: "#212121"
- radius: 0
- text: "Video Modes"
- enabled: false
- }
- Button {
- width: root.width / 2
- height: 0.8 * d.itemHeight
- bgColor: "#212121"
- radius: 0
- text: "Camera Modes"
- enabled: false
- }
- }
-
- Rectangle {
- id: divider
- height: 1
- width: parent.width
- color: "black"
- anchors.top: modes.bottom
- }
-
- SceneSelectionPanel {
- id: sceneSelectionPanel
- itemHeight: d.itemHeight
- color: "#212121"
- anchors {
- top: divider.bottom
- left: parent.left
- right: parent.right
- bottom: parent.bottom
- }
- radius: 0
- onSceneSourceChanged: {
- sceneLoader.source = sceneSource
- var scene = null
- var innerVisible = true
- if (sceneSource == "") {
- if (performanceLoader.item)
- performanceLoader.item.videoActive = false
- } else {
- scene = sceneLoader.item
- if (scene) {
- if (scene.contentType === "video" && source1 === "") {
- errorDialog.show("You must first select a video file")
- sceneSource = ""
- } else {
- scene.parent = root
- scene.color = root.bgColor
- scene.buttonHeight = d.buttonHeight
- scene.source1 = source1
- scene.source2 = source2
- scene.volume = volume
- scene.anchors.fill = root
- scene.close.connect(closeScene)
- scene.content.initialize()
- innerVisible = false
- }
- }
- }
- videoFramePaintedConnection.target = scene
- inner.visible = innerVisible
- }
- }
- }
-
- Loader {
- id: sceneLoader
- }
-
- Connections {
- id: videoFramePaintedConnection
- function onVideoFramePainted() {
- if (performanceLoader.item)
- performanceLoader.item.videoFramePainted()
- }
- ignoreUnknownSignals: true
- }
-
- FileBrowser {
- id: fileBrowser1
- anchors.fill: root
- onFolderChanged: fileBrowser2.folder = folder
- Component.onCompleted: fileSelected.connect(root.openFile1)
- }
-
- FileBrowser {
- id: fileBrowser2
- anchors.fill: root
- onFolderChanged: fileBrowser1.folder = folder
- Component.onCompleted: fileSelected.connect(root.openFile2)
- }
-
- function openFile1(path) {
- root.source1 = path
- }
-
- function openFile2(path) {
- root.source2 = path
- }
-
- ErrorDialog {
- id: errorDialog
- anchors.fill: root
- dialogWidth: d.itemHeight * 5
- dialogHeight: d.itemHeight * 3
- enabled: false
- }
-
- // Called from main() once root properties have been set
- function init() {
- performanceLoader.init()
- fileBrowser1.folder = videoPath
- fileBrowser2.folder = videoPath
- }
-
- function qmlFramePainted() {
- if (performanceLoader.item)
- performanceLoader.item.qmlFramePainted()
- }
-
- function closeScene() {
- sceneSelectionPanel.sceneSource = ""
- }
-}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo.pro b/examples/multimedia/video/qmlvideo/qmlvideo.pro
index 7a6bbccff..b61339264 100644
--- a/examples/multimedia/video/qmlvideo/qmlvideo.pro
+++ b/examples/multimedia/video/qmlvideo/qmlvideo.pro
@@ -12,73 +12,84 @@ DEFINES += \
PERFORMANCEMONITOR_SUPPORT
SOURCES += \
- main.cpp \
frequencymonitor.cpp \
frequencymonitordeclarative.cpp \
+ main.cpp \
performancemonitor.cpp \
- performancemonitordeclarative.cpp
+ performancemonitordeclarative.cpp \
+ qmlvideo/videosingleton.cpp
+
+INCLUDEPATH += qmlvideo
+
+DEFINES += QMLVIDEO_LIB
HEADERS += \
- trace.h \
frequencymonitor.h \
performancemonitor.h \
- performancemonitordeclarative.h
+ performancemonitordeclarative.h \
+ trace.h \
+ qmlvideo/videosingleton.h
resources.files = \
- images/folder.png \
- images/leaves.jpg \
- images/up.png \
- qml/frequencymonitor/FrequencyItem.qml \
- qml/performancemonitor/PerformanceItem.qml \
- qml/qmlvideo/Button.qml \
- qml/qmlvideo/CameraBasic.qml \
- qml/qmlvideo/CameraDrag.qml \
- qml/qmlvideo/CameraDummy.qml \
- qml/qmlvideo/CameraFullScreen.qml \
- qml/qmlvideo/CameraFullScreenInverted.qml \
- qml/qmlvideo/CameraItem.qml \
- qml/qmlvideo/CameraMove.qml \
- qml/qmlvideo/CameraOverlay.qml \
- qml/qmlvideo/CameraResize.qml \
- qml/qmlvideo/CameraRotate.qml \
- qml/qmlvideo/CameraSpin.qml \
- qml/qmlvideo/Content.qml \
- qml/qmlvideo/ErrorDialog.qml \
- qml/qmlvideo/FileBrowser.qml \
- qml/qmlvideo/Scene.qml \
- qml/qmlvideo/SceneBasic.qml \
- qml/qmlvideo/SceneDrag.qml \
- qml/qmlvideo/SceneFullScreen.qml \
- qml/qmlvideo/SceneFullScreenInverted.qml \
- qml/qmlvideo/SceneMove.qml \
- qml/qmlvideo/SceneMulti.qml \
- qml/qmlvideo/SceneOverlay.qml \
- qml/qmlvideo/SceneResize.qml \
- qml/qmlvideo/SceneRotate.qml \
- qml/qmlvideo/SceneSelectionPanel.qml \
- qml/qmlvideo/SceneSpin.qml \
- qml/qmlvideo/SeekControl.qml \
- qml/qmlvideo/VideoBasic.qml \
- qml/qmlvideo/VideoDrag.qml \
- qml/qmlvideo/VideoDummy.qml \
- qml/qmlvideo/VideoFillMode.qml \
- qml/qmlvideo/VideoFullScreen.qml \
- qml/qmlvideo/VideoFullScreenInverted.qml \
- qml/qmlvideo/VideoItem.qml \
- qml/qmlvideo/VideoMetadata.qml \
- qml/qmlvideo/VideoMove.qml \
- qml/qmlvideo/VideoOverlay.qml \
- qml/qmlvideo/VideoPlaybackRate.qml \
- qml/qmlvideo/VideoResize.qml \
- qml/qmlvideo/VideoRotate.qml \
- qml/qmlvideo/VideoSeek.qml \
- qml/qmlvideo/VideoSpin.qml \
- qml/qmlvideo/main.qml
+ frequencymonitor/FrequencyItem.qml \
+ frequencymonitor/qmldir \
+ performancemonitor/PerformanceItem.qml \
+ performancemonitor/qmldir \
+ qmlvideo/CameraBasic.qml \
+ qmlvideo/CameraDrag.qml \
+ qmlvideo/CameraDummy.qml \
+ qmlvideo/CameraFullScreen.qml \
+ qmlvideo/CameraFullScreenInverted.qml \
+ qmlvideo/CameraItem.qml \
+ qmlvideo/CameraMove.qml \
+ qmlvideo/CameraOverlay.qml \
+ qmlvideo/CameraResize.qml \
+ qmlvideo/CameraRotate.qml \
+ qmlvideo/CameraSpin.qml \
+ qmlvideo/Content.qml \
+ qmlvideo/ErrorDialog.qml \
+ qmlvideo/Main.qml \
+ qmlvideo/Scene.qml \
+ qmlvideo/SceneBasic.qml \
+ qmlvideo/SceneDrag.qml \
+ qmlvideo/SceneFullScreen.qml \
+ qmlvideo/SceneFullScreenInverted.qml \
+ qmlvideo/SceneMove.qml \
+ qmlvideo/SceneMulti.qml \
+ qmlvideo/SceneOverlay.qml \
+ qmlvideo/SceneResize.qml \
+ qmlvideo/SceneRotate.qml \
+ qmlvideo/SceneSelectionPanel.qml \
+ qmlvideo/SceneSpin.qml \
+ qmlvideo/SeekControl.qml \
+ qmlvideo/VideoBasic.qml \
+ qmlvideo/VideoDrag.qml \
+ qmlvideo/VideoDummy.qml \
+ qmlvideo/VideoFillMode.qml \
+ qmlvideo/VideoFullScreen.qml \
+ qmlvideo/VideoFullScreenInverted.qml \
+ qmlvideo/VideoItem.qml \
+ qmlvideo/VideoMetadata.qml \
+ qmlvideo/VideoMove.qml \
+ qmlvideo/VideoOverlay.qml \
+ qmlvideo/VideoPlaybackRate.qml \
+ qmlvideo/VideoResize.qml \
+ qmlvideo/VideoRotate.qml \
+ qmlvideo/VideoSeek.qml \
+ qmlvideo/VideoSpin.qml \
+ qmlvideo/images/folder.png \
+ qmlvideo/images/leaves.jpg \
+ qmlvideo/images/up.png \
+ qmlvideo/qmldir
-resources.prefix = /
+resources.prefix = /qt/qml/
RESOURCES += resources
+CONFIG += qmltypes
+QML_IMPORT_NAME = qmlvideo
+QML_IMPORT_MAJOR_VERSION = 1
+
target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/video/qmlvideo
INSTALLS += target
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CMakeLists.txt b/examples/multimedia/video/qmlvideo/qmlvideo/CMakeLists.txt
new file mode 100644
index 000000000..84a65710e
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CMakeLists.txt
@@ -0,0 +1,81 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ message(FATAL_ERROR "This module is part of the 'qmlvideo' example, and should not be built independently.")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/video/qmlvideo/qmlvideo")
+
+qt_add_qml_module(qmlvideo
+ URI qmlvideo
+ SOURCES
+ videosingleton.cpp
+ videosingleton.h
+ qmlvideo_global.h
+ QML_FILES
+ "CameraBasic.qml"
+ "CameraDrag.qml"
+ "CameraDummy.qml"
+ "CameraFullScreen.qml"
+ "CameraFullScreenInverted.qml"
+ "CameraItem.qml"
+ "CameraMove.qml"
+ "CameraOverlay.qml"
+ "CameraResize.qml"
+ "CameraRotate.qml"
+ "CameraSpin.qml"
+ "Content.qml"
+ "ErrorDialog.qml"
+ "Main.qml"
+ "Scene.qml"
+ "SceneBasic.qml"
+ "SceneDrag.qml"
+ "SceneFullScreen.qml"
+ "SceneFullScreenInverted.qml"
+ "SceneMove.qml"
+ "SceneMulti.qml"
+ "SceneOverlay.qml"
+ "SceneResize.qml"
+ "SceneRotate.qml"
+ "SceneSelectionPanel.qml"
+ "SceneSpin.qml"
+ "SeekControl.qml"
+ "VideoBasic.qml"
+ "VideoDrag.qml"
+ "VideoDummy.qml"
+ "VideoFillMode.qml"
+ "VideoFullScreen.qml"
+ "VideoFullScreenInverted.qml"
+ "VideoItem.qml"
+ "VideoMetadata.qml"
+ "VideoMove.qml"
+ "VideoOverlay.qml"
+ "VideoPlaybackRate.qml"
+ "VideoResize.qml"
+ "VideoRotate.qml"
+ "VideoSeek.qml"
+ "VideoSpin.qml"
+ RESOURCES
+ "images/folder.png"
+ "images/leaves.jpg"
+ "images/up.png"
+)
+
+target_compile_definitions(qmlvideo PRIVATE QMLVIDEO_LIB)
+
+target_link_libraries(qmlvideo PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+)
+
+install(TARGETS qmlvideo
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
+ DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraBasic.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraBasic.qml
new file mode 100644
index 000000000..77072c6c9
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraBasic.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneBasic {
+ contentType: "camera"
+ started: true
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraDrag.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraDrag.qml
new file mode 100644
index 000000000..2ea3672c6
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraDrag.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneDrag {
+ contentType: "camera"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraDummy.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraDummy.qml
new file mode 100644
index 000000000..c9d14e595
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraDummy.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+// Item which is loaded by CameraItem if Qt Multimedia is not available
+Rectangle {
+ id: root
+ color: "grey"
+ height: width
+
+ signal fatalError
+ signal sizeChanged
+ signal framePainted
+
+ Label {
+ anchors.fill: parent
+ anchors.margins: 10
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Failed to create Camera item\n\nCheck that Qt Multimedia is installed")
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.Wrap
+ }
+
+ onWidthChanged: height = width
+ onHeightChanged: root.sizeChanged()
+
+ function start() { }
+ function stop() { }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreen.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreen.qml
new file mode 100644
index 000000000..b204e74ab
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreen.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneFullScreen {
+ contentType: "camera"
+}
+
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreenInverted.qml
new file mode 100644
index 000000000..ff7a40253
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraFullScreenInverted.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneFullScreenInverted {
+ contentType: "camera"
+}
+
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraItem.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraItem.qml
new file mode 100644
index 000000000..d6e4b85b5
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraItem.qml
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtMultimedia
+
+Item {
+ id: root
+ height: width
+
+ signal fatalError
+ signal sizeChanged
+
+ onHeightChanged: root.sizeChanged()
+
+ CaptureSession {
+ camera: Camera {
+ id: camera
+
+ onErrorOccurred: function(error, errorString) {
+ if (Camera.NoError !== error) {
+ console.log("[qmlvideo] CameraItem.onError error " + error + " errorString " + errorString)
+ root.fatalError()
+ }
+ }
+ }
+ imageCapture: ImageCapture {
+ id: imageCapture
+ }
+
+ recorder: MediaRecorder {
+ id: recorder
+// resolution: "640x480"
+// frameRate: 30
+ }
+ videoOutput: videoOutput
+ }
+
+ VideoOutput {
+ id: videoOutput
+ anchors.fill: parent
+ }
+
+
+ function start() { camera.start() }
+ function stop() { camera.stop() }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraMove.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraMove.qml
new file mode 100644
index 000000000..c4c5da914
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraMove.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneMove {
+ contentType: "camera"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraOverlay.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraOverlay.qml
new file mode 100644
index 000000000..13136be00
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraOverlay.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneOverlay {
+ contentType: "camera"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraResize.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraResize.qml
new file mode 100644
index 000000000..d89eef3ad
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraResize.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneResize {
+ contentType: "camera"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraRotate.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraRotate.qml
new file mode 100644
index 000000000..8394fb6fd
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraRotate.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneRotate {
+ contentType: "camera"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/CameraSpin.qml b/examples/multimedia/video/qmlvideo/qmlvideo/CameraSpin.qml
new file mode 100644
index 000000000..642a0edfd
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/CameraSpin.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneSpin {
+ contentType: "camera"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/Content.qml b/examples/multimedia/video/qmlvideo/qmlvideo/Content.qml
new file mode 100644
index 000000000..b08d19325
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/Content.qml
@@ -0,0 +1,125 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import frequencymonitor
+
+Rectangle {
+ id: root
+ border.color: palette.window
+ border.width: showBorder ? 1 : 0
+ color: "transparent"
+ property string contentType // "camera" or "video"
+ property string source
+ property real volume
+ property bool dummy: false
+ property bool autoStart: true
+ property bool started: false
+ property bool showFrameRate: false
+ property bool showBorder: false
+ property alias contentItem: contentLoader.item
+
+ signal initialized
+ signal error
+ signal videoFramePainted
+
+ Loader {
+ id: contentLoader
+ }
+
+ Connections {
+ id: framePaintedConnection
+ function onFramePainted() {
+ (frameRateLoader.item as FrequencyItem)?.notify()
+ root.videoFramePainted()
+ }
+ ignoreUnknownSignals: true
+ }
+
+ Connections {
+ id: errorConnection
+ function onFatalError() {
+ console.log("[qmlvideo] Content.onFatalError")
+ root.stop()
+ root.error()
+ }
+ ignoreUnknownSignals: true
+ }
+
+ Component {
+ id: frequencyItem
+ FrequencyItem {}
+ }
+
+ Loader {
+ id: frameRateLoader
+ sourceComponent: root.showFrameRate ? frequencyItem : undefined
+ onLoaded: {
+ item.parent = root
+ item.anchors.top = root.top
+ item.anchors.right = root.right
+ item.anchors.margins = 10
+ }
+ }
+
+ onWidthChanged: {
+ if (root.contentItem)
+ root.contentItem.width = width
+ }
+
+ onHeightChanged: {
+ if (root.contentItem)
+ root.contentItem.height = height
+ }
+
+ function initialize() {
+ if ("video" == contentType) {
+ contentLoader.source = "VideoItem.qml"
+ if (Loader.Error == contentLoader.status) {
+ contentLoader.source = "VideoDummy.qml"
+ dummy = true
+ }
+ contentLoader.item.volume = volume
+ } else if ("camera" == contentType) {
+ contentLoader.source = "CameraItem.qml"
+ if (Loader.Error == contentLoader.status) {
+ contentLoader.source = "CameraDummy.qml"
+ dummy = true
+ }
+ } else {
+ console.log("[qmlvideo] Content.initialize: error: invalid contentType")
+ }
+ if (contentLoader.item) {
+ contentLoader.item.sizeChanged.connect(updateRootSize)
+ contentLoader.item.parent = root
+ contentLoader.item.width = root.width
+ framePaintedConnection.target = contentLoader.item
+ errorConnection.target = contentLoader.item
+ if (root.autoStart)
+ root.start()
+ }
+ root.initialized()
+ }
+
+ function start() {
+ if (contentLoader.item) {
+ if (root.contentType == "video")
+ contentLoader.item.mediaSource = root.source
+ contentLoader.item.start()
+ root.started = true
+ }
+ }
+
+ // qmllint disable
+ function stop() {
+ if (contentLoader.item) {
+ contentLoader.item.stop()
+ if (root.contentType == "video")
+ contentLoader.item.mediaSource = ""
+ root.started = false
+ }
+ }
+ // qmllint enable
+
+ function updateRootSize() { root.height = (root.contentItem as Item).height }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/ErrorDialog.qml b/examples/multimedia/video/qmlvideo/qmlvideo/ErrorDialog.qml
new file mode 100644
index 000000000..f06079a35
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/ErrorDialog.qml
@@ -0,0 +1,70 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: root
+ color: "transparent"
+ opacity: 0.0
+ property alias enabled: mouseArea.enabled
+ property int dialogWidth: 300
+ property int dialogHeight: 200
+ state: enabled ? "on" : "baseState"
+
+ states: [
+ State {
+ name: "on"
+ PropertyChanges {
+ root.opacity: 1.0
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "*"
+ to: "*"
+ NumberAnimation {
+ properties: "opacity"
+ easing.type: Easing.OutQuart
+ duration: 500
+ }
+ }
+ ]
+
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
+ opacity: 0.75
+ }
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: root.dialogWidth
+ height: root.dialogHeight
+ radius: 5
+ color: "white"
+
+ Text {
+ id: text
+ anchors.fill: parent
+ anchors.margins: 10
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: "black"
+ wrapMode: Text.WordWrap
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onClicked: root.enabled = false
+ }
+
+ function show(msg) {
+ text.text = "<b>Error</b><br><br>" + msg
+ root.enabled = true
+ }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/Main.qml b/examples/multimedia/video/qmlvideo/qmlvideo/Main.qml
new file mode 100644
index 000000000..49b94590c
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/Main.qml
@@ -0,0 +1,188 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Dialogs
+import performancemonitor
+
+Rectangle {
+ id: root
+ anchors.fill: parent
+ color: palette.window
+
+ property bool perfMonitorsLogging: false
+ property bool perfMonitorsVisible: false
+
+ Loader {
+ id: performanceLoader
+
+ Connections {
+ target: columnLayout
+ function onVisibleChanged() {
+ if (performanceLoader.item)
+ performanceLoader.item.enabled = !columnLayout.visible
+ }
+ ignoreUnknownSignals: true
+ }
+
+ Component {
+ id: performanceItem
+ PerformanceItem {}
+ }
+
+ function init() {
+ var enabled = root.perfMonitorsLogging || root.perfMonitorsVisible
+ sourceComponent = enabled ? performanceItem : undefined
+ }
+
+ onLoaded: {
+ item.parent = root
+ item.anchors.fill = root
+ item.logging = root.perfMonitorsLogging
+ item.displayed = root.perfMonitorsVisible
+ item.enabled = false
+ item.init()
+ }
+ }
+
+ ColumnLayout {
+ id: columnLayout
+ anchors.fill: parent
+ spacing: 5
+
+ Button {
+ id: openFile1Button
+ text: (VideoSingleton.source1 == '') ? qsTr("Select file 1") : VideoSingleton.source1
+ Component.onCompleted: console.log("source1: " + VideoSingleton.source1)
+ onClicked: {
+ fileDialog.setFirstSource = true
+ fileDialog.open()
+ }
+
+ Layout.fillWidth: true
+ }
+
+ Button {
+ id: openFile2Button
+ text: (VideoSingleton.source2 == '') ? qsTr("Select file 2") : VideoSingleton.source2
+ Component.onCompleted: console.log("source2: " + VideoSingleton.source2)
+ onClicked: {
+ fileDialog.setFirstSource = false
+ fileDialog.open()
+ }
+
+ Layout.fillWidth: true
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+
+ Label {
+ text: qsTr("Video Modes")
+
+ horizontalAlignment: Qt.AlignHCenter
+ Layout.preferredWidth: 50
+ Layout.fillWidth: true
+ }
+ Label {
+ text: qsTr("Camera Modes")
+
+ horizontalAlignment: Qt.AlignHCenter
+ Layout.preferredWidth: 50
+ Layout.fillWidth: true
+ }
+ }
+
+ SceneSelectionPanel {
+ id: sceneSelectionPanel
+ itemHeight: Math.min(width / 10, height / 10)
+ color: palette.dark
+ radius: 0
+ onSceneSourceChanged: {
+ sceneLoader.source = sceneSource
+ var scene = null
+ var innerVisible = true
+ if (sceneSource == "") {
+ if (performanceLoader.item)
+ performanceLoader.item.videoActive = false
+ } else {
+ scene = sceneLoader.item
+ if (scene) {
+ if (scene.contentType === "video" && VideoSingleton.source1 === "") {
+ errorDialog.show(qsTr("You must first select a video file"))
+ sceneSource = ""
+ } else {
+ scene.parent = root
+ scene.color = root.palette.window
+ scene.source1 = VideoSingleton.source1
+ scene.source2 = VideoSingleton.source2
+ scene.volume = VideoSingleton.volume
+ scene.anchors.fill = root
+ scene.close.connect(closeScene)
+ scene.content.initialize()
+ innerVisible = false
+ }
+ }
+ }
+ videoFramePaintedConnection.target = scene
+ columnLayout.visible = innerVisible
+ }
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+
+ Loader {
+ id: sceneLoader
+ }
+
+ Connections {
+ id: videoFramePaintedConnection
+ // qmllint disable
+ function onVideoFramePainted() {
+ if (performanceLoader.item)
+ performanceLoader.item.videoFramePainted()
+ }
+ // qmllint enable
+ ignoreUnknownSignals: true
+ }
+
+ FileDialog {
+ id: fileDialog
+ property bool setFirstSource
+ onAccepted: function() {
+ if (setFirstSource)
+ VideoSingleton.source1 = selectedFile
+ else
+ VideoSingleton.source2 = selectedFile
+ }
+ }
+
+ ErrorDialog {
+ id: errorDialog
+ anchors.fill: root
+ dialogWidth: Math.min(root.width, root.height) * 0.5
+ dialogHeight: Math.min(root.width, root.height) * 0.3
+ enabled: false
+ }
+
+ // Called from main() once root properties have been set
+ function init() {
+ performanceLoader.init()
+ fileDialog.currentFolder = VideoSingleton.videoPath
+ }
+
+ // qmllint disable
+ function qmlFramePainted() {
+ if (performanceLoader.item)
+ performanceLoader.item.qmlFramePainted()
+ }
+ // qmllint enable
+
+ function closeScene() {
+ sceneSelectionPanel.sceneSource = ""
+ }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/Scene.qml b/examples/multimedia/video/qmlvideo/qmlvideo/Scene.qml
new file mode 100644
index 000000000..e16478d41
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/Scene.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+ id: root
+ color: palette.window
+ property string source1
+ property string source2
+ property int contentWidth: parent.width / 2
+ property real volume: 0.25
+ property int margins: 5
+ property QtObject content
+
+ signal close
+ signal videoFramePainted
+
+ Button {
+ id: closeButton
+ anchors {
+ top: parent.top
+ right: parent.right
+ margins: root.margins
+ }
+ z: 2.0
+ text: qsTr("Back")
+ onClicked: root.close()
+ }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneBasic.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneBasic.qml
new file mode 100644
index 000000000..8ad6c99b8
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneBasic.qml
@@ -0,0 +1,47 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Scene {
+ id: root
+ property string contentType
+ property bool autoStart: false
+ property bool started: false
+
+ Content {
+ id: content
+ autoStart: parent.autoStart
+ started: parent.started
+ anchors.fill: parent
+ width: parent.contentWidth
+ contentType: parent.contentType
+ source: parent.source1
+ volume: parent.volume
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ Label {
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ bottom: parent.bottom
+ margins: 20
+ }
+ text: content.started ? qsTr("Tap the screen to stop content")
+ : qsTr("Tap the screen to start content")
+ z: 2.0
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (content.started)
+ content.stop()
+ else
+ content.start()
+ }
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneDrag.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneDrag.qml
new file mode 100644
index 000000000..99a7cb7f1
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneDrag.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Scene {
+ id: root
+ property int margin: 20
+ property string contentType
+
+ Image {
+ id: background
+ source: "images/leaves.jpg"
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: root.contentWidth
+ contentType: root.contentType
+ source: root.source1
+ volume: root.volume
+ onVideoFramePainted: root.videoFramePainted()
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: background
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreen.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreen.qml
new file mode 100644
index 000000000..ec8564311
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreen.qml
@@ -0,0 +1,67 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Scene {
+ id: root
+ property string contentType
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: root.contentType
+ source: parent.source1
+ volume: parent.volume
+ state: "left"
+
+ states: [
+ State {
+ name: "fullScreen"
+ PropertyChanges {
+ content.width: content.parent.width
+ content.height: content.parent.height
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ ParallelAnimation {
+ PropertyAnimation {
+ property: "width"
+ easing.type: Easing.Linear
+ duration: 250
+ }
+ PropertyAnimation {
+ property: "height"
+ easing.type: Easing.Linear
+ duration: 250
+ }
+ }
+ }
+ ]
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: content.state = (content.state == "fullScreen") ? "baseState" : "fullScreen"
+ }
+
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ Label {
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ bottom: parent.bottom
+ margins: 20
+ }
+ text: qsTr("Tap on the content to toggle full-screen mode")
+ z: 2.0
+ }
+
+ Component.onCompleted: root.content = content
+}
+
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreenInverted.qml
new file mode 100644
index 000000000..cb96ac134
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneFullScreenInverted.qml
@@ -0,0 +1,70 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Scene {
+ id: root
+ property string contentType
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.width
+ height: parent.height
+ contentType: root.contentType
+ source: parent.source1
+ volume: parent.volume
+ state: "left"
+
+ states: [
+ State {
+ name: "nonFullScreen"
+ PropertyChanges { content.width: root.contentWidth }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ ParallelAnimation {
+ PropertyAnimation {
+ property: "width"
+ easing.type: Easing.Linear
+ duration: 250
+ }
+ PropertyAnimation {
+ property: "height"
+ easing.type: Easing.Linear
+ duration: 250
+ }
+ }
+ }
+ ]
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: content.state = (content.state === "nonFullScreen") ? "baseState" : "nonFullScreen"
+ }
+
+ onVideoFramePainted: root.videoFramePainted()
+
+ onInitialized: {
+ width = parent.width
+ height = parent.height
+ }
+ }
+
+ Label {
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ bottom: parent.bottom
+ margins: 20
+ }
+ text: qsTr("Tap on the content to toggle full-screen mode")
+ z: 2.0
+ }
+
+ Component.onCompleted: root.content = content
+}
+
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneMove.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneMove.qml
new file mode 100644
index 000000000..d1512831e
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneMove.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Scene {
+ id: root
+ property int margin: 20
+ property string contentType
+
+ Content {
+ id: content
+ anchors.verticalCenter: parent.verticalCenter
+ width: parent.contentWidth
+ contentType: root.contentType
+ source: parent.source1
+ volume: parent.volume
+
+ SequentialAnimation on x {
+ id: animation
+ loops: Animation.Infinite
+ property int from: root.margin
+ property int to: 100
+ property int duration: 1500
+ running: false
+ PropertyAnimation {
+ from: animation.from
+ to: animation.to
+ duration: animation.duration
+ easing.type: Easing.InOutCubic
+ }
+ PropertyAnimation {
+ from: animation.to
+ to: animation.from
+ duration: animation.duration
+ easing.type: Easing.InOutCubic
+ }
+ }
+
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ onWidthChanged: {
+ animation.to = root.width - content.width - margin
+ animation.start()
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMulti.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneMulti.qml
index f06c187e4..f93dabcdd 100644
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SceneMulti.qml
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneMulti.qml
@@ -1,54 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
+import QtQuick.Controls
Scene {
id: root
@@ -71,28 +25,30 @@ Scene {
id: root
color: "transparent"
- function content() {
- return root.parent
- }
+ signal start
+ signal stop
- Text {
+ Label {
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
margins: 20
}
- text: content() ? content().started ? "Tap to stop" : "Tap to start" : ""
- color: "#e0e0e0"
+ // qmllint disable
+ text: root.started ? qsTr("Tap to stop") : qsTr("Tap to start")
+ // qmllint enable
}
MouseArea {
anchors.fill: parent
+ // qmllint disable
onClicked: {
- if (content().started)
- content().stop()
+ if (root.started)
+ root.stop()
else
- content().start()
+ root.start()
}
+ // qmllint enable
}
}
}
@@ -110,14 +66,19 @@ Scene {
showBorder: true
showFrameRate: started
source: parent.source1
- width: itemWidth
+ width: root.itemWidth
volume: parent.volume
Loader {
id: video1StartStopLoader
+
+ property bool started: parent.started
+
onLoaded: {
item.parent = video1
item.anchors.fill = video1
+ item.start.connect(video1.start)
+ item.stop.connect(video1.stop)
}
}
@@ -126,26 +87,28 @@ Scene {
Rectangle {
id: cameraHolder
+
+ property bool started: false
+
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: root.itemTopMargin
}
border.width: 1
- border.color: "white"
+ border.color: palette.base
color: "transparent"
- width: itemWidth
+ width: root.itemWidth
height: width
- property bool started: false
Loader {
id: cameraLoader
onLoaded: {
item.parent = cameraHolder
- item.centerIn = cameraHolder
+ item.anchors.centerIn = cameraHolder
item.contentType = "camera"
item.showFrameRate = true
- item.width = itemWidth
+ item.width = root.itemWidth
item.z = 1.0
cameraErrorConnection.target = item
item.initialize()
@@ -154,20 +117,26 @@ Scene {
Loader {
id: cameraStartStopLoader
+
+ property bool started: parent.started
+
sourceComponent: startStopComponent
onLoaded: {
item.parent = cameraHolder
item.anchors.fill = cameraHolder
item.z = 2.0
+ item.start.connect(cameraHolder.start)
+ item.stop.connect(cameraHolder.stop)
}
}
Connections {
id: cameraErrorConnection
- onError: {
+ function onError() {
console.log("[qmlvideo] SceneMulti.camera.onError")
cameraHolder.stop()
}
+ ignoreUnknownSignals: true
}
function start() {
@@ -194,14 +163,19 @@ Scene {
showBorder: true
showFrameRate: started
source: parent.source2
- width: itemWidth
+ width: root.itemWidth
volume: parent.volume
Loader {
id: video2StartStopLoader
+
+ property bool started: parent.started
+
onLoaded: {
item.parent = video2
item.anchors.fill = video2
+ item.start.connect(video2.start)
+ item.stop.connect(video2.stop)
}
}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneOverlay.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneOverlay.qml
new file mode 100644
index 000000000..5863d618c
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneOverlay.qml
@@ -0,0 +1,83 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Scene {
+ id: root
+ property int margin: 20
+ property string contentType
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: root.contentType
+ source: parent.source1
+ volume: parent.volume
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ Rectangle {
+ id: overlay
+ y: 0.5 * parent.height
+ width: content.width
+ height: content.height
+ color: "#e0e0e0"
+ opacity: 0.5
+
+ SequentialAnimation on x {
+ id: xAnimation
+ loops: Animation.Infinite
+ property int from: root.margin
+ property int to: 100
+ property int duration: 1500
+ running: false
+ PropertyAnimation {
+ from: xAnimation.from
+ to: xAnimation.to
+ duration: xAnimation.duration
+ easing.type: Easing.InOutCubic
+ }
+ PropertyAnimation {
+ from: xAnimation.to
+ to: xAnimation.from
+ duration: xAnimation.duration
+ easing.type: Easing.InOutCubic
+ }
+ }
+
+ SequentialAnimation on y {
+ id: yAnimation
+ loops: Animation.Infinite
+ property int from: root.margin
+ property int to: 180
+ property int duration: 1500
+ running: false
+ PropertyAnimation {
+ from: yAnimation.from
+ to: yAnimation.to
+ duration: yAnimation.duration
+ easing.type: Easing.InOutCubic
+ }
+ PropertyAnimation {
+ from: yAnimation.to
+ to: yAnimation.from
+ duration: yAnimation.duration
+ easing.type: Easing.InOutCubic
+ }
+ }
+ }
+
+ onWidthChanged: {
+ xAnimation.to = root.width - content.width - margin
+ xAnimation.start()
+ }
+
+ onHeightChanged: {
+ //yAnimation.to = root.height - content.height - margin
+ yAnimation.start()
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneResize.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneResize.qml
new file mode 100644
index 000000000..4e2f15588
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneResize.qml
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Scene {
+ id: root
+ property string contentType
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: root.contentType
+ source: parent.source1
+ volume: parent.volume
+
+ SequentialAnimation on scale {
+ id: animation
+ loops: Animation.Infinite
+ property int duration: 1500
+ running: true
+ PropertyAnimation {
+ from: 1.5
+ to: 0.5
+ duration: animation.duration
+ easing.type: Easing.InOutCubic
+ }
+ PropertyAnimation {
+ from: 0.5
+ to: 1.5
+ duration: animation.duration
+ easing.type: Easing.InOutCubic
+ }
+ }
+
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneRotate.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneRotate.qml
new file mode 100644
index 000000000..1362522e6
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneRotate.qml
@@ -0,0 +1,57 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Scene {
+ id: root
+ property int margin: 20
+ property int delta: 30
+ property string contentType
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: root.contentType
+ source: parent.source1
+ volume: parent.volume
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ Button {
+ id: rotatePositiveButton
+ anchors {
+ right: parent.right
+ bottom: rotateNegativeButton.top
+ margins: parent.margins
+ }
+ text: qsTr("Rotate +%1").arg(root.delta)
+ onClicked: content.rotation = content.rotation + root.delta
+ }
+
+ Button {
+ id: rotateNegativeButton
+ anchors {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ margins: parent.margins
+ }
+ text: qsTr("Rotate -%1").arg(root.delta)
+ onClicked: content.rotation = content.rotation - root.delta
+ }
+
+ Button {
+ id: rotateValueButton
+ anchors {
+ left: parent.left
+ verticalCenter: parent.verticalCenter
+ margins: parent.margins
+ }
+ enabled: false
+ text: content.rotation % 360
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneSelectionPanel.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneSelectionPanel.qml
new file mode 100644
index 000000000..91e7c6ad4
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneSelectionPanel.qml
@@ -0,0 +1,88 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+pragma ComponentBehavior: Bound
+
+Rectangle {
+ id: root
+ property int itemHeight: 25
+ property string sceneSource: ""
+
+ ListModel {
+ id: videolist
+ ListElement { name: qsTr("Multi"); source: "SceneMulti.qml" }
+ ListElement { name: qsTr("Video"); source: "VideoBasic.qml" }
+ ListElement { name: qsTr("Drag"); source: "VideoDrag.qml" }
+ ListElement { name: qsTr("Fillmode"); source: "VideoFillMode.qml" }
+ ListElement { name: qsTr("Fullscreen"); source: "VideoFullScreen.qml" }
+ ListElement { name: qsTr("Fullscreen-inverted"); source: "VideoFullScreenInverted.qml" }
+ ListElement { name: qsTr("Metadata"); source: "VideoMetadata.qml" }
+ ListElement { name: qsTr("Move"); source: "VideoMove.qml" }
+ ListElement { name: qsTr("Overlay"); source: "VideoOverlay.qml" }
+ ListElement { name: qsTr("Playback Rate"); source: "VideoPlaybackRate.qml" }
+ ListElement { name: qsTr("Resize"); source: "VideoResize.qml" }
+ ListElement { name: qsTr("Rotate"); source: "VideoRotate.qml" }
+ ListElement { name: qsTr("Spin"); source: "VideoSpin.qml" }
+ ListElement { name: qsTr("Seek"); source: "VideoSeek.qml" }
+ }
+
+ ListModel {
+ id: cameralist
+ ListElement { name: qsTr("Camera"); source: "CameraBasic.qml" }
+ ListElement { name: qsTr("Drag"); source: "CameraDrag.qml" }
+ ListElement { name: qsTr("Fullscreen"); source: "CameraFullScreen.qml" }
+ ListElement { name: qsTr("Fullscreen-inverted"); source: "CameraFullScreenInverted.qml" }
+ ListElement { name: qsTr("Move"); source: "CameraMove.qml" }
+ ListElement { name: qsTr("Overlay"); source: "CameraOverlay.qml" }
+ ListElement { name: qsTr("Resize"); source: "CameraResize.qml" }
+ ListElement { name: qsTr("Rotate"); source: "CameraRotate.qml" }
+ ListElement { name: qsTr("Spin"); source: "CameraSpin.qml" }
+ }
+
+ Component {
+ id: buttonDelegate
+ Button {
+ required property string name
+ required property string source
+
+ width: root.width / 2
+ height: root.itemHeight
+
+ text: name
+ padding: 3
+ onClicked: root.sceneSource = source
+ }
+ }
+
+ Flickable {
+ anchors.fill: parent
+ contentHeight: (root.itemHeight * videolist.count) + 10
+ clip: true
+
+ Row {
+ id: layout
+ anchors {
+ fill: parent
+ topMargin: 5
+ bottomMargin: 5
+ }
+
+ Column {
+ Repeater {
+ model: videolist
+ delegate: buttonDelegate
+ }
+ }
+
+ Column {
+ Repeater {
+ model: cameralist
+ delegate: buttonDelegate
+ }
+ }
+ }
+ }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/SceneSpin.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SceneSpin.qml
new file mode 100644
index 000000000..3e280d3b1
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SceneSpin.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Scene {
+ id: root
+ property int margin: 20
+ property string contentType
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: root.contentType
+ source: parent.source1
+ volume: parent.volume
+
+ PropertyAnimation on rotation {
+ id: animation
+ loops: Animation.Infinite
+ running: true
+ from: 0
+ to: 360
+ duration: 3000
+ easing.type: Easing.Linear
+ }
+
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SeekControl.qml b/examples/multimedia/video/qmlvideo/qmlvideo/SeekControl.qml
index b5bb804da..646e5e8ef 100644
--- a/examples/multimedia/video/qmlvideo/qml/qmlvideo/SeekControl.qml
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/SeekControl.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
@@ -62,7 +15,7 @@ Item {
Rectangle {
id: background
anchors.fill: parent
- color: "white"
+ color: palette.base
opacity: 0.3
radius: parent.height / 15
}
@@ -71,7 +24,7 @@ Item {
id: progressBar
anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
width: seekControl.duration == 0 ? 0 : background.width * seekControl.playPosition / seekControl.duration
- color: "black"
+ color: palette.highlight
opacity: 0.7
}
@@ -80,9 +33,9 @@ Item {
anchors { left: parent.left; top: parent.top; bottom: parent.bottom; leftMargin: 10 }
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
- color: "white"
+ color: palette.windowText
smooth: true
- text: formatTime(playPosition)
+ text: seekControl.formatTime(seekControl.playPosition)
}
Text {
@@ -90,16 +43,16 @@ Item {
anchors { right: parent.right; top: parent.top; bottom: parent.bottom; rightMargin: 10 }
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: "white"
+ color: palette.windowText
smooth: true
- text: formatTime(duration)
+ text: seekControl.formatTime(seekControl.duration)
}
Rectangle {
id: progressHandle
height: parent.height
width: parent.height / 2
- color: "white"
+ color: palette.accent
opacity: 0.5
anchors.verticalCenter: progressBar.verticalCenter
x: seekControl.duration == 0 ? 0 : seekControl.playPosition / seekControl.duration * background.width
@@ -143,8 +96,8 @@ Item {
function formatTime(timeInMs) {
if (!timeInMs || timeInMs <= 0) return "0:00"
- var seconds = timeInMs / 1000;
- var minutes = Math.floor(seconds / 60)
+ let seconds = timeInMs / 1000;
+ let minutes = Math.floor(seconds / 60)
seconds = Math.floor(seconds % 60)
if (seconds < 10) seconds = "0" + seconds;
return minutes + ":" + seconds
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoBasic.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoBasic.qml
new file mode 100644
index 000000000..15c25c978
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoBasic.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneBasic {
+ contentType: "video"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoDrag.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoDrag.qml
new file mode 100644
index 000000000..4a5c5d49b
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoDrag.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneDrag {
+ contentType: "video"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoDummy.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoDummy.qml
new file mode 100644
index 000000000..a30b9ebf6
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoDummy.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+// Item which is loaded by VideoItem if Qt Multimedia is not available
+Rectangle {
+ id: root
+ color: "grey"
+ height: width
+ property int duration: 0
+ property int position: 0
+ property string source
+ property real volume: 1.0
+ property real playbackRate: 1.0
+
+ signal fatalError
+ signal sizeChanged
+ signal framePainted
+
+ Label {
+ anchors.fill: parent
+ anchors.margins: 10
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Failed to create Video item\n\nCheck that Qt Multimedia is installed")
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.Wrap
+ }
+
+ onWidthChanged: height = width
+ onHeightChanged: root.sizeChanged()
+
+ function start() { }
+ function stop() { }
+ function seek() { }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoFillMode.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFillMode.qml
new file mode 100644
index 000000000..af950c735
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFillMode.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtMultimedia
+
+Scene {
+ id: root
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: "video"
+ source: parent.source1
+ volume: parent.volume
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ Button {
+ id: button
+ anchors {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ margins: parent.margins
+ }
+ text: qsTr("PreserveAspectFit")
+ // qmllint disable
+ onClicked: {
+ if (!content.dummy) {
+ let video = content.contentItem
+ if (video.fillMode === VideoOutput.Stretch) {
+ video.fillMode = VideoOutput.PreserveAspectFit
+ text = qsTr("PreserveAspectFit")
+ } else if (video.fillMode === VideoOutput.PreserveAspectFit) {
+ video.fillMode = VideoOutput.PreserveAspectCrop
+ text = qsTr("PreserveAspectCrop")
+ } else {
+ video.fillMode = VideoOutput.Stretch
+ text = qsTr("Stretch")
+ }
+ }
+ }
+ // qmllint enable
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreen.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreen.qml
new file mode 100644
index 000000000..e49faadb6
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreen.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneFullScreen {
+ contentType: "video"
+}
+
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreenInverted.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreenInverted.qml
new file mode 100644
index 000000000..0bf82941e
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoFullScreenInverted.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneFullScreenInverted {
+ contentType: "video"
+}
+
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoItem.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoItem.qml
new file mode 100644
index 000000000..2ea796de8
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoItem.qml
@@ -0,0 +1,42 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtMultimedia
+
+VideoOutput {
+ id: root
+ height: width
+
+ property alias duration: mediaPlayer.duration
+ property alias mediaSource: mediaPlayer.source
+ property alias metaData: mediaPlayer.metaData
+ property alias playbackRate: mediaPlayer.playbackRate
+ property alias position: mediaPlayer.position
+ property alias seekable: mediaPlayer.seekable
+ property alias volume: audioOutput.volume
+
+ signal sizeChanged
+ signal fatalError
+
+ onHeightChanged: root.sizeChanged()
+
+ MediaPlayer {
+ id: mediaPlayer
+ videoOutput: root;
+ audioOutput: AudioOutput {
+ id: audioOutput
+ }
+
+ onErrorOccurred: function(error, errorString) {
+ if (MediaPlayer.NoError !== error) {
+ console.log("[qmlvideo] VideoItem.onError error " + error + " errorString " + errorString)
+ root.fatalError()
+ }
+ }
+ }
+
+ function start() { mediaPlayer.play() }
+ function stop() { mediaPlayer.stop() }
+ function seek(position) { mediaPlayer.setPosition(position); }
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoMetadata.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoMetadata.qml
new file mode 100644
index 000000000..f83cbffe9
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoMetadata.qml
@@ -0,0 +1,80 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtMultimedia
+
+pragma ComponentBehavior: Bound
+
+Scene {
+ id: root
+ property string contentType: "video"
+
+ Content {
+ id: videoContent
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: "video"
+ source: parent.source1
+ volume: parent.volume
+ onInitialized: {
+ if (!dummy)
+ metadata.createObject(root)
+ }
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ Component {
+ id: metadata
+ Column {
+ anchors.fill: parent
+ // qmllint disable
+ property var videoMetaData: videoContent.contentItem?.metaData
+ // qmllint enable
+ Label {
+ text: qsTr("Title: %1").arg(parent.videoMetaData?.value(MediaMetaData.Title) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Resolution: %1").arg(parent.videoMetaData?.value(MediaMetaData.Resolution) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Media type: %1").arg(parent.videoMetaData?.value(MediaMetaData.MediaType) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Video codec: %1").arg(parent.videoMetaData?.value(MediaMetaData.VideoCodec) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Video bit rate: %1").arg(parent.videoMetaData?.value(MediaMetaData.VideoBitRate) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Video frame rate: %1").arg(parent.videoMetaData?.value(MediaMetaData.VideoFrameRate) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Audio codec: %1").arg(parent.videoMetaData?.value(MediaMetaData.AudioCodec) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Audio bit rate: %1").arg(parent.videoMetaData?.value(MediaMetaData.AudioBitRate) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Date: %1").arg(parent.videoMetaData?.value(MediaMetaData.Date) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Description: %1").arg(parent.videoMetaData?.value(MediaMetaData.Description) ?? qsTr("Unknown"))
+ }
+ Label {
+ text: qsTr("Copyright: %1").arg(parent.videoMetaData?.value(MediaMetaData.Copyright) ?? qsTr("Unknown"))
+ }
+ Label {
+ // qmllint disable
+ text: qsTr("Seekable: %1").arg(videoContent.contentItem?.seekable ?? qsTr("Unknown"))
+ // qmllint enable
+ }
+ Label {
+ text: qsTr("Orientation: %1").arg(parent.videoMetaData?.value(MediaMetaData.Orientation) ?? qsTr("Unknown"))
+ }
+ }
+ }
+
+ Component.onCompleted: root.content = videoContent
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoMove.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoMove.qml
new file mode 100644
index 000000000..2b9230f3c
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoMove.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneMove {
+ contentType: "video"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoOverlay.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoOverlay.qml
new file mode 100644
index 000000000..576b33ff9
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoOverlay.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneOverlay {
+ contentType: "video"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoPlaybackRate.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoPlaybackRate.qml
new file mode 100644
index 000000000..55e38ef19
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoPlaybackRate.qml
@@ -0,0 +1,65 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Scene {
+ id: root
+ property int margin: 20
+ property real delta: 0.1
+ property string contentType: "video"
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: "video"
+ source: parent.source1
+ volume: parent.volume
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ Button {
+ id: increaseButton
+ anchors {
+ right: parent.right
+ bottom: decreaseButton.top
+ margins: parent.margins
+ }
+ text: qsTr("Increase")
+ onClicked: {
+ let video = (content.contentItem as VideoItem)
+ video.playbackRate += root.delta
+ }
+ }
+
+ Button {
+ id: decreaseButton
+ anchors {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ margins: parent.margins
+ }
+ text: qsTr("Decrease")
+ onClicked: {
+ let video = (content.contentItem as VideoItem)
+ video.playbackRate -= root.delta
+ }
+ }
+
+ Button {
+ id: valueButton
+ anchors {
+ left: parent.left
+ verticalCenter: parent.verticalCenter
+ margins: parent.margins
+ }
+ enabled: false
+ // qmllint disable
+ text: Math.round(10 * content.contentItem?.playbackRate ?? 1) / 10
+ // qmllint enable
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoResize.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoResize.qml
new file mode 100644
index 000000000..88fe7a2d2
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoResize.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneResize {
+ contentType: "video"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoRotate.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoRotate.qml
new file mode 100644
index 000000000..d429ec413
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoRotate.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneRotate {
+ contentType: "video"
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoSeek.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoSeek.qml
new file mode 100644
index 000000000..5e2584fb6
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoSeek.qml
@@ -0,0 +1,36 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Scene {
+ id: root
+ property string contentType: "video"
+ contentWidth: parent.width
+
+ Content {
+ id: content
+ anchors.centerIn: parent
+ width: parent.contentWidth
+ contentType: "video"
+ source: parent.source1
+ volume: parent.volume
+ onVideoFramePainted: root.videoFramePainted()
+ }
+
+ SeekControl {
+ anchors {
+ left: parent.left
+ right: parent.right
+ margins: 10
+ bottom: parent.bottom
+ }
+ // qmllint disable
+ duration: content.contentItem?.duration ?? 0
+ playPosition: content.contentItem?.position ?? 0
+ onSeekPositionChanged: content.contentItem?.seek(seekPosition);
+ // qmllint enable
+ }
+
+ Component.onCompleted: root.content = content
+}
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/VideoSpin.qml b/examples/multimedia/video/qmlvideo/qmlvideo/VideoSpin.qml
new file mode 100644
index 000000000..7c365ddc7
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/VideoSpin.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+SceneSpin {
+ contentType: "video"
+}
diff --git a/examples/multimedia/video/qmlvideo/images/folder.png b/examples/multimedia/video/qmlvideo/qmlvideo/images/folder.png
index 62d97004f..62d97004f 100644
--- a/examples/multimedia/video/qmlvideo/images/folder.png
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/images/folder.png
Binary files differ
diff --git a/examples/multimedia/video/qmlvideo/images/leaves.jpg b/examples/multimedia/video/qmlvideo/qmlvideo/images/leaves.jpg
index 66533b34a..66533b34a 100644
--- a/examples/multimedia/video/qmlvideo/images/leaves.jpg
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/images/leaves.jpg
Binary files differ
diff --git a/examples/multimedia/video/qmlvideo/images/up.png b/examples/multimedia/video/qmlvideo/qmlvideo/images/up.png
index 6823de004..6823de004 100644
--- a/examples/multimedia/video/qmlvideo/images/up.png
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/images/up.png
Binary files differ
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/qmldir b/examples/multimedia/video/qmlvideo/qmlvideo/qmldir
new file mode 100644
index 000000000..1cba13d95
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/qmldir
@@ -0,0 +1,44 @@
+module qmlvideo
+
+CameraBasic 1.0 CameraBasic.qml
+CameraDrag 1.0 CameraDrag.qml
+CameraDummy 1.0 CameraDummy.qml
+CameraFullScreen 1.0 CameraFullScreen.qml
+CameraFullScreenInverted 1.0 CameraFullScreenInverted.qml
+CameraItem 1.0 CameraItem.qml
+CameraMove 1.0 CameraMove.qml
+CameraOverlay 1.0 CameraOverlay.qml
+CameraResize 1.0 CameraResize.qml
+CameraRotate 1.0 CameraRotate.qml
+CameraSpin 1.0 CameraSpin.qml
+CameraContent 1.0 Content.qml
+ErrorDialog 1.0 ErrorDialog.qml
+Scene 1.0 Scene.qml
+SceneBasic 1.0 SceneBasic.qml
+SceneDrag 1.0 SceneDrag.qml
+SceneFullScreen 1.0 SceneFullScreen.qml
+SceneFullScreeninverted 1.0 SceneFullScreenInverted.qml
+SceneMoved 1.0 SceneMove.qml
+SceneMulti 1.0 SceneMulti.qml
+SceneOverlay 1.0 SceneOverlay.qml
+SceneResize 1.0 SceneResize.qml
+SceneRotate 1.0 SceneRotate.qml
+SceneSelectionPanel 1.0 SceneSelectionPanel.qml
+SceneSpin 1.0 SceneSpin.qml
+SeekControl 1.0 SeekControl.qml
+VideoBasic 1.0 VideoBasic.qml
+VideoDrag 1.0 VideoDrag.qml
+VideoDummy 1.0 VideoDummy.qml
+VideoFillMode 1.0 VideoFillMode.qml
+VideoFullScreen 1.0 VideoFullScreen.qml
+VideoFullScreenInverted 1.0 VideoFullScreenInverted.qml
+VideoItem 1.0 VideoItem.qml
+VideoMetadata 1.0 VideoMetadata.qml
+VideoMove 1.0 VideoMove.qml
+VideoOverlay 1.0 VideoOverlay.qml
+VideoPlaybackRate 1.0 VideoPlaybackRate.qml
+VideoResize 1.0 VideoResize.qml
+VideoRotate 1.0 VideoRotate.qml
+VideoSeek 1.0 VideoSeek.qml
+VideoSpin 1.0 VideoSpin.qml
+Main 1.0 Main.qml
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/qmlvideo_global.h b/examples/multimedia/video/qmlvideo/qmlvideo/qmlvideo_global.h
new file mode 100644
index 000000000..8df5b5b64
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/qmlvideo_global.h
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore/QtGlobal>
+
+#if defined(QMLVIDEO_LIB)
+#define QMLVIDEO_LIB_EXPORT Q_DECL_EXPORT
+#else
+#define QMLVIDEO_LIB_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.cpp b/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.cpp
new file mode 100644
index 000000000..2b02fa505
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.cpp
@@ -0,0 +1,54 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "videosingleton.h"
+
+VideoSingleton::VideoSingleton(QObject * parent) : QObject(parent)
+{ }
+
+QUrl VideoSingleton::source1() const
+{
+ return m_source1;
+}
+void VideoSingleton::setSource1(const QUrl &source1)
+{
+ if (source1 == m_source1)
+ return;
+ m_source1 = source1;
+ emit source1Changed();
+}
+QUrl VideoSingleton::source2() const
+{
+ return m_source2;
+}
+void VideoSingleton::setSource2(const QUrl &source2)
+{
+ if (source2 == m_source2)
+ return;
+ m_source2 = source2;
+ emit source2Changed();
+}
+qreal VideoSingleton::volume() const
+{
+ return m_volume;
+}
+void VideoSingleton::setVolume(qreal volume)
+{
+ if (volume == m_volume)
+ return;
+ m_volume = volume;
+ emit volumeChanged();
+}
+QUrl VideoSingleton::videoPath() const
+{
+ return m_videoPath;
+}
+void VideoSingleton::setVideoPath(const QUrl &videoPath)
+{
+ if (m_videoPath == videoPath)
+ return;
+ m_videoPath = videoPath;
+ emit videoPathChanged();
+}
+
+#include "moc_videosingleton.cpp"
diff --git a/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.h b/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.h
new file mode 100644
index 000000000..3e0d56247
--- /dev/null
+++ b/examples/multimedia/video/qmlvideo/qmlvideo/videosingleton.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef QMLVIDEOSINGLETON_H
+#define QMLVIDEOSINGLETON_H
+
+#include "qmlvideo_global.h"
+
+#include <QtQml/qqml.h>
+
+class QMLVIDEO_LIB_EXPORT VideoSingleton : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source1 READ source1 WRITE setSource1 NOTIFY source1Changed FINAL)
+ Q_PROPERTY(QUrl source2 READ source2 WRITE setSource2 NOTIFY source2Changed FINAL)
+ Q_PROPERTY(QUrl videoPath READ videoPath WRITE setVideoPath NOTIFY videoPathChanged FINAL)
+ Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged FINAL)
+ QML_SINGLETON
+ QML_ELEMENT
+
+public:
+ explicit VideoSingleton(QObject *parent = nullptr);
+
+ QUrl source1() const;
+ void setSource1(const QUrl &source1);
+ QUrl source2() const;
+ void setSource2(const QUrl &source2);
+ QUrl videoPath() const;
+ void setVideoPath(const QUrl &videoPath);
+ qreal volume() const;
+ void setVolume(qreal volume);
+
+Q_SIGNALS:
+ void source1Changed();
+ void source2Changed();
+ void volumeChanged();
+ void videoPathChanged();
+
+private:
+ QUrl m_source1;
+ QUrl m_source2;
+ QUrl m_videoPath;
+ qreal m_volume = 0.5;
+};
+
+QML_DECLARE_TYPE(VideoSingleton);
+
+#endif // QMLVIDEOSINGLETON_H
diff --git a/examples/multimedia/video/qmlvideo/trace.h b/examples/multimedia/video/qmlvideo/trace.h
index 0ad9b5167..bd7f63b68 100644
--- a/examples/multimedia/video/qmlvideo/trace.h
+++ b/examples/multimedia/video/qmlvideo/trace.h
@@ -1,57 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef TRACE_H
#define TRACE_H
-#include <QtCore/QDebug>
+#include <QDebug>
#define ENABLE_TRACE
//#define VERBOSE_TRACE
@@ -61,13 +14,19 @@ namespace Trace {
class NullDebug
{
public:
- template <typename T>
- NullDebug& operator<<(const T&) { return *this; }
+ template<typename T>
+ NullDebug &operator<<(const T &)
+ {
+ return *this;
+ }
};
-inline NullDebug nullDebug() { return NullDebug(); }
+inline NullDebug nullDebug()
+{
+ return NullDebug();
+}
-template <typename T>
+template<typename T>
struct PtrWrapper
{
PtrWrapper(const T *ptr) : m_ptr(ptr) { }
@@ -76,8 +35,8 @@ struct PtrWrapper
} // namespace Trace
-template <typename T>
-inline QDebug& operator<<(QDebug &debug, const Trace::PtrWrapper<T> &wrapper)
+template<typename T>
+inline QDebug &operator<<(QDebug &debug, const Trace::PtrWrapper<T> &wrapper)
{
QDebugStateSaver saver(debug);
debug.nospace() << '[' << static_cast<const void *>(wrapper.m_ptr) << ']';
@@ -86,20 +45,37 @@ inline QDebug& operator<<(QDebug &debug, const Trace::PtrWrapper<T> &wrapper)
template<typename T>
inline const void *qtVoidPtr(const T *ptr)
-{ return static_cast<const void *>(ptr); }
+{
+ return static_cast<const void *>(ptr);
+}
#define qtThisPtr() qtVoidPtr(this)
#ifdef ENABLE_TRACE
- inline QDebug qtTrace() { return qDebug() << "[qmlvideo]"; }
+inline QDebug qtTrace()
+{
+ return qDebug() << "[qmlvideo]";
+}
# ifdef VERBOSE_TRACE
- inline QDebug qtVerboseTrace() { return qtTrace(); }
+inline QDebug qtVerboseTrace()
+{
+ return qtTrace();
+}
# else
- inline Trace::NullDebug qtVerboseTrace() { return Trace::nullDebug(); }
+inline Trace::NullDebug qtVerboseTrace()
+{
+ return Trace::nullDebug();
+}
# endif
#else
- inline Trace::NullDebug qtTrace() { return Trace::nullDebug(); }
- inline Trace::NullDebug qtVerboseTrace() { return Trace::nullDebug(); }
+inline Trace::NullDebug qtTrace()
+{
+ return Trace::nullDebug();
+}
+inline Trace::NullDebug qtVerboseTrace()
+{
+ return Trace::nullDebug();
+}
#endif
#endif // TRACE_H
diff --git a/examples/multimedia/video/recorder/AudioInputSelect.qml b/examples/multimedia/video/recorder/AudioInputSelect.qml
index bb79f2793..d1e30f080 100644
--- a/examples/multimedia/video/recorder/AudioInputSelect.qml
+++ b/examples/multimedia/video/recorder/AudioInputSelect.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -54,13 +7,18 @@ import QtMultimedia
Row {
id: root
-
+ height: Style.height
property AudioInput selected: available ? audioInput : null
property bool available: (typeof comboBox.currentValue !== 'undefined') && audioSwitch.checked
+ Component.onCompleted: {
+ audioInputModel.populate()
+ comboBox.currentIndex = 0
+ }
+
MediaDevices { id: mediaDevices }
- AudioInput { id: audioInput; muted: false }
+ AudioInput { id: audioInput; muted: !audioSwitch.checked }
Switch {
id: audioSwitch;
@@ -68,15 +26,28 @@ Row {
checked: true
}
+ ListModel {
+ id: audioInputModel
+ property var audioInputs: mediaDevices.audioInputs
+
+ function populate() {
+ audioInputModel.clear()
+
+ for (var audioDevice of audioInputs)
+ audioInputModel.append({ text: audioDevice.description, value:
+ { type: 'audioDevice', audioDevice: audioDevice } })
+ }
+ }
ComboBox {
id: comboBox
width: Style.widthLong
height: Style.height
background: StyleRectangle { anchors.fill: parent }
- model: mediaDevices.audioInputs
- textRole: "description"
+ model: audioInputModel
+ textRole: "text"
font.pointSize: Style.fontSize
- displayText: typeof currentValue === 'undefined' ? "unavailable" : currentValue.description
- onCurrentValueChanged: if (typeof comboBox.currentValue !== 'undefined') audioInput.device = currentValue
+ displayText: typeof currentValue === 'undefined' ? "unavailable" : currentText
+ valueRole: "value"
+ onCurrentValueChanged: if (typeof comboBox.currentValue !== 'undefined') audioInput.device = currentValue.audioDevice
}
}
diff --git a/examples/multimedia/video/recorder/CMakeLists.txt b/examples/multimedia/video/recorder/CMakeLists.txt
index 1948c5b23..116a1b817 100644
--- a/examples/multimedia/video/recorder/CMakeLists.txt
+++ b/examples/multimedia/video/recorder/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(recorder LANGUAGES CXX)
@@ -18,10 +21,11 @@ qt_add_executable(recorder
set(resource_files
"main.qml"
+ "main_no_permissions.qml"
"qmldir"
"MediaList.qml"
"AudioInputSelect.qml"
- "CameraSelect.qml"
+ "VideoSourceSelect.qml"
"RecordButton.qml"
"Controls.qml"
"StyleParameter.qml"
diff --git a/examples/multimedia/video/recorder/CameraSelect.qml b/examples/multimedia/video/recorder/CameraSelect.qml
deleted file mode 100644
index 0810d4baf..000000000
--- a/examples/multimedia/video/recorder/CameraSelect.qml
+++ /dev/null
@@ -1,85 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick
-import QtQuick.Controls
-import QtMultimedia
-
-Row {
- id: root
- height: Style.height
- property Camera selected: available ? camera : null
- property bool available: (typeof comboBox.currentValue !== 'undefined') && cameraSwitch.checked
-
- Camera {
- id: camera
- active: available && selected != null
- }
-
- MediaDevices { id: mediaDevices }
-
- Switch {
- id: cameraSwitch
- anchors.verticalCenter: parent.verticalCenter
- checked: true
- }
-
- ComboBox {
- id: comboBox
- width: Style.widthLong
- height: Style.height
- background: StyleRectangle { anchors.fill: parent }
- model: mediaDevices.videoInputs
- displayText: typeof currentValue === 'undefined' ? "Unavailable" : currentValue.description
- font.pointSize: Style.fontSize
- textRole: "description"
- onCurrentValueChanged: if (typeof comboBox.currentValue !== 'undefined') camera.cameraDevice = currentValue
- }
-}
diff --git a/examples/multimedia/video/recorder/Controls.qml b/examples/multimedia/video/recorder/Controls.qml
index baaad0ddf..627c2fe04 100644
--- a/examples/multimedia/video/recorder/Controls.qml
+++ b/examples/multimedia/video/recorder/Controls.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -61,7 +14,9 @@ Row {
property bool capturesVisible: false
property alias audioInput: audioInputSelect.selected
- property alias camera: cameraSelect.selected
+ property alias camera: videoSourceSelect.selectedCamera
+ property alias screenCapture: videoSourceSelect.selectedScreenCapture
+ property alias windowCapture: videoSourceSelect.selectedWindowCapture
spacing: Style.interSpacing * Style.ratio
@@ -69,7 +24,7 @@ Row {
id: inputControls
spacing: Style.intraSpacing
- CameraSelect { id: cameraSelect }
+ VideoSourceSelect { id: videoSourceSelect }
AudioInputSelect { id: audioInputSelect }
}
@@ -84,6 +39,7 @@ Row {
id: recordingTime
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: Style.fontSize
+ color: palette.text
}
}
@@ -91,6 +47,10 @@ Row {
id: optionButtons
spacing: Style.intraSpacing
Button {
+ leftPadding: 0
+ rightPadding: 0
+ topPadding: 0
+ bottomPadding: 0
height: Style.height
width: Style.widthMedium
background: StyleRectangle { anchors.fill: parent }
@@ -99,6 +59,10 @@ Row {
font.pointSize: Style.fontSize
}
Button {
+ leftPadding: 0
+ rightPadding: 0
+ topPadding: 0
+ bottomPadding: 0
height: Style.height
width: Style.widthMedium
background: StyleRectangle { anchors.fill: parent }
diff --git a/examples/multimedia/video/recorder/MediaList.qml b/examples/multimedia/video/recorder/MediaList.qml
index 33c65d32c..5fa65393a 100644
--- a/examples/multimedia/video/recorder/MediaList.qml
+++ b/examples/multimedia/video/recorder/MediaList.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/examples/multimedia/video/recorder/Playback.qml b/examples/multimedia/video/recorder/Playback.qml
index fdcfd1723..78575ae26 100644
--- a/examples/multimedia/video/recorder/Playback.qml
+++ b/examples/multimedia/video/recorder/Playback.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtMultimedia
diff --git a/examples/multimedia/video/recorder/RecordButton.qml b/examples/multimedia/video/recorder/RecordButton.qml
index 44d8d48d1..5296a3028 100644
--- a/examples/multimedia/video/recorder/RecordButton.qml
+++ b/examples/multimedia/video/recorder/RecordButton.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
diff --git a/examples/multimedia/video/recorder/SettingsEncoder.qml b/examples/multimedia/video/recorder/SettingsEncoder.qml
index c129eaed0..16a9590be 100644
--- a/examples/multimedia/video/recorder/SettingsEncoder.qml
+++ b/examples/multimedia/video/recorder/SettingsEncoder.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
@@ -71,7 +24,10 @@ Column {
function onMediaFormatChanged() { root.populateModels() }
}
- Text { text: "Encoder settings" }
+ Text {
+ text: "Encoder settings"
+ color: palette.text
+ }
StyleParameter {
label: "Quality"
diff --git a/examples/multimedia/video/recorder/SettingsMetaData.qml b/examples/multimedia/video/recorder/SettingsMetaData.qml
index e675e2325..7bfc2255d 100644
--- a/examples/multimedia/video/recorder/SettingsMetaData.qml
+++ b/examples/multimedia/video/recorder/SettingsMetaData.qml
@@ -1,70 +1,29 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtMultimedia
+pragma ComponentBehavior: Bound
+
ColumnLayout {
+ id: root
required property MediaRecorder recorder
- Text { text: "Metadata settings" }
+ Text {
+ text: "Metadata settings"
+ color: palette.text
+ }
ListModel { id: metaDataModel }
Connections {
- target: recorder
+ target: root.recorder
function onMetaDataChanged() {
metaDataModel.clear()
- for (var key of recorder.metaData.keys()) {
+ for (var key of root.recorder.metaData.keys()) {
if (recorder.metaData.stringValue(key))
metaDataModel.append(
{ text: recorder.metaData.metaDataKeyToString(key)
@@ -114,7 +73,7 @@ ColumnLayout {
font.pointSize: Style.fontSize
clip: true
onAccepted: {
- recorder.metaData.insert(metaDataType.currentValue, text)
+ root.recorder.metaData.insert(metaDataType.currentValue, text)
recorder.metaDataChanged()
text = ""
textInput.deselect()
@@ -168,8 +127,8 @@ ColumnLayout {
anchors.bottom: parent.bottom
font.pointSize: Style.fontSize
clip: true
- text: recorder.metaData.stringValue(r.value)
- onAccepted: recorder.metaData.insert(r.value, text)
+ text: root.recorder.metaData.stringValue(r.value)
+ onAccepted: root.recorder.metaData.insert(r.value, text)
}
}
@@ -179,7 +138,7 @@ ColumnLayout {
text: "del"
font.pointSize: Style.fontSize
background: StyleRectangle { anchors.fill: parent }
- onClicked: { recorder.metaData.remove(r.value); recorder.metaDataChanged() }
+ onClicked: { root.recorder.metaData.remove(r.value); recorder.metaDataChanged() }
}
}
}
diff --git a/examples/multimedia/video/recorder/Style.qml b/examples/multimedia/video/recorder/Style.qml
index f00a3c9f1..d30c3b8ef 100644
--- a/examples/multimedia/video/recorder/Style.qml
+++ b/examples/multimedia/video/recorder/Style.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
pragma Singleton
import QtQuick
diff --git a/examples/multimedia/video/recorder/StyleParameter.qml b/examples/multimedia/video/recorder/StyleParameter.qml
index 2a6c3ddb3..3a7573b92 100644
--- a/examples/multimedia/video/recorder/StyleParameter.qml
+++ b/examples/multimedia/video/recorder/StyleParameter.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -68,7 +21,7 @@ Row {
width: Style.widthShort
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: root.enabled ? "black" : "gray"
+ color: root.enabled ? palette.text : palette.mid
font.pointSize: Style.fontSize
}
diff --git a/examples/multimedia/video/recorder/StyleRectangle.qml b/examples/multimedia/video/recorder/StyleRectangle.qml
index 7e9186fc8..e28cf4a19 100644
--- a/examples/multimedia/video/recorder/StyleRectangle.qml
+++ b/examples/multimedia/video/recorder/StyleRectangle.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
diff --git a/examples/multimedia/video/recorder/StyleSlider.qml b/examples/multimedia/video/recorder/StyleSlider.qml
index 5f8e12309..a15304aaa 100644
--- a/examples/multimedia/video/recorder/StyleSlider.qml
+++ b/examples/multimedia/video/recorder/StyleSlider.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
@@ -69,7 +22,7 @@ Row {
height: Style.height
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
- color: root.enabled ? "black" : "gray"
+ color: root.enabled ? palette.text : palette.mid
}
Slider {
diff --git a/examples/multimedia/video/recorder/VideoSourceSelect.qml b/examples/multimedia/video/recorder/VideoSourceSelect.qml
new file mode 100644
index 000000000..7eeed99b9
--- /dev/null
+++ b/examples/multimedia/video/recorder/VideoSourceSelect.qml
@@ -0,0 +1,165 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtMultimedia
+
+Row {
+ id: root
+ height: Style.height
+ property Camera selectedCamera: cameraAvailable ? camera : null
+ property ScreenCapture selectedScreenCapture: screenAvailable ? screenCapture : null
+ property WindowCapture selectedWindowCapture: windowAvailable ? windowCapture : null
+
+ property bool sourceAvailable: typeof comboBox.currentValue !== 'undefined' &&
+ comboBox.currentValue.type !== 'toggler' &&
+ videoSourceSwitch.checked
+
+ property bool cameraAvailable: sourceAvailable && comboBox.currentValue.type === 'camera'
+ property bool screenAvailable: sourceAvailable && comboBox.currentValue.type === 'screen'
+ property bool windowAvailable: sourceAvailable && comboBox.currentValue.type === 'window'
+
+ Component.onCompleted: {
+ videoSourceModel.populate()
+
+ for (var i = 0; i < videoSourceModel.count; i++) {
+ if (videoSourceModel.get(i).value.type !== 'toggler') {
+ comboBox.currentIndex = i
+ break
+ }
+ }
+ }
+
+ Camera {
+ id: camera
+ active: cameraAvailable
+ }
+
+ ScreenCapture {
+ id: screenCapture
+ active: screenAvailable
+ }
+
+ WindowCapture {
+ id: windowCapture
+ active: windowAvailable
+ }
+
+ MediaDevices { id: mediaDevices
+ onVideoInputsChanged: {
+
+ videoSourceModel.populate()
+
+ for (var i = 0; i < videoSourceModel.count; i++) {
+ if (videoSourceModel.get(i).value.type !== 'toggler') {
+ comboBox.currentIndex = i
+ break
+ }
+ }
+ }
+ }
+
+ Switch {
+ id: videoSourceSwitch
+ anchors.verticalCenter: parent.verticalCenter
+ checked: true
+ }
+
+ ListModel {
+ id: videoSourceModel
+ property var enabledSources: {
+ 'camera': true,
+ 'screen': true,
+ 'window': false
+ }
+
+ function toggleEnabledSource(type) {
+ enabledSources[type] = !enabledSources[type]
+ populate()
+ }
+
+ function appendItem(text, value) {
+ append({ text: text, value: value})
+ }
+
+ function appendToggler(name, sourceType) {
+ appendItem((enabledSources[sourceType] ? "- Hide " : "+ Show ") + name,
+ { type: 'toggler', 'sourceType': sourceType })
+ }
+
+ function populate() {
+ clear()
+
+ appendToggler('Cameras', 'camera')
+ if (enabledSources['camera'])
+ for (var camera of mediaDevices.videoInputs)
+ appendItem(camera.description, { type: 'camera', camera: camera })
+
+ appendToggler('Screens', 'screen')
+ if (enabledSources['screen'])
+ for (var screen of Application.screens)
+ appendItem(screen.name, { type: 'screen', screen: screen })
+
+ appendToggler('Windows', 'window')
+ if (enabledSources['window'])
+ for (var window of windowCapture.capturableWindows())
+ appendItem(window.description, { type: 'window', window: window })
+ }
+ }
+
+ ComboBox {
+ id: comboBox
+ width: Style.widthLong
+ height: Style.height
+ background: StyleRectangle { anchors.fill: parent }
+ model: videoSourceModel
+ displayText: typeof currentValue === 'undefined' ||
+ currentValue.type === 'toggler' ? "Unavailable" : currentText
+ font.pointSize: Style.fontSize
+ textRole: "text"
+ valueRole: "value"
+ onCurrentValueChanged: {
+ if (typeof currentValue === 'undefined')
+ return
+ if (currentValue.type === 'screen')
+ screenCapture.screen = currentValue.screen
+ else if (currentValue.type === 'camera')
+ camera.cameraDevice = currentValue.camera
+ else if (currentValue.type === 'window')
+ windowCapture.window = currentValue.window
+ else if (currentValue.type === 'toggler')
+ model.toggleEnabledSource(currentValue.sourceType)
+ }
+
+ delegate: ItemDelegate {
+ property bool isToggler: value.type === 'toggler'
+ text: model[comboBox.textRole]
+ width: comboBox.width
+ height: comboBox.height
+ highlighted: comboBox.highlightedIndex === index && !isToggler
+ font.italic: isToggler
+ font.underline: isToggler
+ font.bold: comboBox.currentIndex === index && !isToggler ||
+ isToggler && comboBox.highlightedIndex === index
+ palette.text: isToggler ? comboBox.palette.link : comboBox.palette.text
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (isToggler)
+ videoSourceModel.toggleEnabledSource(value.sourceType)
+ else {
+ comboBox.currentIndex = index
+ comboBox.popup.close()
+ }
+ }
+ }
+
+ required property var value
+ required property int index
+ required property var model
+ }
+
+ }
+}
diff --git a/examples/multimedia/video/recorder/doc/images/qml-recorder-control-bar-overview.gif b/examples/multimedia/video/recorder/doc/images/qml-recorder-control-bar-overview.gif
new file mode 100644
index 000000000..b02053ea5
--- /dev/null
+++ b/examples/multimedia/video/recorder/doc/images/qml-recorder-control-bar-overview.gif
Binary files differ
diff --git a/examples/multimedia/video/recorder/doc/images/qml-recorder-overview.gif b/examples/multimedia/video/recorder/doc/images/qml-recorder-overview.gif
new file mode 100644
index 000000000..2e5e39215
--- /dev/null
+++ b/examples/multimedia/video/recorder/doc/images/qml-recorder-overview.gif
Binary files differ
diff --git a/examples/multimedia/video/recorder/doc/src/recorder.qdoc b/examples/multimedia/video/recorder/doc/src/recorder.qdoc
index 9ced09e74..3c0de3730 100644
--- a/examples/multimedia/video/recorder/doc/src/recorder.qdoc
+++ b/examples/multimedia/video/recorder/doc/src/recorder.qdoc
@@ -1,46 +1,102 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example multimedia/video/recorder
- \title QML Recorder Example
+ \example video/recorder
+ \title QML Video Recorder
\ingroup multimedia_examples
\ingroup video_examples_qml
\ingroup camera_examples_qml
+ \examplecategory {Multimedia}
\brief Recording audio and video using Qt Quick.
\meta {tag} {quick}
\image qmlrecorder.jpg
\e{QML Recorder} demonstrates a simple application that can record
- audio and or video files using the camera and microphone.
+ audio and video separate or together, using a microphone, a camera, or
+ with screen capturing.
\include examples-run.qdocinc
- The example uses the QML Camera and AudioInput objects connected to a MediaCaptureSession.
- A MediaRecorder object is then used to record the captured audio and video.
+ \section1 Overview
+ At its core, this is a QML application, see
+ \l{Getting Started Programming with Qt Quick}. This documentation is focused
+ on how this example uses the \l{Qt Multimedia QML Types}.
+
+ \image qml-recorder-overview.gif "Animation cycling through qml objects declared in main.qml"
+
+ The example uses the QML \l Camera and \l AudioInput types connected to a
+ \l CaptureSession. A \l MediaRecorder object is then used to record the
+ captured audio and video.
+
+ In addition to QtMultimedia, features of Qt Quick Windows, Controls, and
+ Layouts are used to implement the graphic user interface and functionality.
+ Playback won't be covered here, for that see the \l{QML Media Player Example}.
+
+ The example demonstrates the following:
+ \list
+ \li Input devices can be selected.
+ \li An input type switched off.
+ \li Settings for capturing such as quality, codec choice, file format,
+ and assigning metadata.
+ \li Captured files are stored and can be played back.
+ \endlist
+
+ \section1 Recording
+
+ The application implements recording.
+
+ \section2 captureSession
+
+ In \c main.qml, \c captureSession is declared like so:
+
+ \quotefromfile video/recorder/main.qml
+ \skipto CaptureSession
+ \printto MediaRecorder
+
+ \section2 recorder
+
+ In \c main.qml, the \l MediaRecorder \c recorder handles recording media
+ as well as capturing a thumbnail for the file and appending it to a ListModel,
+ \c mediaList.
+ \printto Playback
+
+ \c mediaList is declared in the \l Frame \c mediaListFrame
+ \quotefromfile video/recorder/main.qml
+ \skipto Frame
+ \printuntil playback: playback
+
+ \section2 controls
+
+ \image qml-recorder-control-bar-overview.gif
+
+ These are defined in \c Controls.qml and declared in main.qml.
+
+ Its root is a \l Row that contains the \l{Column}{Columns} \c inputControls,
+ \c recordButton, \c optionButtons, each defined in their own .qml files.
+
+ \section3 Selecting a video source
+
+ Defined in \c VideoSourceSelect.qml, \c VideoSourceSlect is comprised of a
+ \l Switch and a \l ComboBox and enables the user to select from available
+ cameras.
+
+ \quotefromfile video/recorder/VideoSourceSelect.qml
+ \skipto Row
+ \printto ComboBox
+
+ \c comboBox, declared in the above snippet, assigns the current video source.
+
+ \printuntil camera.cameraDevice = currentValue.camera
+
+ \section3 Selecting an audio input
+
+ Implemented in the same way as \l{Selecting a video source} and defined in
+ \c AudioInputSelect.qml like so:
+
+ \quotefromfile video/recorder/AudioInputSelect.qml
+ \skipto Row
+ \printto audioInput.device = currentValue
+
*/
diff --git a/examples/multimedia/video/recorder/main.cpp b/examples/multimedia/video/recorder/main.cpp
index 21067f50d..92e54cb1b 100644
--- a/examples/multimedia/video/recorder/main.cpp
+++ b/examples/multimedia/video/recorder/main.cpp
@@ -1,68 +1,51 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
#include <QQmlApplicationEngine>
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
+
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
- QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
- &app, [url](QObject *obj, const QUrl &objUrl) {
- if (!obj && url == objUrl)
- QCoreApplication::exit(-1);
- }, Qt::QueuedConnection);
+ QObject::connect(
+ &engine, &QQmlApplicationEngine::objectCreated, &app,
+ [url](QObject *obj, const QUrl &objUrl) {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ },
+ Qt::QueuedConnection);
+
+#if QT_CONFIG(permissions)
+ // If the permissions are not granted, display another main window, which
+ // simply contains the error message.
+ const QUrl noPermissionsUrl(QStringLiteral("qrc:/main_no_permissions.qml"));
+ QCameraPermission cameraPermission;
+ qApp->requestPermission(cameraPermission, [&](const QPermission &permission) {
+ if (permission.status() != Qt::PermissionStatus::Granted) {
+ qWarning("Camera permission is not granted!");
+ engine.load(noPermissionsUrl);
+ return;
+ }
+ QMicrophonePermission micPermission;
+ qApp->requestPermission(micPermission, [&](const QPermission &permission) {
+ if (permission.status() != Qt::PermissionStatus::Granted) {
+ qWarning("Microphone permission is not granted!");
+ engine.load(noPermissionsUrl);
+ } else {
+ engine.load(url);
+ }
+ });
+ });
+#else
engine.load(url);
+#endif
return app.exec();
}
diff --git a/examples/multimedia/video/recorder/main.qml b/examples/multimedia/video/recorder/main.qml
index c7e625f0e..d8d1cd54f 100644
--- a/examples/multimedia/video/recorder/main.qml
+++ b/examples/multimedia/video/recorder/main.qml
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
@@ -61,6 +14,8 @@ Window {
width: Style.screenWidth
height: Style.screenHeigth
+ color: palette.window
+
onWidthChanged:{
Style.calculateRatio(root.width, root.height)
}
@@ -82,6 +37,8 @@ Window {
recorder: recorder
audioInput: controls.audioInput
camera: controls.camera
+ screenCapture: controls.screenCapture
+ windowCapture: controls.windowCapture
videoOutput: videoOutput
}
@@ -119,7 +76,7 @@ Window {
x: controls.capturesVisible ? 0 : parent.width
background: Rectangle {
anchors.fill: parent
- color: "white"
+ color: palette.base
opacity: 0.8
}
@@ -145,7 +102,7 @@ Window {
background: Rectangle {
anchors.fill: parent
- color: "white"
+ color: palette.base
opacity: 0.8
}
diff --git a/examples/multimedia/video/recorder/main_no_permissions.qml b/examples/multimedia/video/recorder/main_no_permissions.qml
new file mode 100644
index 000000000..8c76533b0
--- /dev/null
+++ b/examples/multimedia/video/recorder/main_no_permissions.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+
+Window {
+ id: root
+ visible: true
+ title: "Media recorder"
+ width: Style.screenWidth
+ height: Style.screenHeigth
+
+ StyleRectangle {
+ anchors.fill: parent
+ Text {
+ anchors.fill: parent
+ anchors.margins: 20
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("The example is not usable without the permissions.\n"
+ + "Please grant all requested permissions and restart the application.")
+ }
+ }
+}
diff --git a/examples/multimediawidgets/videographicsitem/CMakeLists.txt b/examples/multimedia/videographicsitem/CMakeLists.txt
index fb8f4d857..a45e373a3 100644
--- a/examples/multimediawidgets/videographicsitem/CMakeLists.txt
+++ b/examples/multimedia/videographicsitem/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(videographicsitem LANGUAGES CXX)
@@ -7,7 +10,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimediawidgets/videographicsitem")
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/videographicsitem")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia MultimediaWidgets Widgets)
diff --git a/examples/multimediawidgets/videographicsitem/doc/images/video-videographicsitem.png b/examples/multimedia/videographicsitem/doc/images/video-videographicsitem.png
index e333c54a2..e333c54a2 100644
--- a/examples/multimediawidgets/videographicsitem/doc/images/video-videographicsitem.png
+++ b/examples/multimedia/videographicsitem/doc/images/video-videographicsitem.png
Binary files differ
diff --git a/examples/multimedia/videographicsitem/doc/src/videographicsitem.qdoc b/examples/multimedia/videographicsitem/doc/src/videographicsitem.qdoc
new file mode 100644
index 000000000..74196dfeb
--- /dev/null
+++ b/examples/multimedia/videographicsitem/doc/src/videographicsitem.qdoc
@@ -0,0 +1,20 @@
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example videographicsitem
+ \title Video Graphics Item Example
+ \ingroup multimedia_examples
+ \examplecategory {Multimedia}
+ \brief Streaming video on a graphics scene.
+ \meta {tag} {widgets}
+
+ \e{Video Graphics Item} demonstrates how to implement a QGraphicsItem that
+ displays video on a graphics scene using QVideoSink.
+
+ \image video-videographicsitem.png
+
+ \sa {Video Widget Example}
+
+ \include examples-run.qdocinc
+*/
diff --git a/examples/multimedia/videographicsitem/main.cpp b/examples/multimedia/videographicsitem/main.cpp
new file mode 100644
index 000000000..725243ed9
--- /dev/null
+++ b/examples/multimedia/videographicsitem/main.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "videoplayer.h"
+
+#include <QApplication>
+#include <QCommandLineOption>
+#include <QCommandLineParser>
+#include <QDir>
+#include <QUrl>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("Player Example");
+ QCoreApplication::setOrganizationName("QtProject");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Qt MultiMedia Player QGraphicsView Example");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("url", "The URL to open.");
+ parser.process(app);
+
+ VideoPlayer player;
+
+ if (!parser.positionalArguments().isEmpty() && player.isPlayerAvailable()) {
+ const QUrl url = QUrl::fromUserInput(parser.positionalArguments().constFirst(),
+ QDir::currentPath(), QUrl::AssumeLocalFile);
+ player.load(url);
+ }
+
+ player.show();
+
+ return app.exec();
+}
diff --git a/examples/multimediawidgets/videographicsitem/videographicsitem.pro b/examples/multimedia/videographicsitem/videographicsitem.pro
index 30a9b6f21..3415ef64c 100644
--- a/examples/multimediawidgets/videographicsitem/videographicsitem.pro
+++ b/examples/multimedia/videographicsitem/videographicsitem.pro
@@ -8,7 +8,7 @@ HEADERS += videoplayer.h
SOURCES += main.cpp \
videoplayer.cpp
-target.path = $$[QT_INSTALL_EXAMPLES]/multimediawidgets/videographicsitem
+target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/videographicsitem
INSTALLS += target
QT+=widgets
diff --git a/examples/multimediawidgets/videographicsitem/videoplayer.cpp b/examples/multimedia/videographicsitem/videoplayer.cpp
index 2f76f2be3..744a35469 100644
--- a/examples/multimediawidgets/videographicsitem/videoplayer.cpp
+++ b/examples/multimedia/videographicsitem/videoplayer.cpp
@@ -1,60 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "videoplayer.h"
-#include <QtWidgets>
+#include <QBoxLayout>
+#include <QDir>
+#include <QFileDialog>
+#include <QGraphicsScene>
#include <QGraphicsVideoItem>
-
-VideoPlayer::VideoPlayer(QWidget *parent)
- : QWidget(parent)
+#include <QGraphicsView>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QScreen>
+#include <QSlider>
+#include <QStandardPaths>
+#include <QStyle>
+#include <QVBoxLayout>
+#include <QWidget>
+
+VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
{
m_mediaPlayer = new QMediaPlayer(this);
const QSize screenGeometry = screen()->availableSize();
@@ -68,11 +32,10 @@ VideoPlayer::VideoPlayer(QWidget *parent)
QSlider *rotateSlider = new QSlider(Qt::Horizontal);
rotateSlider->setToolTip(tr("Rotate Video"));
- rotateSlider->setRange(-180, 180);
+ rotateSlider->setRange(-180, 180);
rotateSlider->setValue(0);
- connect(rotateSlider, &QAbstractSlider::valueChanged,
- this, &VideoPlayer::rotateVideo);
+ connect(rotateSlider, &QAbstractSlider::valueChanged, this, &VideoPlayer::rotateVideo);
QAbstractButton *openButton = new QPushButton(tr("Open..."));
connect(openButton, &QAbstractButton::clicked, this, &VideoPlayer::openFile);
@@ -86,8 +49,7 @@ VideoPlayer::VideoPlayer(QWidget *parent)
m_positionSlider = new QSlider(Qt::Horizontal);
m_positionSlider->setRange(0, 0);
- connect(m_positionSlider, &QAbstractSlider::sliderMoved,
- this, &VideoPlayer::setPosition);
+ connect(m_positionSlider, &QAbstractSlider::sliderMoved, this, &VideoPlayer::setPosition);
QBoxLayout *controlLayout = new QHBoxLayout;
controlLayout->setContentsMargins(0, 0, 0, 0);
@@ -101,15 +63,13 @@ VideoPlayer::VideoPlayer(QWidget *parent)
layout->addLayout(controlLayout);
m_mediaPlayer->setVideoOutput(m_videoItem);
- connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged,
- this, &VideoPlayer::mediaStateChanged);
+ connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged, this,
+ &VideoPlayer::mediaStateChanged);
connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, &VideoPlayer::positionChanged);
connect(m_mediaPlayer, &QMediaPlayer::durationChanged, this, &VideoPlayer::durationChanged);
}
-VideoPlayer::~VideoPlayer()
-{
-}
+VideoPlayer::~VideoPlayer() { }
QSize VideoPlayer::sizeHint() const
{
@@ -126,7 +86,8 @@ void VideoPlayer::openFile()
QFileDialog fileDialog(this);
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
fileDialog.setWindowTitle(tr("Open Movie"));
- fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).value(0, QDir::homePath()));
+ fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MoviesLocation)
+ .value(0, QDir::homePath()));
if (fileDialog.exec() == QDialog::Accepted)
load(fileDialog.selectedUrls().constFirst());
}
@@ -151,7 +112,7 @@ void VideoPlayer::play()
void VideoPlayer::mediaStateChanged(QMediaPlayer::PlaybackState state)
{
- switch(state) {
+ switch (state) {
case QMediaPlayer::PlayingState:
m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
break;
@@ -176,11 +137,12 @@ void VideoPlayer::setPosition(int position)
m_mediaPlayer->setPosition(position);
}
-
void VideoPlayer::rotateVideo(int angle)
{
- //rotate around the center of video element
+ // rotate around the center of video element
qreal x = m_videoItem->boundingRect().width() / 2.0;
qreal y = m_videoItem->boundingRect().height() / 2.0;
m_videoItem->setTransform(QTransform().translate(x, y).rotate(angle).translate(-x, -y));
}
+
+#include "moc_videoplayer.cpp"
diff --git a/examples/multimedia/videographicsitem/videoplayer.h b/examples/multimedia/videographicsitem/videoplayer.h
new file mode 100644
index 000000000..5cd4d7434
--- /dev/null
+++ b/examples/multimedia/videographicsitem/videoplayer.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef VIDEOPLAYER_H
+#define VIDEOPLAYER_H
+
+#include <QMediaPlayer>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QAbstractButton;
+class QSlider;
+class QGraphicsVideoItem;
+QT_END_NAMESPACE
+
+class VideoPlayer : public QWidget
+{
+ Q_OBJECT
+
+public:
+ VideoPlayer(QWidget *parent = nullptr);
+ ~VideoPlayer();
+
+ void load(const QUrl &url);
+ bool isPlayerAvailable() const;
+
+ QSize sizeHint() const override;
+
+public slots:
+ void openFile();
+ void play();
+
+private slots:
+ void mediaStateChanged(QMediaPlayer::PlaybackState state);
+ void positionChanged(qint64 position);
+ void durationChanged(qint64 duration);
+ void setPosition(int position);
+ void rotateVideo(int angle);
+
+private:
+ QMediaPlayer *m_mediaPlayer = nullptr;
+ QGraphicsVideoItem *m_videoItem = nullptr;
+ QAbstractButton *m_playButton = nullptr;
+ QSlider *m_positionSlider = nullptr;
+};
+
+#endif
diff --git a/examples/multimediawidgets/videowidget/CMakeLists.txt b/examples/multimedia/videowidget/CMakeLists.txt
index fbfaba50b..e47cea1fa 100644
--- a/examples/multimediawidgets/videowidget/CMakeLists.txt
+++ b/examples/multimedia/videowidget/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(videowidget LANGUAGES CXX)
@@ -7,7 +10,7 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimediawidgets/videowidget")
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/videowidget")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia MultimediaWidgets Widgets)
diff --git a/examples/multimediawidgets/videowidget/doc/images/video-videowidget.png b/examples/multimedia/videowidget/doc/images/video-videowidget.png
index a3c7bcb44..a3c7bcb44 100644
--- a/examples/multimediawidgets/videowidget/doc/images/video-videowidget.png
+++ b/examples/multimedia/videowidget/doc/images/video-videowidget.png
Binary files differ
diff --git a/examples/multimedia/videowidget/doc/src/videowidget.qdoc b/examples/multimedia/videowidget/doc/src/videowidget.qdoc
new file mode 100644
index 000000000..65be142fe
--- /dev/null
+++ b/examples/multimedia/videowidget/doc/src/videowidget.qdoc
@@ -0,0 +1,18 @@
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example videowidget
+ \title Video Widget Example
+ \ingroup multimedia_examples
+ \examplecategory {Multimedia}
+ \brief Implementing a video player widget.
+ \meta {tag} {widgets,QVideoWidget}
+
+ \e{Video Widget} demonstrates how to implement a simple video player using
+ QVideoWidget.
+
+ \image video-videowidget.png
+
+ \include examples-run.qdocinc
+*/
diff --git a/examples/multimedia/videowidget/main.cpp b/examples/multimedia/videowidget/main.cpp
new file mode 100644
index 000000000..a4e2dc4aa
--- /dev/null
+++ b/examples/multimedia/videowidget/main.cpp
@@ -0,0 +1,40 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "videoplayer.h"
+
+#include <QApplication>
+#include <QCommandLineOption>
+#include <QCommandLineParser>
+#include <QDir>
+#include <QScreen>
+#include <QUrl>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QCoreApplication::setApplicationName("Video Widget Example");
+ QCoreApplication::setOrganizationName("QtProject");
+ QGuiApplication::setApplicationDisplayName(QCoreApplication::applicationName());
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Qt Video Widget Example");
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument("url", "The URL to open.");
+ parser.process(app);
+
+ VideoPlayer player;
+ if (!parser.positionalArguments().isEmpty()) {
+ const QUrl url = QUrl::fromUserInput(parser.positionalArguments().constFirst(),
+ QDir::currentPath(), QUrl::AssumeLocalFile);
+ player.setUrl(url);
+ }
+
+ const QSize availableGeometry = player.screen()->availableSize();
+ player.resize(availableGeometry.width() / 6, availableGeometry.height() / 4);
+ player.show();
+
+ return app.exec();
+}
diff --git a/examples/multimediawidgets/videowidget/videoplayer.cpp b/examples/multimedia/videowidget/videoplayer.cpp
index ded52499c..1a778d2fb 100644
--- a/examples/multimediawidgets/videowidget/videoplayer.cpp
+++ b/examples/multimedia/videowidget/videoplayer.cpp
@@ -1,60 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "videoplayer.h"
-#include <QtWidgets>
+#include <QBoxLayout>
+#include <QFileDialog>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QMediaPlayer>
+#include <QPushButton>
+#include <QSizePolicy>
+#include <QSlider>
+#include <QStandardPaths>
+#include <QString>
+#include <QStyle>
#include <QVideoWidget>
-VideoPlayer::VideoPlayer(QWidget *parent)
- : QWidget(parent)
+VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
{
m_mediaPlayer = new QMediaPlayer(this);
QVideoWidget *videoWidget = new QVideoWidget;
@@ -66,14 +28,12 @@ VideoPlayer::VideoPlayer(QWidget *parent)
m_playButton->setEnabled(false);
m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
- connect(m_playButton, &QAbstractButton::clicked,
- this, &VideoPlayer::play);
+ connect(m_playButton, &QAbstractButton::clicked, this, &VideoPlayer::play);
m_positionSlider = new QSlider(Qt::Horizontal);
m_positionSlider->setRange(0, 0);
- connect(m_positionSlider, &QAbstractSlider::sliderMoved,
- this, &VideoPlayer::setPosition);
+ connect(m_positionSlider, &QAbstractSlider::sliderMoved, this, &VideoPlayer::setPosition);
m_errorLabel = new QLabel;
m_errorLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
@@ -92,24 +52,22 @@ VideoPlayer::VideoPlayer(QWidget *parent)
setLayout(layout);
m_mediaPlayer->setVideoOutput(videoWidget);
- connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged,
- this, &VideoPlayer::mediaStateChanged);
+ connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged, this,
+ &VideoPlayer::mediaStateChanged);
connect(m_mediaPlayer, &QMediaPlayer::positionChanged, this, &VideoPlayer::positionChanged);
connect(m_mediaPlayer, &QMediaPlayer::durationChanged, this, &VideoPlayer::durationChanged);
- connect(m_mediaPlayer, &QMediaPlayer::errorChanged,
- this, &VideoPlayer::handleError);
+ connect(m_mediaPlayer, &QMediaPlayer::errorChanged, this, &VideoPlayer::handleError);
}
-VideoPlayer::~VideoPlayer()
-{
-}
+VideoPlayer::~VideoPlayer() { }
void VideoPlayer::openFile()
{
QFileDialog fileDialog(this);
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
fileDialog.setWindowTitle(tr("Open Movie"));
- fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).value(0, QDir::homePath()));
+ fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MoviesLocation)
+ .value(0, QDir::homePath()));
if (fileDialog.exec() == QDialog::Accepted)
setUrl(fileDialog.selectedUrls().constFirst());
}
@@ -136,7 +94,7 @@ void VideoPlayer::play()
void VideoPlayer::mediaStateChanged(QMediaPlayer::PlaybackState state)
{
- switch(state) {
+ switch (state) {
case QMediaPlayer::PlayingState:
m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
break;
@@ -175,3 +133,5 @@ void VideoPlayer::handleError()
message += errorString;
m_errorLabel->setText(message);
}
+
+#include "moc_videoplayer.cpp"
diff --git a/examples/multimedia/videowidget/videoplayer.h b/examples/multimedia/videowidget/videoplayer.h
new file mode 100644
index 000000000..3657a04de
--- /dev/null
+++ b/examples/multimedia/videowidget/videoplayer.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef VIDEOPLAYER_H
+#define VIDEOPLAYER_H
+
+#include <QMediaPlayer>
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QAbstractButton;
+class QSlider;
+class QLabel;
+class QUrl;
+QT_END_NAMESPACE
+
+class VideoPlayer : public QWidget
+{
+ Q_OBJECT
+public:
+ VideoPlayer(QWidget *parent = nullptr);
+ ~VideoPlayer();
+
+ void setUrl(const QUrl &url);
+
+public slots:
+ void openFile();
+ void play();
+
+private slots:
+ void mediaStateChanged(QMediaPlayer::PlaybackState state);
+ void positionChanged(qint64 position);
+ void durationChanged(qint64 duration);
+ void setPosition(int position);
+ void handleError();
+
+private:
+ QMediaPlayer *m_mediaPlayer;
+ QAbstractButton *m_playButton;
+ QSlider *m_positionSlider;
+ QLabel *m_errorLabel;
+};
+
+#endif
diff --git a/examples/multimediawidgets/videowidget/videowidget.pro b/examples/multimedia/videowidget/videowidget.pro
index 603996b2e..56312a028 100644
--- a/examples/multimediawidgets/videowidget/videowidget.pro
+++ b/examples/multimedia/videowidget/videowidget.pro
@@ -10,7 +10,7 @@ SOURCES = \
main.cpp \
videoplayer.cpp
-target.path = $$[QT_INSTALL_EXAMPLES]/multimediawidgets/videowidget
+target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/videowidget
INSTALLS += target
QT+=widgets
diff --git a/examples/multimediawidgets/CMakeLists.txt b/examples/multimediawidgets/CMakeLists.txt
deleted file mode 100644
index 8d16458c2..000000000
--- a/examples/multimediawidgets/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-if(TARGET Qt::Widgets)
- qt_internal_add_example(camera)
- qt_internal_add_example(videographicsitem)
- qt_internal_add_example(videowidget)
- qt_internal_add_example(player)
-endif()
diff --git a/examples/multimediawidgets/camera/camera.h b/examples/multimediawidgets/camera/camera.h
deleted file mode 100644
index 786a6f722..000000000
--- a/examples/multimediawidgets/camera/camera.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/multimediawidgets/camera/doc/images/camera-example.png b/examples/multimediawidgets/camera/doc/images/camera-example.png
deleted file mode 100644
index 12e1b5728..000000000
--- a/examples/multimediawidgets/camera/doc/images/camera-example.png
+++ /dev/null
Binary files differ
diff --git a/examples/multimediawidgets/camera/doc/src/camera.qdoc b/examples/multimediawidgets/camera/doc/src/camera.qdoc
deleted file mode 100644
index 4db430fc3..000000000
--- a/examples/multimediawidgets/camera/doc/src/camera.qdoc
+++ /dev/null
@@ -1,82 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-/*!
-
-\example multimediawidgets/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/multimediawidgets/camera/imagesettings.cpp b/examples/multimediawidgets/camera/imagesettings.cpp
deleted file mode 100644
index a14e519e7..000000000
--- a/examples/multimediawidgets/camera/imagesettings.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/multimediawidgets/camera/imagesettings.h b/examples/multimediawidgets/camera/imagesettings.h
deleted file mode 100644
index 6564bd91c..000000000
--- a/examples/multimediawidgets/camera/imagesettings.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/multimediawidgets/camera/main.cpp b/examples/multimediawidgets/camera/main.cpp
deleted file mode 100644
index 04982a725..000000000
--- a/examples/multimediawidgets/camera/main.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/multimediawidgets/camera/metadatadialog.cpp b/examples/multimediawidgets/camera/metadatadialog.cpp
deleted file mode 100644
index 28d8db035..000000000
--- a/examples/multimediawidgets/camera/metadatadialog.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/multimediawidgets/camera/metadatadialog.h b/examples/multimediawidgets/camera/metadatadialog.h
deleted file mode 100644
index 0269e3479..000000000
--- a/examples/multimediawidgets/camera/metadatadialog.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/multimediawidgets/camera/videosettings.cpp b/examples/multimediawidgets/camera/videosettings.cpp
deleted file mode 100644
index f515d1bca..000000000
--- a/examples/multimediawidgets/camera/videosettings.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/multimediawidgets/camera/videosettings.h b/examples/multimediawidgets/camera/videosettings.h
deleted file mode 100644
index 14e00bc25..000000000
--- a/examples/multimediawidgets/camera/videosettings.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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/multimediawidgets/multimediawidgets.pro b/examples/multimediawidgets/multimediawidgets.pro
deleted file mode 100644
index aa74b00f0..000000000
--- a/examples/multimediawidgets/multimediawidgets.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = subdirs
-
-# These examples all need widgets for now (using creator templates that use widgets)
-qtHaveModule(widgets) {
- SUBDIRS += \
- camera \
- videographicsitem \
- videowidget \
- player
-}
diff --git a/examples/multimediawidgets/player/main.cpp b/examples/multimediawidgets/player/main.cpp
deleted file mode 100644
index 49c82d877..000000000
--- a/examples/multimediawidgets/player/main.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "player.h"
-
-#include <QApplication>
-#include <QCommandLineParser>
-#include <QCommandLineOption>
-#include <QDir>
-#include <QUrl>
-
-int main(int argc, char *argv[])
-{
- QApplication app(argc, argv);
-
- QCoreApplication::setApplicationName("Player Example");
- QCoreApplication::setOrganizationName("QtProject");
- QCoreApplication::setApplicationVersion(QT_VERSION_STR);
- QCommandLineParser parser;
- parser.setApplicationDescription("Qt MultiMedia Player Example");
- parser.addHelpOption();
- parser.addVersionOption();
- parser.addPositionalArgument("url", "The URL(s) to open.");
- parser.process(app);
-
- Player player;
-
- if (!parser.positionalArguments().isEmpty() && player.isPlayerAvailable()) {
- QList<QUrl> urls;
- for (auto &a: parser.positionalArguments())
- urls.append(QUrl::fromUserInput(a, QDir::currentPath()));
- player.addToPlaylist(urls);
- }
-
- player.show();
- return app.exec();
-}
diff --git a/examples/multimediawidgets/player/player.h b/examples/multimediawidgets/player/player.h
deleted file mode 100644
index 3f1c51795..000000000
--- a/examples/multimediawidgets/player/player.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PLAYER_H
-#define PLAYER_H
-
-#include "qmediaplaylist.h"
-
-#include <QWidget>
-#include <QMediaPlayer>
-#include <QMediaMetaData>
-
-QT_BEGIN_NAMESPACE
-class QAbstractItemView;
-class QLabel;
-class QMediaPlayer;
-class QModelIndex;
-class QPushButton;
-class QComboBox;
-class QSlider;
-class QStatusBar;
-class QVideoWidget;
-QT_END_NAMESPACE
-
-class PlaylistModel;
-
-class Player : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit Player(QWidget *parent = nullptr);
- ~Player() = default;
-
- bool isPlayerAvailable() const;
-
- void addToPlaylist(const QList<QUrl> &urls);
-
-signals:
- void fullScreenChanged(bool fullScreen);
-
-private slots:
- void open();
- void durationChanged(qint64 duration);
- void positionChanged(qint64 progress);
- void metaDataChanged();
- void tracksChanged();
-
- void previousClicked();
-
- void seek(int mseconds);
- void jump(const QModelIndex &index);
- void playlistPositionChanged(int);
-
- void statusChanged(QMediaPlayer::MediaStatus status);
- void bufferingProgress(float progress);
- void videoAvailableChanged(bool available);
-
- void selectAudioStream();
- void selectVideoStream();
- void selectSubtitleStream();
-
- void displayErrorMessage();
-
- void audioOutputChanged(int);
-
-private:
- void setTrackInfo(const QString &info);
- void setStatusInfo(const QString &info);
- void handleCursor(QMediaPlayer::MediaStatus status);
- void updateDurationInfo(qint64 currentInfo);
- QString trackName(const QMediaMetaData &metaData, int index);
-
- QMediaPlayer *m_player = nullptr;
- QAudioOutput *m_audioOutput = nullptr;
- QMediaPlaylist *m_playlist = nullptr;
- QVideoWidget *m_videoWidget = nullptr;
- QSlider *m_slider = nullptr;
- QLabel *m_labelDuration = nullptr;
- QPushButton *m_fullScreenButton = nullptr;
- QComboBox *m_audioOutputCombo = nullptr;
- QLabel *m_statusLabel = nullptr;
- QStatusBar *m_statusBar = nullptr;
-
- QComboBox *m_audioTracks = nullptr;
- QComboBox *m_videoTracks = nullptr;
- QComboBox *m_subtitleTracks = nullptr;
-
- PlaylistModel *m_playlistModel = nullptr;
- QAbstractItemView *m_playlistView = nullptr;
- QString m_trackInfo;
- QString m_statusInfo;
- qint64 m_duration;
-
- QWidget *m_metaDataFields[QMediaMetaData::NumMetaData] = {};
- QLabel *m_metaDataLabels[QMediaMetaData::NumMetaData] = {};
-};
-
-#endif // PLAYER_H
diff --git a/examples/multimediawidgets/player/playercontrols.h b/examples/multimediawidgets/player/playercontrols.h
deleted file mode 100644
index 927f72608..000000000
--- a/examples/multimediawidgets/player/playercontrols.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PLAYERCONTROLS_H
-#define PLAYERCONTROLS_H
-
-#include <QMediaPlayer>
-#include <QWidget>
-
-QT_BEGIN_NAMESPACE
-class QAbstractButton;
-class QAbstractSlider;
-class QComboBox;
-QT_END_NAMESPACE
-
-class PlayerControls : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit PlayerControls(QWidget *parent = nullptr);
-
- QMediaPlayer::PlaybackState state() const;
- float volume() const;
- bool isMuted() const;
- qreal playbackRate() const;
-
-public slots:
- void setState(QMediaPlayer::PlaybackState state);
- void setVolume(float volume);
- void setMuted(bool muted);
- void setPlaybackRate(float rate);
-
-signals:
- void play();
- void pause();
- void stop();
- void next();
- void previous();
- void changeVolume(float volume);
- void changeMuting(bool muting);
- void changeRate(qreal rate);
-
-private slots:
- void playClicked();
- void muteClicked();
- void updateRate();
- void onVolumeSliderValueChanged();
-
-private:
- QMediaPlayer::PlaybackState m_playerState = QMediaPlayer::StoppedState;
- bool m_playerMuted = false;
- QAbstractButton *m_playButton = nullptr;
- QAbstractButton *m_stopButton = nullptr;
- QAbstractButton *m_nextButton = nullptr;
- QAbstractButton *m_previousButton = nullptr;
- QAbstractButton *m_muteButton = nullptr;
- QAbstractSlider *m_volumeSlider = nullptr;
- QComboBox *m_rateBox = nullptr;
-};
-
-#endif // PLAYERCONTROLS_H
diff --git a/examples/multimediawidgets/player/playlistmodel.cpp b/examples/multimediawidgets/player/playlistmodel.cpp
deleted file mode 100644
index 28fc58a83..000000000
--- a/examples/multimediawidgets/player/playlistmodel.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "playlistmodel.h"
-#include "qmediaplaylist.h"
-
-#include <QFileInfo>
-#include <QUrl>
-
-PlaylistModel::PlaylistModel(QObject *parent)
- : QAbstractItemModel(parent)
-{
- m_playlist.reset(new QMediaPlaylist);
- connect(m_playlist.data(), &QMediaPlaylist::mediaAboutToBeInserted, this, &PlaylistModel::beginInsertItems);
- connect(m_playlist.data(), &QMediaPlaylist::mediaInserted, this, &PlaylistModel::endInsertItems);
- connect(m_playlist.data(), &QMediaPlaylist::mediaAboutToBeRemoved, this, &PlaylistModel::beginRemoveItems);
- connect(m_playlist.data(), &QMediaPlaylist::mediaRemoved, this, &PlaylistModel::endRemoveItems);
- connect(m_playlist.data(), &QMediaPlaylist::mediaChanged, this, &PlaylistModel::changeItems);
-}
-
-PlaylistModel::~PlaylistModel() = default;
-
-int PlaylistModel::rowCount(const QModelIndex &parent) const
-{
- return m_playlist && !parent.isValid() ? m_playlist->mediaCount() : 0;
-}
-
-int PlaylistModel::columnCount(const QModelIndex &parent) const
-{
- return !parent.isValid() ? ColumnCount : 0;
-}
-
-QModelIndex PlaylistModel::index(int row, int column, const QModelIndex &parent) const
-{
- return m_playlist && !parent.isValid()
- && row >= 0 && row < m_playlist->mediaCount()
- && column >= 0 && column < ColumnCount
- ? createIndex(row, column)
- : QModelIndex();
-}
-
-QModelIndex PlaylistModel::parent(const QModelIndex &child) const
-{
- Q_UNUSED(child);
-
- return QModelIndex();
-}
-
-QVariant PlaylistModel::data(const QModelIndex &index, int role) const
-{
- if (index.isValid() && role == Qt::DisplayRole) {
- QVariant value = m_data[index];
- if (!value.isValid() && index.column() == Title) {
- QUrl location = m_playlist->media(index.row());
- return QFileInfo(location.path()).fileName();
- }
-
- return value;
- }
- return QVariant();
-}
-
-QMediaPlaylist *PlaylistModel::playlist() const
-{
- return m_playlist.data();
-}
-
-bool PlaylistModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- Q_UNUSED(role);
- m_data[index] = value;
- emit dataChanged(index, index);
- return true;
-}
-
-void PlaylistModel::beginInsertItems(int start, int end)
-{
- m_data.clear();
- beginInsertRows(QModelIndex(), start, end);
-}
-
-void PlaylistModel::endInsertItems()
-{
- endInsertRows();
-}
-
-void PlaylistModel::beginRemoveItems(int start, int end)
-{
- m_data.clear();
- beginRemoveRows(QModelIndex(), start, end);
-}
-
-void PlaylistModel::endRemoveItems()
-{
- endInsertRows();
-}
-
-void PlaylistModel::changeItems(int start, int end)
-{
- m_data.clear();
- emit dataChanged(index(start,0), index(end,ColumnCount));
-}
diff --git a/examples/multimediawidgets/player/playlistmodel.h b/examples/multimediawidgets/player/playlistmodel.h
deleted file mode 100644
index c2bc8319a..000000000
--- a/examples/multimediawidgets/player/playlistmodel.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PLAYLISTMODEL_H
-#define PLAYLISTMODEL_H
-
-#include <QAbstractItemModel>
-#include <QScopedPointer>
-
-QT_BEGIN_NAMESPACE
-class QMediaPlaylist;
-QT_END_NAMESPACE
-
-class PlaylistModel : public QAbstractItemModel
-{
- Q_OBJECT
-
-public:
- enum Column
- {
- Title = 0,
- ColumnCount
- };
-
- explicit PlaylistModel(QObject *parent = nullptr);
- ~PlaylistModel();
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- int columnCount(const QModelIndex &parent = QModelIndex()) const override;
-
- QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
- QModelIndex parent(const QModelIndex &child) const override;
-
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
-
- QMediaPlaylist *playlist() const;
-
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override;
-
-private slots:
- void beginInsertItems(int start, int end);
- void endInsertItems();
- void beginRemoveItems(int start, int end);
- void endRemoveItems();
- void changeItems(int start, int end);
-
-private:
- QScopedPointer<QMediaPlaylist> m_playlist;
- QMap<QModelIndex, QVariant> m_data;
-};
-
-#endif // PLAYLISTMODEL_H
diff --git a/examples/multimediawidgets/player/qmediaplaylist_p.h b/examples/multimediawidgets/player/qmediaplaylist_p.h
deleted file mode 100644
index a09831377..000000000
--- a/examples/multimediawidgets/player/qmediaplaylist_p.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QMEDIAPLAYLIST_P_H
-#define QMEDIAPLAYLIST_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qmediaplaylist.h"
-#include "qplaylistfileparser_p.h"
-
-#include <QtCore/qdebug.h>
-
-#ifdef Q_MOC_RUN
-# pragma Q_MOC_EXPAND_MACROS
-#endif
-
-QT_BEGIN_NAMESPACE
-
-
-class QMediaPlaylistControl;
-
-class QMediaPlaylistPrivate
-{
- Q_DECLARE_PUBLIC(QMediaPlaylist)
-public:
- QMediaPlaylistPrivate()
- : error(QMediaPlaylist::NoError)
- {
- }
-
- virtual ~QMediaPlaylistPrivate()
- {
- if (parser)
- delete parser;
- }
-
- void loadFailed(QMediaPlaylist::Error error, const QString &errorString)
- {
- this->error = error;
- this->errorString = errorString;
-
- emit q_ptr->loadFailed();
- }
-
- void loadFinished()
- {
- q_ptr->addMedia(parser->playlist);
-
- emit q_ptr->loaded();
- }
-
- bool checkFormat(const char *format) const
- {
- QLatin1String f(format);
- QPlaylistFileParser::FileType type = format ? QPlaylistFileParser::UNKNOWN : QPlaylistFileParser::M3U8;
- if (format) {
- if (f == QLatin1String("m3u") || f == QLatin1String("text/uri-list") ||
- f == QLatin1String("audio/x-mpegurl") || f == QLatin1String("audio/mpegurl"))
- type = QPlaylistFileParser::M3U;
- else if (f == QLatin1String("m3u8") || f == QLatin1String("application/x-mpegURL") ||
- f == QLatin1String("application/vnd.apple.mpegurl"))
- type = QPlaylistFileParser::M3U8;
- }
-
- if (type == QPlaylistFileParser::UNKNOWN || type == QPlaylistFileParser::PLS) {
- error = QMediaPlaylist::FormatNotSupportedError;
- errorString = QMediaPlaylist::tr("This file format is not supported.");
- return false;
- }
- return true;
- }
-
- void ensureParser()
- {
- if (parser)
- return;
-
- parser = new QPlaylistFileParser(q_ptr);
- QObject::connect(parser, &QPlaylistFileParser::finished, [this]() { loadFinished(); });
- QObject::connect(parser, &QPlaylistFileParser::error,
- [this](QMediaPlaylist::Error err, const QString& errorMsg) { loadFailed(err, errorMsg); });
- }
-
- int nextPosition(int steps) const;
- int prevPosition(int steps) const;
-
- QList<QUrl> playlist;
-
- int currentPos = -1;
- QMediaPlaylist::PlaybackMode playbackMode = QMediaPlaylist::Sequential;
-
- QPlaylistFileParser *parser = nullptr;
- mutable QMediaPlaylist::Error error;
- mutable QString errorString;
-
- QMediaPlaylist *q_ptr;
-};
-
-QT_END_NAMESPACE
-
-
-#endif // QMEDIAPLAYLIST_P_H
diff --git a/examples/multimediawidgets/player/qplaylistfileparser_p.h b/examples/multimediawidgets/player/qplaylistfileparser_p.h
deleted file mode 100644
index 98011fce0..000000000
--- a/examples/multimediawidgets/player/qplaylistfileparser_p.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PLAYLISTFILEPARSER_P_H
-#define PLAYLISTFILEPARSER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qtmultimediaglobal.h"
-#include "qmediaplaylist.h"
-#include <QtCore/qobject.h>
-
-QT_BEGIN_NAMESPACE
-
-class QIODevice;
-class QUrl;
-class QNetworkRequest;
-
-class QPlaylistFileParserPrivate;
-
-class QPlaylistFileParser : public QObject
-{
- Q_OBJECT
-public:
- QPlaylistFileParser(QObject *parent = nullptr);
- ~QPlaylistFileParser();
-
- enum FileType
- {
- UNKNOWN,
- M3U,
- M3U8, // UTF-8 version of M3U
- PLS
- };
-
- void start(const QUrl &media, QIODevice *stream = nullptr, const QString &mimeType = QString());
- void start(const QUrl &request, const QString &mimeType = QString());
- void start(QIODevice *stream, const QString &mimeType = QString());
- void abort();
-
- QList<QUrl> playlist;
-
-Q_SIGNALS:
- void newItem(const QVariant& content);
- void finished();
- void error(QMediaPlaylist::Error err, const QString& errorMsg);
-
-private Q_SLOTS:
- void handleData();
- void handleError();
-
-private:
-
- static FileType findByMimeType(const QString &mime);
- static FileType findBySuffixType(const QString &suffix);
- static FileType findByDataHeader(const char *data, quint32 size);
- static FileType findPlaylistType(QIODevice *device,
- const QString& mime);
- static FileType findPlaylistType(const QString &suffix,
- const QString& mime,
- const char *data = nullptr,
- quint32 size = 0);
-
- Q_DISABLE_COPY(QPlaylistFileParser)
- Q_DECLARE_PRIVATE(QPlaylistFileParser)
- QScopedPointer<QPlaylistFileParserPrivate> d_ptr;
-};
-
-QT_END_NAMESPACE
-
-#endif // PLAYLISTFILEPARSER_P_H
diff --git a/examples/multimediawidgets/player/videowidget.cpp b/examples/multimediawidgets/player/videowidget.cpp
deleted file mode 100644
index a97e1d940..000000000
--- a/examples/multimediawidgets/player/videowidget.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "videowidget.h"
-
-#include <QKeyEvent>
-#include <QMouseEvent>
-
-VideoWidget::VideoWidget(QWidget *parent)
- : QVideoWidget(parent)
-{
- setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
-
- QPalette p = palette();
- p.setColor(QPalette::Window, Qt::black);
- setPalette(p);
-
-#ifndef Q_OS_ANDROID // QTBUG-95723
- setAttribute(Qt::WA_OpaquePaintEvent);
-#endif
-}
-
-void VideoWidget::keyPressEvent(QKeyEvent *event)
-{
- if ((event->key() == Qt::Key_Escape || event->key() == Qt::Key_Back) && isFullScreen()) {
- setFullScreen(false);
- event->accept();
- } else if (event->key() == Qt::Key_Enter && event->modifiers() & Qt::Key_Alt) {
- setFullScreen(!isFullScreen());
- event->accept();
- } else {
- QVideoWidget::keyPressEvent(event);
- }
-}
-
-void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event)
-{
- setFullScreen(!isFullScreen());
- event->accept();
-}
-
-void VideoWidget::mousePressEvent(QMouseEvent *event)
-{
- QVideoWidget::mousePressEvent(event);
-}
-
diff --git a/examples/multimediawidgets/player/videowidget.h b/examples/multimediawidgets/player/videowidget.h
deleted file mode 100644
index c01632e7b..000000000
--- a/examples/multimediawidgets/player/videowidget.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef VIDEOWIDGET_H
-#define VIDEOWIDGET_H
-
-#include <QVideoWidget>
-
-class VideoWidget : public QVideoWidget
-{
- Q_OBJECT
-
-public:
- explicit VideoWidget(QWidget *parent = nullptr);
-
-protected:
- void keyPressEvent(QKeyEvent *event) override;
- void mouseDoubleClickEvent(QMouseEvent *event) override;
- void mousePressEvent(QMouseEvent *event) override;
-};
-
-#endif // VIDEOWIDGET_H
diff --git a/examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc b/examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc
deleted file mode 100644
index 74fbf65cf..000000000
--- a/examples/multimediawidgets/videographicsitem/doc/src/videographicsitem.qdoc
+++ /dev/null
@@ -1,43 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example multimediawidgets/videographicsitem
- \title Video Graphics Item Example
- \ingroup multimedia_examples
- \brief Streaming video on a graphics scene.
- \meta {tag} {widgets}
-
- \e{Video Graphics Item} demonstrates how to implement a QGraphicsItem that
- displays video on a graphics scene using QVideoSink.
-
- \image video-videographicsitem.png
-
- \sa {Video Widget Example}
-
- \include examples-run.qdocinc
-*/
diff --git a/examples/multimediawidgets/videographicsitem/main.cpp b/examples/multimediawidgets/videographicsitem/main.cpp
deleted file mode 100644
index e262c3908..000000000
--- a/examples/multimediawidgets/videographicsitem/main.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "videoplayer.h"
-
-#include <QApplication>
-#include <QCommandLineParser>
-#include <QCommandLineOption>
-#include <QDir>
-#include <QUrl>
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
-
- QCoreApplication::setApplicationName("Player Example");
- QCoreApplication::setOrganizationName("QtProject");
- QCoreApplication::setApplicationVersion(QT_VERSION_STR);
- QCommandLineParser parser;
- parser.setApplicationDescription("Qt MultiMedia Player QGraphicsView Example");
- parser.addHelpOption();
- parser.addVersionOption();
- parser.addPositionalArgument("url", "The URL to open.");
- parser.process(app);
-
- VideoPlayer player;
-
- if (!parser.positionalArguments().isEmpty() && player.isPlayerAvailable()) {
- const QUrl url =
- QUrl::fromUserInput(parser.positionalArguments().constFirst(),
- QDir::currentPath(), QUrl::AssumeLocalFile);
- player.load(url);
- }
-
- player.show();
-
- return app.exec();
-}
-
diff --git a/examples/multimediawidgets/videographicsitem/videoplayer.h b/examples/multimediawidgets/videographicsitem/videoplayer.h
deleted file mode 100644
index 9f01e8790..000000000
--- a/examples/multimediawidgets/videographicsitem/videoplayer.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef VIDEOPLAYER_H
-#define VIDEOPLAYER_H
-
-#include <QMediaPlayer>
-#include <QWidget>
-
-QT_BEGIN_NAMESPACE
-class QAbstractButton;
-class QSlider;
-class QGraphicsVideoItem;
-QT_END_NAMESPACE
-
-class VideoPlayer : public QWidget
-{
- Q_OBJECT
-
-public:
- VideoPlayer(QWidget *parent = nullptr);
- ~VideoPlayer();
-
- void load(const QUrl &url);
- bool isPlayerAvailable() const;
-
- QSize sizeHint() const override;
-
-public slots:
- void openFile();
- void play();
-
-private slots:
- void mediaStateChanged(QMediaPlayer::PlaybackState state);
- void positionChanged(qint64 position);
- void durationChanged(qint64 duration);
- void setPosition(int position);
- void rotateVideo(int angle);
-
-private:
- QMediaPlayer *m_mediaPlayer = nullptr;
- QGraphicsVideoItem *m_videoItem = nullptr;
- QAbstractButton *m_playButton = nullptr;
- QSlider *m_positionSlider = nullptr;
-};
-
-#endif
-
diff --git a/examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc b/examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc
deleted file mode 100644
index 25bf7b2e3..000000000
--- a/examples/multimediawidgets/videowidget/doc/src/videowidget.qdoc
+++ /dev/null
@@ -1,41 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example multimediawidgets/videowidget
- \title Video Widget Example
- \ingroup multimedia_examples
- \brief Implementing a video player widget.
- \meta {tag} {widgets,QVideoWidget}
-
- \e{Video Widget} demonstrates how to implement a simple video player using
- QVideoWidget.
-
- \image video-videowidget.png
-
- \include examples-run.qdocinc
-*/
diff --git a/examples/multimediawidgets/videowidget/main.cpp b/examples/multimediawidgets/videowidget/main.cpp
deleted file mode 100644
index 3cf7e7989..000000000
--- a/examples/multimediawidgets/videowidget/main.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "videoplayer.h"
-
-#include <QtWidgets/QApplication>
-#include <QtCore/QCommandLineParser>
-#include <QtCore/QCommandLineOption>
-#include <QtCore/QDir>
-#include <QtCore/QUrl>
-#include <QScreen>
-
-int main(int argc, char *argv[])
-{
- QApplication app(argc, argv);
-
- QCoreApplication::setApplicationName("Video Widget Example");
- QCoreApplication::setOrganizationName("QtProject");
- QGuiApplication::setApplicationDisplayName(QCoreApplication::applicationName());
- QCoreApplication::setApplicationVersion(QT_VERSION_STR);
- QCommandLineParser parser;
- parser.setApplicationDescription("Qt Video Widget Example");
- parser.addHelpOption();
- parser.addVersionOption();
- parser.addPositionalArgument("url", "The URL to open.");
- parser.process(app);
-
- VideoPlayer player;
- if (!parser.positionalArguments().isEmpty()) {
- const QUrl url =
- QUrl::fromUserInput(parser.positionalArguments().constFirst(),
- QDir::currentPath(), QUrl::AssumeLocalFile);
- player.setUrl(url);
- }
-
- const QSize availableGeometry = player.screen()->availableSize();
- player.resize(availableGeometry.width() / 6, availableGeometry.height() / 4);
- player.show();
-
- return app.exec();
-}
diff --git a/examples/multimediawidgets/videowidget/videoplayer.h b/examples/multimediawidgets/videowidget/videoplayer.h
deleted file mode 100644
index 723df0b6c..000000000
--- a/examples/multimediawidgets/videowidget/videoplayer.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef VIDEOPLAYER_H
-#define VIDEOPLAYER_H
-
-#include <QMediaPlayer>
-#include <QWidget>
-
-QT_BEGIN_NAMESPACE
-class QAbstractButton;
-class QSlider;
-class QLabel;
-class QUrl;
-QT_END_NAMESPACE
-
-class VideoPlayer : public QWidget
-{
- Q_OBJECT
-public:
- VideoPlayer(QWidget *parent = nullptr);
- ~VideoPlayer();
-
- void setUrl(const QUrl &url);
-
-public slots:
- void openFile();
- void play();
-
-private slots:
- void mediaStateChanged(QMediaPlayer::PlaybackState state);
- void positionChanged(qint64 position);
- void durationChanged(qint64 duration);
- void setPosition(int position);
- void handleError();
-
-private:
- QMediaPlayer* m_mediaPlayer;
- QAbstractButton *m_playButton;
- QSlider *m_positionSlider;
- QLabel *m_errorLabel;
-};
-
-#endif
diff --git a/examples/spatialaudio/CMakeLists.txt b/examples/spatialaudio/CMakeLists.txt
new file mode 100644
index 000000000..0d92a79f5
--- /dev/null
+++ b/examples/spatialaudio/CMakeLists.txt
@@ -0,0 +1 @@
+qt_internal_add_example(audiopanning)
diff --git a/examples/multimedia/spatialaudio/CMakeLists.txt b/examples/spatialaudio/audiopanning/CMakeLists.txt
index 9e6649fc8..00367897a 100644
--- a/examples/multimedia/spatialaudio/CMakeLists.txt
+++ b/examples/spatialaudio/audiopanning/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(audiodecoder LANGUAGES CXX)
@@ -7,26 +10,26 @@ if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/spatialaudio")
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/spatialaudio/audiopanning")
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia Widgets)
+find_package(Qt6 REQUIRED COMPONENTS Core Gui SpatialAudio Widgets)
-qt_add_executable(spatialaudio
+qt_add_executable(audiopanning
main.cpp
)
-set_target_properties(spatialaudio PROPERTIES
+set_target_properties(audiopanning PROPERTIES
WIN32_EXECUTABLE FALSE
MACOSX_BUNDLE TRUE
)
-target_link_libraries(spatialaudio PUBLIC
+target_link_libraries(audiopanning PUBLIC
Qt::Core
- Qt::Multimedia
+ Qt::SpatialAudio
Qt::Widgets
)
-install(TARGETS spatialaudio
+install(TARGETS audiopanning
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
diff --git a/examples/spatialaudio/audiopanning/audiopanning.pro b/examples/spatialaudio/audiopanning/audiopanning.pro
new file mode 100644
index 000000000..3b1cce652
--- /dev/null
+++ b/examples/spatialaudio/audiopanning/audiopanning.pro
@@ -0,0 +1,9 @@
+TEMPLATE = app
+TARGET = audiopanning
+
+QT += multimedia widgets spatialaudio
+
+SOURCES = main.cpp
+
+target.path = $$[QT_INSTALL_EXAMPLES]/spatialaudio/audiopanning
+INSTALLS += target
diff --git a/examples/multimedia/spatialaudio/doc/images/spatialaudio-example.png b/examples/spatialaudio/audiopanning/doc/images/audiopanning-example.png
index 45c53975d..45c53975d 100644
--- a/examples/multimedia/spatialaudio/doc/images/spatialaudio-example.png
+++ b/examples/spatialaudio/audiopanning/doc/images/audiopanning-example.png
Binary files differ
diff --git a/examples/spatialaudio/audiopanning/doc/src/audiopanning.qdoc b/examples/spatialaudio/audiopanning/doc/src/audiopanning.qdoc
new file mode 100644
index 000000000..ebb18e5c7
--- /dev/null
+++ b/examples/spatialaudio/audiopanning/doc/src/audiopanning.qdoc
@@ -0,0 +1,28 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+/*!
+
+\example audiopanning
+\title Spatial Audio Panning Example
+\ingroup spatialaudio_examples
+\ingroup audio_examples
+\examplecategory {Multimedia}
+\meta {tag} {widgets}
+\brief Shows some of the capabilities of the spatial audio engine in Qt.
+
+The Spatial Audio Example demonstrates how you can use \l{Qt Spatial Audio} to
+place sound sources in 3D space and how positioning of the sound source and
+room properties affect the listening experience.
+
+The example lets you specify a sound file to be played back at a certain
+position in 3D space relative to the listener. Using sliders you can change
+the distance and the azimuth and elevation angles to the source. You can also
+change dimensions of a virtual room, the intensity of the room reflections
+and reverb.
+
+Various other properties can also be modified such as the dimensions of a virtual
+room and the intensity of the room reflections and reverb.
+
+\image audiopanning-example.png
+
+*/
diff --git a/examples/spatialaudio/audiopanning/main.cpp b/examples/spatialaudio/audiopanning/main.cpp
new file mode 100644
index 000000000..84e5197bd
--- /dev/null
+++ b/examples/spatialaudio/audiopanning/main.cpp
@@ -0,0 +1,241 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QApplication>
+#include <QAudioEngine>
+#include <QAudioListener>
+#include <QAudioRoom>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QCommandLineParser>
+#include <QFileDialog>
+#include <QFormLayout>
+#include <QHBoxLayout>
+#include <QLibraryInfo>
+#include <QLineEdit>
+#include <QPropertyAnimation>
+#include <QPushButton>
+#include <QSlider>
+#include <QSpatialSound>
+#include <QStandardPaths>
+
+class AudioWidget : public QWidget
+{
+public:
+ AudioWidget();
+ void setFile(const QString &file);
+
+private slots:
+ void updatePosition();
+ void newOcclusion();
+ void modeChanged();
+ void fileChanged(const QString &file);
+ void openFileDialog();
+ void updateRoom();
+ void animateChanged();
+
+private:
+ QLineEdit *fileEdit = nullptr;
+ QPushButton *fileDialogButton = nullptr;
+ QSlider *azimuth = nullptr;
+ QSlider *elevation = nullptr;
+ QSlider *distance = nullptr;
+ QSlider *occlusion = nullptr;
+ QSlider *roomDimension = nullptr;
+ QSlider *reverbGain = nullptr;
+ QSlider *reflectionGain = nullptr;
+ QComboBox *mode = nullptr;
+ QCheckBox *animateButton = nullptr;
+ QPropertyAnimation *animation = nullptr;
+
+ QAudioEngine engine;
+ QAudioListener *listener = nullptr;
+ QSpatialSound *sound = nullptr;
+ QAudioRoom *room = nullptr;
+ QFileDialog *fileDialog = nullptr;
+};
+
+AudioWidget::AudioWidget()
+ : QWidget()
+{
+ setMinimumSize(400, 300);
+ auto *form = new QFormLayout(this);
+
+ auto *fileLayout = new QHBoxLayout;
+ fileEdit = new QLineEdit;
+ fileEdit->setPlaceholderText(tr("Audio File"));
+ fileLayout->addWidget(fileEdit);
+ fileDialogButton = new QPushButton(tr("Choose..."));
+ fileLayout->addWidget(fileDialogButton);
+ form->addRow(fileLayout);
+
+ azimuth = new QSlider(Qt::Horizontal);
+ azimuth->setRange(-180, 180);
+ form->addRow(tr("Azimuth (-180 - 180 degree):"), azimuth);
+
+ elevation = new QSlider(Qt::Horizontal);
+ elevation->setRange(-90, 90);
+ form->addRow(tr("Elevation (-90 - 90 degree)"), elevation);
+
+ distance = new QSlider(Qt::Horizontal);
+ distance->setRange(0, 1000);
+ distance->setValue(100);
+ form->addRow(tr("Distance (0 - 10 meter):"), distance);
+
+ occlusion = new QSlider(Qt::Horizontal);
+ occlusion->setRange(0, 400);
+ form->addRow(tr("Occlusion (0 - 4):"), occlusion);
+
+ roomDimension = new QSlider(Qt::Horizontal);
+ roomDimension->setRange(0, 10000);
+ roomDimension->setValue(1000);
+ form->addRow(tr("Room dimension (0 - 100 meter):"), roomDimension);
+
+ reverbGain = new QSlider(Qt::Horizontal);
+ reverbGain->setRange(0, 500);
+ reverbGain->setValue(0);
+ form->addRow(tr("Reverb gain (0-5):"), reverbGain);
+
+ reflectionGain = new QSlider(Qt::Horizontal);
+ reflectionGain->setRange(0, 500);
+ reflectionGain->setValue(0);
+ form->addRow(tr("Reflection gain (0-5):"), reflectionGain);
+
+ mode = new QComboBox;
+ mode->addItem(tr("Surround"), QVariant::fromValue(QAudioEngine::Surround));
+ mode->addItem(tr("Stereo"), QVariant::fromValue(QAudioEngine::Stereo));
+ mode->addItem(tr("Headphone"), QVariant::fromValue(QAudioEngine::Headphone));
+
+ form->addRow(tr("Output mode:"), mode);
+
+ animateButton = new QCheckBox(tr("Animate sound position"));
+ form->addRow(animateButton);
+
+ connect(fileEdit, &QLineEdit::textChanged, this, &AudioWidget::fileChanged);
+ connect(fileDialogButton, &QPushButton::clicked, this, &AudioWidget::openFileDialog);
+
+ connect(azimuth, &QSlider::valueChanged, this, &AudioWidget::updatePosition);
+ connect(elevation, &QSlider::valueChanged, this, &AudioWidget::updatePosition);
+ connect(distance, &QSlider::valueChanged, this, &AudioWidget::updatePosition);
+ connect(occlusion, &QSlider::valueChanged, this, &AudioWidget::newOcclusion);
+
+ connect(roomDimension, &QSlider::valueChanged, this, &AudioWidget::updateRoom);
+ connect(reverbGain, &QSlider::valueChanged, this, &AudioWidget::updateRoom);
+ connect(reflectionGain, &QSlider::valueChanged, this, &AudioWidget::updateRoom);
+
+ connect(mode, &QComboBox::currentIndexChanged, this, &AudioWidget::modeChanged);
+
+ room = new QAudioRoom(&engine);
+ room->setWallMaterial(QAudioRoom::BackWall, QAudioRoom::BrickBare);
+ room->setWallMaterial(QAudioRoom::FrontWall, QAudioRoom::BrickBare);
+ room->setWallMaterial(QAudioRoom::LeftWall, QAudioRoom::BrickBare);
+ room->setWallMaterial(QAudioRoom::RightWall, QAudioRoom::BrickBare);
+ room->setWallMaterial(QAudioRoom::Floor, QAudioRoom::Marble);
+ room->setWallMaterial(QAudioRoom::Ceiling, QAudioRoom::WoodCeiling);
+ updateRoom();
+
+ listener = new QAudioListener(&engine);
+ listener->setPosition({});
+ listener->setRotation({});
+ engine.start();
+
+ sound = new QSpatialSound(&engine);
+ updatePosition();
+
+ animation = new QPropertyAnimation(azimuth, "value");
+ animation->setDuration(10000);
+ animation->setStartValue(-180);
+ animation->setEndValue(180);
+ animation->setLoopCount(-1);
+ connect(animateButton, &QCheckBox::toggled, this, &AudioWidget::animateChanged);
+}
+
+void AudioWidget::setFile(const QString &file)
+{
+ fileEdit->setText(file);
+}
+
+void AudioWidget::updatePosition()
+{
+ const float az = azimuth->value() / 180. * M_PI;
+ const float el = elevation->value() / 180. * M_PI;
+ const float d = distance->value();
+
+ const float x = d * sin(az) * cos(el);
+ const float y = d * sin(el);
+ const float z = -d * cos(az) * cos(el);
+ sound->setPosition({x, y, z});
+}
+
+void AudioWidget::newOcclusion()
+{
+ sound->setOcclusionIntensity(occlusion->value() / 100.);
+}
+
+void AudioWidget::modeChanged()
+{
+ engine.setOutputMode(mode->currentData().value<QAudioEngine::OutputMode>());
+}
+
+void AudioWidget::fileChanged(const QString &file)
+{
+ sound->setSource(QUrl::fromLocalFile(file));
+ sound->setSize(5);
+ sound->setLoops(QSpatialSound::Infinite);
+}
+
+void AudioWidget::openFileDialog()
+{
+ if (fileDialog == nullptr) {
+ const QString dir = QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
+ fileDialog = new QFileDialog(this, tr("Open Audio File"), dir);
+ fileDialog->setAcceptMode(QFileDialog::AcceptOpen);
+ const QStringList mimeTypes{"audio/mpeg", "audio/aac", "audio/x-ms-wma",
+ "audio/x-flac+ogg", "audio/x-wav"};
+ fileDialog->setMimeTypeFilters(mimeTypes);
+ fileDialog->selectMimeTypeFilter(mimeTypes.constFirst());
+ }
+
+ if (fileDialog->exec() == QDialog::Accepted)
+ fileEdit->setText(fileDialog->selectedFiles().constFirst());
+}
+
+void AudioWidget::updateRoom()
+{
+ const float d = roomDimension->value();
+ room->setDimensions(QVector3D(d, d, 400));
+ room->setReflectionGain(float(reflectionGain->value()) / 100);
+ room->setReverbGain(float(reverbGain->value()) / 100);
+}
+
+void AudioWidget::animateChanged()
+{
+ if (animateButton->isChecked())
+ animation->start();
+ else
+ animation->stop();
+}
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ QCoreApplication::setApplicationVersion(qVersion());
+ QGuiApplication::setApplicationDisplayName(AudioWidget::tr("Spatial Audio test application"));
+
+ QCommandLineParser commandLineParser;
+ commandLineParser.addVersionOption();
+ commandLineParser.addHelpOption();
+ commandLineParser.addPositionalArgument("Audio File",
+ "Audio File to play");
+
+ commandLineParser.process(app);
+
+ AudioWidget w;
+ w.show();
+
+ if (!commandLineParser.positionalArguments().isEmpty())
+ w.setFile(commandLineParser.positionalArguments().constFirst());
+
+ return app.exec();
+}
diff --git a/examples/spatialaudio/spatialaudio.pro b/examples/spatialaudio/spatialaudio.pro
new file mode 100644
index 000000000..80039c1a5
--- /dev/null
+++ b/examples/spatialaudio/spatialaudio.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS += audiopanning
+
diff --git a/licenseRule.json b/licenseRule.json
new file mode 100644
index 000000000..7afb5f208
--- /dev/null
+++ b/licenseRule.json
@@ -0,0 +1,109 @@
+[
+ {
+ "comment" : ["file_pattern_ending: strings matched against the end of a file name.",
+ "location keys: regular expression matched against the beginning of",
+ "the file path (relative to the git submodule root).",
+ "spdx: list of SPDX-License-Expression's allowed in the matching files.",
+ "-------------------------------------------------------",
+ "Files with the following endings are Build System licensed,",
+ "unless they are examples",
+ "Files with other endings can also be build system files"
+ ],
+ "file_pattern_ending" : ["CMakeLists.txt", ".cmake", ".pro", ".pri", ".prf",
+ "configure", "configure.bat", "cmake.in", "plist.in", "CMakeLists.txt.in"],
+ "location" : {
+ "" : {
+ "comment" : "Default",
+ "file type" : "build system",
+ "spdx" : ["BSD-3-Clause"]
+ },
+ "(.*)(examples/|snippets/)" : {
+ "comment" : "Example takes precedence",
+ "file type" : "examples and snippets",
+ "spdx" : ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+ }
+ }
+ },
+ {
+ "comments" : ["Files with the following endings are Tool licensed,",
+ "unless they are examples.",
+ "Files with other endings can also be tool files."],
+ "file_pattern_ending" : [".sh", ".py", ".pl", ".bat", ".ps1"],
+ "location" :{
+ "" : {
+ "comment" : "Default",
+ "file type" : "tools and utils",
+ "spdx" : ["LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0"]
+ },
+ "(.*)(examples/|snippets/)" : {
+ "comment" : "Example takes precedence",
+ "file type" : "examples and snippets",
+ "spdx" : ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+ }
+ }
+ },
+ {
+ "comment" : "Files with the following endings are Documentation licensed.",
+ "file_pattern_ending" : [".qdoc", ".qdocinc" , ".qdocconf", ".txt", "README", "qt_attribution.json"],
+ "location" :{
+ "" : {
+ "comment" : "",
+ "file type" : "documentation",
+ "spdx" : ["LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only"]
+ }
+ }
+ },
+ {
+ "comment" : ["All other files",
+ "The licensing is defined only by the file location in the Qt module repository.",
+ "NO <file_pattern_ending> key for this case!",
+ "This needs to be the last entry of the file."],
+ "location" : {
+ "" : {
+ "comment" : "Default",
+ "file type" : "module and plugin",
+ "spdx" : ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only"]
+ },
+ "src/" : {
+ "comment" : "Default",
+ "file type" : "module and plugin",
+ "spdx" : ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only"]
+ },
+ "src/spatialaudioquick3d/" : {
+ "comment" : "Special case",
+ "file type" : "module and plugin",
+ "spdx" : ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only"]
+ },
+ "src/spatialaudio/" : {
+ "comment" : "Special case",
+ "file type" : "module and plugin",
+ "spdx" : ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only"]
+ },
+ "src/resonance-audio/" : {
+ "comment" : "Special case",
+ "file type" : "module and plugin",
+ "spdx" : ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only"]
+ },
+ "tests/" : {
+ "comment" : "Default",
+ "file type" : "test",
+ "spdx" : ["LicenseRef-Qt-Commercial OR GPL-3.0-only"]
+ },
+ "(.*)(examples/|snippets/)" : {
+ "comment" : "Default",
+ "file type" : "examples and snippets",
+ "spdx" : ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+ },
+ "config\\.tests/" : {
+ "comment" : "Default",
+ "file type" : "build system",
+ "spdx" : ["BSD-3-Clause"]
+ },
+ "util/" : {
+ "comment" : "Default",
+ "file type" : "util",
+ "spdx" : ["LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0"]
+ }
+ }
+ }
+]
diff --git a/src/3rdparty/eigen/Eigen/src/Core/arch/NEON/GeneralBlockPanelKernel.h b/src/3rdparty/eigen/Eigen/src/Core/arch/NEON/GeneralBlockPanelKernel.h
index 3481f337e..116dbb448 100644
--- a/src/3rdparty/eigen/Eigen/src/Core/arch/NEON/GeneralBlockPanelKernel.h
+++ b/src/3rdparty/eigen/Eigen/src/Core/arch/NEON/GeneralBlockPanelKernel.h
@@ -24,7 +24,7 @@ struct gebp_traits <float,float,false,false,Architecture::NEON,GEBPPacketFull>
template <typename LaneIdType>
EIGEN_STRONG_INLINE void madd(const Packet4f& a, const Packet4f& b,
- Packet4f& c, Packet4f& tmp,
+ Packet4f& c, Packet4f& /*tmp*/,
const LaneIdType&) const {
acc(a, b, c);
}
diff --git a/src/3rdparty/eigen/Eigen/src/Core/arch/NEON/PacketMath.h b/src/3rdparty/eigen/Eigen/src/Core/arch/NEON/PacketMath.h
index d2aeef430..34c9bbcdd 100644
--- a/src/3rdparty/eigen/Eigen/src/Core/arch/NEON/PacketMath.h
+++ b/src/3rdparty/eigen/Eigen/src/Core/arch/NEON/PacketMath.h
@@ -1668,7 +1668,7 @@ template<> EIGEN_STRONG_INLINE Packet4f pload<Packet4f>(const float* from)
template<> EIGEN_STRONG_INLINE Packet4c pload<Packet4c>(const int8_t* from)
{
Packet4c res;
- memcpy(&res, from, sizeof(Packet4c));
+ memcpy(static_cast<void*>(&res), from, sizeof(Packet4c));
return res;
}
template<> EIGEN_STRONG_INLINE Packet8c pload<Packet8c>(const int8_t* from)
@@ -1678,7 +1678,7 @@ template<> EIGEN_STRONG_INLINE Packet16c pload<Packet16c>(const int8_t* from)
template<> EIGEN_STRONG_INLINE Packet4uc pload<Packet4uc>(const uint8_t* from)
{
Packet4uc res;
- memcpy(&res, from, sizeof(Packet4uc));
+ memcpy(static_cast<void*>(&res), from, sizeof(Packet4c));
return res;
}
template<> EIGEN_STRONG_INLINE Packet8uc pload<Packet8uc>(const uint8_t* from)
@@ -1713,7 +1713,7 @@ template<> EIGEN_STRONG_INLINE Packet4f ploadu<Packet4f>(const float* from)
template<> EIGEN_STRONG_INLINE Packet4c ploadu<Packet4c>(const int8_t* from)
{
Packet4c res;
- memcpy(&res, from, sizeof(Packet4c));
+ memcpy(static_cast<void*>(&res), from, sizeof(Packet4c));
return res;
}
template<> EIGEN_STRONG_INLINE Packet8c ploadu<Packet8c>(const int8_t* from)
@@ -1723,7 +1723,7 @@ template<> EIGEN_STRONG_INLINE Packet16c ploadu<Packet16c>(const int8_t* from)
template<> EIGEN_STRONG_INLINE Packet4uc ploadu<Packet4uc>(const uint8_t* from)
{
Packet4uc res;
- memcpy(&res, from, sizeof(Packet4uc));
+ memcpy(static_cast<void*>(&res), from, sizeof(Packet4c));
return res;
}
template<> EIGEN_STRONG_INLINE Packet8uc ploadu<Packet8uc>(const uint8_t* from)
diff --git a/src/3rdparty/eigen/qt_attribution.json b/src/3rdparty/eigen/qt_attribution.json
index a7cee7341..866a610b0 100644
--- a/src/3rdparty/eigen/qt_attribution.json
+++ b/src/3rdparty/eigen/qt_attribution.json
@@ -1,16 +1,20 @@
-[
- {
- "Id": "eigen",
- "Name": "Eigen",
- "QDocModule": "qtmultimedia",
- "Description": "The Eigen C++ linear algebra library.",
- "QtUsage": "Used for mathematics to support spatial audio.",
+{
+ "Id": "eigen",
+ "Name": "Eigen",
+ "QDocModule": "qtspatialaudio",
+ "Description": "The Eigen C++ linear algebra library.",
+ "QtUsage": "Used for mathematics to support spatial audio.",
+ "SecurityCritical": true,
- "Homepage": "https://eigen.tuxfamily.org/",
- "Version": "3.4.0",
- "License": "Mozilla Public License 2.0 and BSD 3-Clause \"New\" or \"Revised\" License",
- "LicenseId": "MPL-2.0 AND BSD-3-Clause",
- "CopyrightFile": "COPYRIGHTS",
- "LicenseFiles": [ "COPYING.MPL2", "COPYING.BSD" ]
- }
-]
+ "Homepage": "https://eigen.tuxfamily.org/",
+ "Version": "3.4.0",
+ "DownloadLocation": "https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.bz2",
+
+ "License": "Mozilla Public License 2.0 and BSD 3-Clause \"New\" or \"Revised\" License",
+ "LicenseId": "MPL-2.0 AND BSD-3-Clause",
+ "CopyrightFile": "COPYRIGHTS",
+ "LicenseFiles": [
+ "COPYING.MPL2",
+ "COPYING.BSD"
+ ]
+}
diff --git a/src/3rdparty/ffmpeg/qt_attribution.json b/src/3rdparty/ffmpeg/qt_attribution.json
new file mode 100644
index 000000000..6e343be65
--- /dev/null
+++ b/src/3rdparty/ffmpeg/qt_attribution.json
@@ -0,0 +1,55 @@
+[
+ {
+ "Id": "ffmpeg",
+ "Name": "FFmpeg",
+ "QDocModule": "qtmultimedia",
+ "Description": "FFmpeg is a collection of libraries and tools to process multimedia content such as audio, video, subtitles, and related metadata.",
+ "QtUsage": "The FFmpeg media backend uses the \\l {https://ffmpeg.org}{FFmpeg framework}. FFmpeg is licensed under LGPLv2.1 or later versions of the licenses. Note that, while FFmpeg also features some optional components available under GPL or LGPLv3, the binaries that ship with Qt in the online installer do not contain these components. See the \\l {https://ffmpeg.org/legal.html}{FFmpeg licensing page} for further details.",
+ "Homepage": "https://ffmpeg.org/",
+ "DownloadLocation": "https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n6.1.1.zip",
+ "SecurityCritical": true,
+ "Version": "n6.1.1",
+ "LicenseId": "LGPL-2.1-or-later AND BSD-3-Clause AND BSD-2-Clause AND BSD-Source-Code AND ISC AND MIT AND MPL-2.0",
+ "License": "GNU Lesser General Public License v2.1 or later and BSD 3-Clause \"New\" or \"Revised\" License and BSD 2-Clause \"Simplified\" License and BSD Source Code Attribution and ISC License and MIT License and Mozilla Public License 2.0",
+ "Copyright": "Copyright (c) 2000-2023 the FFmpeg developers"
+ },
+ {
+ "Id": "ffmpeg-libjpeg",
+ "Name": "libjpeg",
+ "QDocModule": "qtmultimedia",
+ "Description": "libjpeg is a widely used C library for reading and writing JPEG image files.",
+ "QtUsage": "The FFmpeg media backend uses modified code from libjpeg in `libavcodec/jfdctfst.c`, `libavcodec/jfdctint_template.c` and `libavcodec/jrevdct.c`. For list of modifications, see \\l {https://git.ffmpeg.org/ffmpeg.git}{FFmpeg source code management system}.",
+ "SecurityCritical": true,
+ "Homepage": "https://libjpeg.sourceforge.net/",
+ "DownloadLocation": "https://libjpeg.sourceforge.net/",
+ "License": "Independent JPEG Group License",
+ "LicenseId": "IJG",
+ "Copyright": "Copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding."
+ },
+ {
+ "Id": "ffmpeg-zlib",
+ "Name": "zlib",
+ "QDocModule": "qtmultimedia",
+ "Description": "Zlib is a software library used for data compression",
+ "QtUsage": "The FFmpeg media backend uses the Adler-32 algorithm from zlib",
+ "SecurityCritical": false,
+ "Homepage": "https://www.zlib.net/",
+ "DownloadLocation": "https://www.zlib.net/",
+ "License": "zlib License",
+ "LicenseId": "Zlib",
+ "Copyright": "Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler."
+ },
+ {
+ "Id": "ffmpeg-boost",
+ "Name": "boost",
+ "QDocModule": "qtmultimedia",
+ "Description": "Boost is a set of libraries for the C++ programming language ",
+ "QtUsage": "The FFmpeg media backend uses algorithms from the Boost math library",
+ "SecurityCritical": false,
+ "Homepage": "https://www.boost.org/",
+ "DownloadLocation": "https://www.boost.org/",
+ "License": "Boost Software License 1.0",
+ "LicenseId": "BSL-1.0",
+ "Copyright": "(C) Copyright John Maddock 2006. Copyright (c) 2006 Xiaogang Zhang"
+ }
+]
diff --git a/src/3rdparty/pffft/fftpack.c b/src/3rdparty/pffft/fftpack.c
index b6375a80d..aa8135a50 100644
--- a/src/3rdparty/pffft/fftpack.c
+++ b/src/3rdparty/pffft/fftpack.c
@@ -273,8 +273,8 @@ static void passb2(integer ido, integer l1, const real *cc, real *ch, const real
static void passb3(integer ido, integer l1, const real *cc, real *ch, const real *wa1, const real *wa2)
{
- static const real taur = -.5f;
- static const real taui = .866025403784439f;
+ static const real taur = -.5;
+ static const real taui = .866025403784439;
/* System generated locals */
integer cc_offset, ch_offset;
@@ -419,10 +419,10 @@ static void passb4(integer ido, integer l1, const real *cc, real *ch,
static void passfb5(integer ido, integer l1, const real *cc, real *ch,
const real *wa1, const real *wa2, const real *wa3, const real *wa4, real fsign)
{
- const real tr11 = .309016994374947f;
- const real ti11 = .951056516295154f*fsign;
- const real tr12 = -.809016994374947f;
- const real ti12 = .587785252292473f*fsign;
+ const real tr11 = .309016994374947;
+ const real ti11 = .951056516295154*fsign;
+ const real tr12 = -.809016994374947;
+ const real ti12 = .587785252292473*fsign;
/* System generated locals */
integer cc_offset, ch_offset;
@@ -571,8 +571,8 @@ static void passf2(integer ido, integer l1, const real *cc, real *ch, const real
static void passf3(integer ido, integer l1, const real *cc, real *ch,
const real *wa1, const real *wa2)
{
- static const real taur = -.5f;
- static const real taui = -.866025403784439f;
+ static const real taur = -.5;
+ static const real taui = -.866025403784439;
/* System generated locals */
integer cc_offset, ch_offset;
@@ -771,8 +771,8 @@ static void radb3(integer ido, integer l1, const real *cc, real *ch,
{
/* Initialized data */
- static const real taur = -.5f;
- static const real taui = .866025403784439f;
+ static const real taur = -.5;
+ static const real taui = .866025403784439;
/* System generated locals */
integer cc_offset, ch_offset;
@@ -839,7 +839,7 @@ static void radb4(integer ido, integer l1, const real *cc, real *ch,
{
/* Initialized data */
- static const real sqrt2 = 1.414213562373095f;
+ static const real sqrt2 = 1.414213562373095;
/* System generated locals */
integer cc_offset, ch_offset;
@@ -926,10 +926,10 @@ static void radb5(integer ido, integer l1, const real *cc, real *ch,
{
/* Initialized data */
- static const real tr11 = .309016994374947f;
- static const real ti11 = .951056516295154f;
- static const real tr12 = -.809016994374947f;
- static const real ti12 = .587785252292473f;
+ static const real tr11 = .309016994374947;
+ static const real ti11 = .951056516295154;
+ static const real tr12 = -.809016994374947;
+ static const real ti12 = .587785252292473;
/* System generated locals */
integer cc_offset, ch_offset;
@@ -1111,8 +1111,8 @@ static void radbg(integer ido, integer ip, integer l1, integer idl1,
}
}
}
- ar1 = 1.f;
- ai1 = 0.f;
+ ar1 = 1.;
+ ai1 = 0.;
for (l = 2; l <= ipph; ++l) {
lc = ipp2 - l;
ar1h = dcp * ar1 - dsp * ai1;
@@ -1285,8 +1285,8 @@ static void radf2(integer ido, integer l1, const real *cc, real *ch,
static void radf3(integer ido, integer l1, const real *cc, real *ch,
const real *wa1, const real *wa2)
{
- static const real taur = -.5f;
- static const real taui = .866025403784439f;
+ static const real taur = -.5;
+ static const real taui = .866025403784439;
/* System generated locals */
integer ch_offset, cc_offset;
@@ -1355,7 +1355,7 @@ static void radf4(integer ido, integer l1, const real *cc, real *ch,
{
/* Initialized data */
- static const real hsqt2 = .7071067811865475f;
+ static const real hsqt2 = .7071067811865475;
/* System generated locals */
integer cc_offset, ch_offset;
@@ -1446,10 +1446,10 @@ static void radf5(integer ido, integer l1, const real *cc, real *ch,
{
/* Initialized data */
- static const real tr11 = .309016994374947f;
- static const real ti11 = .951056516295154f;
- static const real tr12 = -.809016994374947f;
- static const real ti12 = .587785252292473f;
+ static const real tr11 = .309016994374947;
+ static const real ti11 = .951056516295154;
+ static const real tr12 = -.809016994374947;
+ static const real ti12 = .587785252292473;
/* System generated locals */
integer cc_offset, ch_offset;
@@ -1661,8 +1661,8 @@ static void radfg(integer ido, integer ip, integer l1, integer idl1,
}
}
- ar1 = 1.f;
- ai1 = 0.f;
+ ar1 = 1.;
+ ai1 = 0.;
for (l = 2; l <= ipph; ++l) {
lc = ipp2 - l;
ar1h = dcp * ar1 - dsp * ai1;
@@ -1984,14 +1984,14 @@ static void cffti1(integer n, real *wa, integer *ifac)
ipm = ip - 1;
for (j = 1; j <= ipm; ++j) {
i1 = i;
- wa[i - 1] = 1.f;
- wa[i] = 0.f;
+ wa[i - 1] = 1.;
+ wa[i] = 0.;
ld += l1;
- fi = 0.f;
+ fi = 0.;
argld = (real) ld * argh;
for (ii = 4; ii <= idot; ii += 2) {
i += 2;
- fi += 1.f;
+ fi += 1.;
arg = fi * argld;
wa[i - 1] = cos(arg);
wa[i] = sin(arg);
@@ -2192,10 +2192,10 @@ static void rffti1(integer n, real *wa, integer *ifac)
ld += l1;
i = is;
argld = (real) ld * argh;
- fi = 0.f;
+ fi = 0.;
for (ii = 3; ii <= ido; ii += 2) {
i += 2;
- fi += 1.f;
+ fi += 1.;
arg = fi * argld;
wa[i - 1] = cos(arg);
wa[i] = sin(arg);
@@ -2277,7 +2277,7 @@ static void cosqb1(integer n, real *x, real *w, real *xh)
void cosqb(integer n, real *x, real *wsave)
{
- static const real tsqrt2 = 2.82842712474619f;
+ static const real tsqrt2 = 2.82842712474619;
/* Local variables */
real x1;
@@ -2287,9 +2287,9 @@ void cosqb(integer n, real *x, real *wsave)
--x;
if (n < 2) {
- x[1] *= 4.f;
+ x[1] *= 4.;
} else if (n == 2) {
- x1 = (x[1] + x[2]) * 4.f;
+ x1 = (x[1] + x[2]) * 4.;
x[2] = tsqrt2 * (x[1] - x[2]);
x[1] = x1;
} else {
@@ -2339,7 +2339,7 @@ static void cosqf1(integer n, real *x, real *w, real *xh)
void cosqf(integer n, real *x, real *wsave)
{
- static const real sqrt2 = 1.4142135623731f;
+ static const real sqrt2 = 1.4142135623731;
/* Local variables */
real tsqx;
@@ -2367,9 +2367,9 @@ void cosqi(integer n, real *wsave)
--wsave;
dt = M_PI/2 / (real) (n);
- fk = 0.f;
+ fk = 0.;
for (k = 1; k <= n; ++k) {
- fk += 1.f;
+ fk += 1.;
wsave[k] = cos(fk * dt);
}
rffti(n, &wsave[n + 1]);
@@ -2458,12 +2458,12 @@ void costi(integer n, real *wsave)
np1 = n + 1;
ns2 = n / 2;
dt = M_PI / (real) nm1;
- fk = 0.f;
+ fk = 0.;
for (k = 2; k <= ns2; ++k) {
kc = np1 - k;
- fk += 1.f;
- wsave[k] = sin(fk * dt) * 2.f;
- wsave[kc] = cos(fk * dt) * 2.f;
+ fk += 1.;
+ wsave[k] = sin(fk * dt) * 2.;
+ wsave[kc] = cos(fk * dt) * 2.;
}
rffti(nm1, &wsave[n + 1]);
} /* costi */
@@ -2480,7 +2480,7 @@ void sinqb(integer n, real *x, real *wsave)
/* Function Body */
if (n <= 1) {
- x[1] *= 4.f;
+ x[1] *= 4.;
return;
}
ns2 = n / 2;
@@ -2538,7 +2538,7 @@ static void sint1(integer n, real *war, real *was, real *xh, real *
{
/* Initialized data */
- static const real sqrt3 = 1.73205080756888f;
+ static const real sqrt3 = 1.73205080756888;
/* Local variables */
integer i, k;
@@ -2568,7 +2568,7 @@ static void sint1(integer n, real *war, real *was, real *xh, real *
} else {
np1 = n + 1;
ns2 = n / 2;
- x[1] = 0.f;
+ x[1] = 0.;
for (k = 1; k <= ns2; ++k) {
kc = np1 - k;
t1 = xh[k] - xh[kc];
@@ -2578,10 +2578,10 @@ static void sint1(integer n, real *war, real *was, real *xh, real *
}
modn = n % 2;
if (modn != 0) {
- x[ns2 + 2] = xh[ns2 + 1] * 4.f;
+ x[ns2 + 2] = xh[ns2 + 1] * 4.;
}
rfftf1(np1, &x[1], &xh[1], &war[1], &ifac[1]);
- xh[1] = x[1] * .5f;
+ xh[1] = x[1] * .5;
for (i = 3; i <= n; i += 2) {
xh[i - 1] = -x[i];
xh[i] = xh[i - 2] + x[i - 1];
@@ -2614,7 +2614,7 @@ void sinti(integer n, real *wsave)
np1 = n + 1;
dt = M_PI / (real) np1;
for (k = 1; k <= ns2; ++k) {
- wsave[k] = sin(k * dt) * 2.f;
+ wsave[k] = sin(k * dt) * 2.;
}
rffti(np1, &wsave[ns2 + 1]);
} /* sinti */
@@ -2652,7 +2652,7 @@ int main(void)
f77complex cx[200], cy[200];
real xh[200];
integer nz, nm1, np1, ns2;
- real arg, tfn;
+ real arg;
real sum, arg1, arg2;
real sum1, sum2, dcfb;
integer modn;
@@ -2716,13 +2716,12 @@ int main(void)
/* 19. CFFTB UNNORMALIZED INVERSE OF CFFTF */
- sqrt2 = sqrt(2.f);
+ sqrt2 = sqrt(2.);
int all_ok = 1;
for (nz = 1; nz <= (int)(sizeof nd/sizeof nd[0]); ++nz) {
n = nd[nz - 1];
modn = n % 2;
fn = (real) n;
- tfn = fn + fn;
np1 = n + 1;
nm1 = n - 1;
for (j = 1; j <= np1; ++j) {
@@ -2740,8 +2739,8 @@ int main(void)
goto L104;
}
for (k = 2; k <= ns2; ++k) {
- sum1 = 0.f;
- sum2 = 0.f;
+ sum1 = 0.;
+ sum2 = 0.;
arg = (real) (k - 1) * dt;
for (i = 1; i <= n; ++i) {
arg1 = (real) (i - 1) * arg;
@@ -2752,8 +2751,8 @@ int main(void)
y[(k << 1) - 2] = -sum2;
}
L104:
- sum1 = 0.f;
- sum2 = 0.f;
+ sum1 = 0.;
+ sum2 = 0.;
for (i = 1; i <= nm1; i += 2) {
sum1 += x[i - 1];
sum2 += x[i];
@@ -2766,7 +2765,7 @@ int main(void)
y[n - 1] = sum1 - sum2;
}
rfftf(n, x, w);
- rftf = 0.f;
+ rftf = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = rftf, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1));
@@ -2775,7 +2774,7 @@ int main(void)
}
rftf /= fn;
for (i = 1; i <= n; ++i) {
- sum = x[0] * .5f;
+ sum = x[0] * .5;
arg = (real) (i - 1) * dt;
if (ns2 < 2) {
goto L108;
@@ -2787,12 +2786,12 @@ int main(void)
}
L108:
if (modn == 0) {
- sum += (real)pow(-1, i-1) * .5f * x[n - 1];
+ sum += (real)pow(-1, i-1) * .5 * x[n - 1];
}
y[i - 1] = sum + sum;
}
rfftb(n, x, w);
- rftb = 0.f;
+ rftb = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = rftb, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1));
@@ -2802,8 +2801,8 @@ int main(void)
}
rfftb(n, y, w);
rfftf(n, y, w);
- cf = 1.f / fn;
- rftfb = 0.f;
+ cf = 1. / fn;
+ rftfb = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = rftfb, r3 = (r1 = cf * y[i - 1] - x[i - 1], fabs(
@@ -2818,7 +2817,7 @@ int main(void)
x[i - 1] = xh[i - 1];
}
for (i = 1; i <= nm1; ++i) {
- y[i - 1] = 0.f;
+ y[i - 1] = 0.;
arg1 = (real) i * dt;
for (k = 1; k <= nm1; ++k) {
y[i - 1] += x[k - 1] * sin((real) k * arg1);
@@ -2827,8 +2826,8 @@ int main(void)
}
sinti(nm1, w);
sint(nm1, x, w);
- cf = .5f / fn;
- sintt = 0.f;
+ cf = .5 / fn;
+ sintt = 0.;
for (i = 1; i <= nm1; ++i) {
/* Computing MAX */
r2 = sintt, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1));
@@ -2839,7 +2838,7 @@ int main(void)
sintt = cf * sintt;
sint(nm1, x, w);
sint(nm1, x, w);
- sintfb = 0.f;
+ sintfb = 0.;
for (i = 1; i <= nm1; ++i) {
/* Computing MAX */
r2 = sintfb, r3 = (r1 = cf * x[i - 1] - y[i - 1], fabs(
@@ -2853,7 +2852,7 @@ int main(void)
x[i - 1] = xh[i - 1];
}
for (i = 1; i <= np1; ++i) {
- y[i - 1] = (x[0] + (real) pow(-1, i+1) * x[n]) * .5f;
+ y[i - 1] = (x[0] + (real) pow(-1, i+1) * x[n]) * .5;
arg = (real) (i - 1) * dt;
for (k = 2; k <= n; ++k) {
y[i - 1] += x[k - 1] * cos((real) (k - 1) * arg);
@@ -2862,7 +2861,7 @@ int main(void)
}
costi(np1, w);
cost(np1, x, w);
- costt = 0.f;
+ costt = 0.;
for (i = 1; i <= np1; ++i) {
/* Computing MAX */
r2 = costt, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1));
@@ -2873,7 +2872,7 @@ int main(void)
costt = cf * costt;
cost(np1, x, w);
cost(np1, x, w);
- costfb = 0.f;
+ costfb = 0.;
for (i = 1; i <= np1; ++i) {
/* Computing MAX */
r2 = costfb, r3 = (r1 = cf * x[i - 1] - y[i - 1], fabs(
@@ -2883,22 +2882,22 @@ int main(void)
/* TEST SUBROUTINES SINQI,SINQF AND SINQB */
- cf = .25f / fn;
+ cf = .25 / fn;
for (i = 1; i <= n; ++i) {
y[i - 1] = xh[i - 1];
}
dt = M_PI / (fn + fn);
for (i = 1; i <= n; ++i) {
- x[i - 1] = 0.f;
+ x[i - 1] = 0.;
arg = dt * (real) i;
for (k = 1; k <= n; ++k) {
x[i - 1] += y[k - 1] * sin((real) (k + k - 1) * arg);
}
- x[i - 1] *= 4.f;
+ x[i - 1] *= 4.;
}
sinqi(n, w);
sinqb(n, y, w);
- sinqbt = 0.f;
+ sinqbt = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = sinqbt, r3 = (r1 = y[i - 1] - x[i - 1], fabs(r1))
@@ -2909,14 +2908,14 @@ int main(void)
sinqbt = cf * sinqbt;
for (i = 1; i <= n; ++i) {
arg = (real) (i + i - 1) * dt;
- y[i - 1] = (real) pow(-1, i+1) * .5f * x[n - 1];
+ y[i - 1] = (real) pow(-1, i+1) * .5 * x[n - 1];
for (k = 1; k <= nm1; ++k) {
y[i - 1] += x[k - 1] * sin((real) k * arg);
}
y[i - 1] += y[i - 1];
}
sinqf(n, x, w);
- sinqft = 0.f;
+ sinqft = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = sinqft, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1))
@@ -2927,7 +2926,7 @@ int main(void)
}
sinqf(n, y, w);
sinqb(n, y, w);
- sinqfb = 0.f;
+ sinqfb = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = sinqfb, r3 = (r1 = cf * y[i - 1] - x[i - 1], fabs(
@@ -2941,16 +2940,16 @@ int main(void)
y[i - 1] = xh[i - 1];
}
for (i = 1; i <= n; ++i) {
- x[i - 1] = 0.f;
+ x[i - 1] = 0.;
arg = (real) (i - 1) * dt;
for (k = 1; k <= n; ++k) {
x[i - 1] += y[k - 1] * cos((real) (k + k - 1) * arg);
}
- x[i - 1] *= 4.f;
+ x[i - 1] *= 4.;
}
cosqi(n, w);
cosqb(n, y, w);
- cosqbt = 0.f;
+ cosqbt = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = cosqbt, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1))
@@ -2960,7 +2959,7 @@ int main(void)
}
cosqbt = cf * cosqbt;
for (i = 1; i <= n; ++i) {
- y[i - 1] = x[0] * .5f;
+ y[i - 1] = x[0] * .5;
arg = (real) (i + i - 1) * dt;
for (k = 2; k <= n; ++k) {
y[i - 1] += x[k - 1] * cos((real) (k - 1) * arg);
@@ -2968,7 +2967,7 @@ int main(void)
y[i - 1] += y[i - 1];
}
cosqf(n, x, w);
- cosqft = 0.f;
+ cosqft = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = cosqft, r3 = (r1 = y[i - 1] - x[i - 1], fabs(r1))
@@ -2980,7 +2979,7 @@ int main(void)
cosqft = cf * cosqft;
cosqb(n, x, w);
cosqf(n, x, w);
- cosqfb = 0.f;
+ cosqfb = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
r2 = cosqfb, r3 = (r1 = cf * x[i - 1] - y[i - 1], fabs(r1));
@@ -2998,7 +2997,7 @@ int main(void)
dt = (2*M_PI) / fn;
for (i = 1; i <= n; ++i) {
arg1 = -((real) (i - 1)) * dt;
- cy[i-1].r = 0.f, cy[i-1].i = 0.f;
+ cy[i-1].r = 0., cy[i-1].i = 0.;
for (k = 1; k <= n; ++k) {
arg2 = (real) (k - 1) * arg1;
r1 = cos(arg2);
@@ -3012,7 +3011,7 @@ int main(void)
}
cffti(n, w);
cfftf(n, (real*)cx, w);
- dcfftf = 0.f;
+ dcfftf = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
q1.r = cx[i-1].r - cy[i-1].r, q1.i = cx[i-1].i - cy[i-1]
@@ -3025,7 +3024,7 @@ int main(void)
dcfftf /= fn;
for (i = 1; i <= n; ++i) {
arg1 = (real) (i - 1) * dt;
- cy[i-1].r = 0.f, cy[i-1].i = 0.f;
+ cy[i-1].r = 0., cy[i-1].i = 0.;
for (k = 1; k <= n; ++k) {
arg2 = (real) (k - 1) * arg1;
r1 = cos(arg2);
@@ -3038,7 +3037,7 @@ int main(void)
}
}
cfftb(n, (real*)cx, w);
- dcfftb = 0.f;
+ dcfftb = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
q1.r = cx[i-1].r - cy[i-1].r, q1.i = cx[i-1].i - cy[i-1].i;
@@ -3046,10 +3045,10 @@ int main(void)
dcfftb = dmax(r1,r2);
cx[i-1].r = cy[i-1].r, cx[i-1].i = cy[i-1].i;
}
- cf = 1.f / fn;
+ cf = 1. / fn;
cfftf(n, (real*)cx, w);
cfftb(n, (real*)cx, w);
- dcfb = 0.f;
+ dcfb = 0.;
for (i = 1; i <= n; ++i) {
/* Computing MAX */
q2.r = cf * cx[i-1].r, q2.i = cf * cx[i-1].i;
diff --git a/src/3rdparty/pffft/pffft.c b/src/3rdparty/pffft/pffft.c
index e6018e162..9271a9ad9 100644
--- a/src/3rdparty/pffft/pffft.c
+++ b/src/3rdparty/pffft/pffft.c
@@ -274,7 +274,7 @@ void pffft_aligned_free(void *p) {
if (p) free(*((void **) p - 1));
}
-int pffft_simd_size() { return SIMD_SZ; }
+int pffft_simd_size(void) { return SIMD_SZ; }
/*
passf2 and passb2 has been merged here, fsign = -1 for passf2, +1 for passb2
@@ -1233,27 +1233,19 @@ PFFFT_Setup *pffft_new_setup(int N, pffft_transform_t transform) {
s->e = (float*)s->data;
s->twiddle = (float*)(s->data + (2*s->Ncvec*(SIMD_SZ-1))/SIMD_SZ);
- if (transform == PFFFT_REAL) {
- for (k=0; k < s->Ncvec; ++k) {
- int i = k/SIMD_SZ;
- int j = k%SIMD_SZ;
- for (m=0; m < SIMD_SZ-1; ++m) {
- float A = -2*M_PI*(m+1)*k / N;
- s->e[(2*(i*3 + m) + 0) * SIMD_SZ + j] = cos(A);
- s->e[(2*(i*3 + m) + 1) * SIMD_SZ + j] = sin(A);
- }
+ for (k=0; k < s->Ncvec; ++k) {
+ int i = k/SIMD_SZ;
+ int j = k%SIMD_SZ;
+ for (m=0; m < SIMD_SZ-1; ++m) {
+ float A = -2*M_PI*(m+1)*k / N;
+ s->e[(2*(i*3 + m) + 0) * SIMD_SZ + j] = cos(A);
+ s->e[(2*(i*3 + m) + 1) * SIMD_SZ + j] = sin(A);
}
+ }
+
+ if (transform == PFFFT_REAL) {
rffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac);
} else {
- for (k=0; k < s->Ncvec; ++k) {
- int i = k/SIMD_SZ;
- int j = k%SIMD_SZ;
- for (m=0; m < SIMD_SZ-1; ++m) {
- float A = -2*M_PI*(m+1)*k / N;
- s->e[(2*(i*3 + m) + 0)*SIMD_SZ + j] = cos(A);
- s->e[(2*(i*3 + m) + 1)*SIMD_SZ + j] = sin(A);
- }
- }
cffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac);
}
@@ -1314,7 +1306,7 @@ void pffft_zreorder(PFFFT_Setup *setup, const float *in, float *out, pffft_direc
v4sf *vout = (v4sf*)out;
assert(in != out);
if (setup->transform == PFFFT_REAL) {
- int k, dk = N/32;
+ int dk = N/32;
if (direction == PFFFT_FORWARD) {
for (k=0; k < dk; ++k) {
INTERLEAVE2(vin[k*8 + 0], vin[k*8 + 1], vout[2*(0*dk + k) + 0], vout[2*(0*dk + k) + 1]);
@@ -1708,19 +1700,19 @@ void pffft_zconvolve_accumulate(PFFFT_Setup *s, const float *a, const float *b,
# endif
#endif
- float ar, ai, br, bi, abr, abi;
+ float ar0, ai0, br0, bi0, abr0, abi0;
#ifndef ZCONVOLVE_USING_INLINE_ASM
v4sf vscal = LD_PS1(scaling);
int i;
#endif
assert(VALIGNED(a) && VALIGNED(b) && VALIGNED(ab));
- ar = ((v4sf_union*)va)[0].f[0];
- ai = ((v4sf_union*)va)[1].f[0];
- br = ((v4sf_union*)vb)[0].f[0];
- bi = ((v4sf_union*)vb)[1].f[0];
- abr = ((v4sf_union*)vab)[0].f[0];
- abi = ((v4sf_union*)vab)[1].f[0];
+ ar0 = ((v4sf_union*)va)[0].f[0];
+ ai0 = ((v4sf_union*)va)[1].f[0];
+ br0 = ((v4sf_union*)vb)[0].f[0];
+ bi0 = ((v4sf_union*)vb)[1].f[0];
+ abr0 = ((v4sf_union*)vab)[0].f[0];
+ abi0 = ((v4sf_union*)vab)[1].f[0];
#ifdef ZCONVOLVE_USING_INLINE_ASM // inline asm version, unfortunately miscompiled by clang 3.2, at least on ubuntu.. so this will be restricted to gcc
const float *a_ = a, *b_ = b; float *ab_ = ab;
@@ -1774,8 +1766,8 @@ void pffft_zconvolve_accumulate(PFFFT_Setup *s, const float *a, const float *b,
}
#endif
if (s->transform == PFFFT_REAL) {
- ((v4sf_union*)vab)[0].f[0] = abr + ar*br*scaling;
- ((v4sf_union*)vab)[1].f[0] = abi + ai*bi*scaling;
+ ((v4sf_union*)vab)[0].f[0] = abr0 + ar0*br0*scaling;
+ ((v4sf_union*)vab)[1].f[0] = abi0 + ai0*bi0*scaling;
}
}
diff --git a/src/3rdparty/pffft/qt_attribution.json b/src/3rdparty/pffft/qt_attribution.json
index 7bbfdbb3a..2cf2c7264 100644
--- a/src/3rdparty/pffft/qt_attribution.json
+++ b/src/3rdparty/pffft/qt_attribution.json
@@ -1,16 +1,16 @@
-[
- {
- "Id": "pfft",
- "Name": "pfft",
- "QDocModule": "qtmultimedia",
- "Description": "A pretty fast FFT.",
- "QtUsage": "Used to support spatial audio.",
+{
+ "Id": "pfft",
+ "Name": "pfft",
+ "QDocModule": "qtspatialaudio",
+ "Description": "A pretty fast FFT.",
+ "QtUsage": "Used to support spatial audio.",
+ "SecurityCritical": true,
- "Homepage": "https://bitbucket.org/jpommier/pffft.git",
- "Version": "7c3b5a7dc510a0f513b9c5b6dc5b56f7aeeda422",
- "CopyrightFile": "COPYRIGHTS",
- "License": "BSD 3-Clause \"New\" or \"Revised\" License",
- "LicenseId": "BSD-3-Clause",
- "LicenseFile": "LICENSE"
- }
-]
+ "Homepage": "https://bitbucket.org/jpommier/pffft.git",
+ "Version": "fbc4058602803f40dc554b8a5d2bcc694c005f2f",
+ "DownloadLocation": "https://bitbucket.org/jpommier/pffft/get/fbc405860280.zip",
+ "CopyrightFile": "COPYRIGHTS",
+ "License": "BSD 3-Clause \"New\" or \"Revised\" License",
+ "LicenseId": "BSD-3-Clause",
+ "LicenseFile": "LICENSE"
+}
diff --git a/src/3rdparty/pffft/test_pffft.c b/src/3rdparty/pffft/test_pffft.c
index 12ba1f7ff..b217b6bec 100644
--- a/src/3rdparty/pffft/test_pffft.c
+++ b/src/3rdparty/pffft/test_pffft.c
@@ -248,8 +248,8 @@ void benchmark_ffts(int N, int cplx) {
int k;
int max_iter = 5120000/N*4;
-#ifdef __arm__
- max_iter /= 4;
+#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
+ max_iter /= 8;
#endif
int iter;
diff --git a/src/3rdparty/resonance-audio/patches/0001-resonance_audio-fix-C-20-build.patch b/src/3rdparty/resonance-audio/patches/0001-resonance_audio-fix-C-20-build.patch
new file mode 100644
index 000000000..2de42ad26
--- /dev/null
+++ b/src/3rdparty/resonance-audio/patches/0001-resonance_audio-fix-C-20-build.patch
@@ -0,0 +1,36 @@
+From f8c9ec7cfabb59977629f303edf7cf90ca8521d3 Mon Sep 17 00:00:00 2001
+From: Marc Mutz <marc.mutz@qt.io>
+Date: Tue, 28 Jun 2022 19:38:13 +0200
+Subject: [PATCH] AlignedAllocator: fix C++20 build
+
+C++17 deprecated, and C++20 removed, the nested pointer, size_type
+etc. typedefs in std::allocator. Since C++11, the way to get these
+types is to go via allocator_traits, so do that.
+
+This breaks compatibility with C++ < 11.
+
+Fixes #61
+---
+ .../resonance_audio/base/aligned_allocator.h | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/resonance_audio/base/aligned_allocator.h b/resonance_audio/base/aligned_allocator.h
+index ac60e8292..628ccaa02 100644
+--- a/resonance_audio/base/aligned_allocator.h
++++ b/resonance_audio/base/aligned_allocator.h
+@@ -72,9 +72,9 @@ void AllignedFree(PointerType mem_block_aligned) {
+ template <typename Type, size_t Alignment>
+ class AlignedAllocator : public std::allocator<Type> {
+ public:
+- typedef typename std::allocator<Type>::pointer Pointer;
+- typedef typename std::allocator<Type>::const_pointer ConstPointer;
+- typedef typename std::allocator<Type>::size_type SizeType;
++ using Pointer = typename std::allocator_traits<std::allocator<Type>>::pointer;
++ using ConstPointer = typename std::allocator_traits<std::allocator<Type>>::const_pointer;
++ using SizeType = typename std::allocator_traits<std::allocator<Type>>::size_type;
+
+ AlignedAllocator() { StaticAlignmentCheck<sizeof(Type), Alignment>(); }
+
+--
+2.25.1
+
diff --git a/src/3rdparty/resonance-audio/qt_attribution.json b/src/3rdparty/resonance-audio/qt_attribution.json
index df0907b01..6f56457cb 100644
--- a/src/3rdparty/resonance-audio/qt_attribution.json
+++ b/src/3rdparty/resonance-audio/qt_attribution.json
@@ -1,16 +1,18 @@
-[
- {
- "Id": "resonance-audio",
- "Name": "Resonance Audio",
- "QDocModule": "qtmultimedia",
- "Description": "3D audio support.",
- "QtUsage": "Used to implement spatial audio.",
+{
+ "Id": "resonance-audio",
+ "Name": "Resonance Audio",
+ "QDocModule": "qtspatialaudio",
+ "Description": "3D audio support.",
+ "QtUsage": "Used to implement spatial audio.",
+ "SecurityCritical": true,
- "Homepage": "https://resonance-audio.github.io/resonance-audio/",
- "Version": "e225aedb5ec76ca6a0fe7079c0b84dbcbb490553",
- "CopyrightFile": "COPYRIGHTS",
- "License": "Apache License 2.0",
- "LicenseId": "Apache-2.0",
- "LicenseFile": "LICENSE"
- }
-]
+ "Homepage": "https://resonance-audio.github.io/resonance-audio/",
+ "Version": "e225aedb5ec7",
+ "DownloadLocation":
+ "https://github.com/resonance-audio/resonance-audio/tree/e225aedb5ec76ca6a0fe7079c0b84dbcbb490553",
+
+ "CopyrightFile": "COPYRIGHTS",
+ "License": "Apache License 2.0",
+ "LicenseId": "Apache-2.0",
+ "LicenseFile": "LICENSE"
+}
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h b/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h
index ac60e8292..628ccaa02 100644
--- a/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h
@@ -72,9 +72,9 @@ void AllignedFree(PointerType mem_block_aligned) {
template <typename Type, size_t Alignment>
class AlignedAllocator : public std::allocator<Type> {
public:
- typedef typename std::allocator<Type>::pointer Pointer;
- typedef typename std::allocator<Type>::const_pointer ConstPointer;
- typedef typename std::allocator<Type>::size_type SizeType;
+ using Pointer = typename std::allocator_traits<std::allocator<Type>>::pointer;
+ using ConstPointer = typename std::allocator_traits<std::allocator<Type>>::const_pointer;
+ using SizeType = typename std::allocator_traits<std::allocator<Type>>::size_type;
AlignedAllocator() { StaticAlignmentCheck<sizeof(Type), Alignment>(); }
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc
index 2046699f8..cb5b85590 100644
--- a/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc
@@ -164,7 +164,12 @@ const AudioBuffer* GraphManager::GetAmbisonicBuffer() const {
}
const AudioBuffer* GraphManager::GetStereoBuffer() const {
- return stereo_mixer_node_->GetOutputBuffer();
+ return stereo_mixer_node_->GetOutputBuffer();
+}
+
+const AudioBuffer *GraphManager::GetReverbBuffer() const
+{
+ return reverb_node_->GetOutputBuffer();
}
size_t GraphManager::GetNumMaxAmbisonicChannels() const {
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h
index ab797ff54..818b6fa01 100644
--- a/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h
@@ -227,6 +227,15 @@ class GraphManager {
// @return Output audio buffer of the stereo mix, or nullptr if no output.
const AudioBuffer* GetStereoBuffer() const;
+ // Returns the last processed buffer containing the reverb data for the room.
+ // The buffer contains stereo data.
+ // Note that, this method will *not* trigger the processing of the audio
+ // graph. |GraphManager::Process| must be called prior to this method call to
+ // ensure that the buffer is up-to-date.
+ //
+ // @return Room reverb audio buffer, or nullptr if no output.
+ const AudioBuffer* GetReverbBuffer() const;
+
// Returns the maximum allowed number of ambisonic channels.
//
// @return Number of channels based on Ambisonic order in the global config.
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc
index ec508144a..f8cc6731b 100644
--- a/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc
@@ -448,7 +448,11 @@ const AudioBuffer* ResonanceAudioApiImpl::GetAmbisonicOutputBuffer() const {
}
const AudioBuffer* ResonanceAudioApiImpl::GetStereoOutputBuffer() const {
- return graph_manager_->GetStereoBuffer();
+ return graph_manager_->GetStereoBuffer();
+}
+
+const AudioBuffer *ResonanceAudioApiImpl::GetReverbBuffer() const {
+ return graph_manager_->GetReverbBuffer();
}
void ResonanceAudioApiImpl::ProcessNextBuffer() {
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h b/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h
index cac118387..0adbc1c67 100644
--- a/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h
@@ -124,6 +124,11 @@ class ResonanceAudioApiImpl : public ResonanceAudioApi {
// @return Pointer to stereo output buffer.
const AudioBuffer* GetStereoOutputBuffer() const;
+ // Returns the last processed buffer containing stereo data for the room reverb
+ //
+ // @return Pointer to room reverb stereo buffer.
+ const AudioBuffer* GetReverbBuffer() const;
+
// Triggers processing of the audio graph with the updated system properties.
void ProcessNextBuffer();
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc
index 36b7042bd..6a9999881 100644
--- a/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc
@@ -95,6 +95,11 @@ void ReverbNode::Update() {
}
}
+const AudioBuffer* ReverbNode::GetOutputBuffer() const
+{
+ return &output_buffer_;
+}
+
const AudioBuffer* ReverbNode::AudioProcess(const NodeInput& input) {
if (rt60_updating_) {
for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h
index e25d0a577..f9921fa66 100644
--- a/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h
@@ -41,6 +41,8 @@ class ReverbNode : public ProcessingNode {
// values depending on the system settings.
void Update();
+ const AudioBuffer *GetOutputBuffer() const;
+
protected:
// Implements ProcessingNode.
const AudioBuffer* AudioProcess(const NodeInput& input) override;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 88f1792b5..7d5f3b8e8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from src.pro.
add_subdirectory(resonance-audio)
@@ -5,13 +8,16 @@ add_subdirectory(multimedia)
if(ANDROID)
add_subdirectory(android)
endif()
+if(QT_FEATURE_spatialaudio)
+ add_subdirectory(spatialaudio)
+endif()
# special case begin
if(TARGET Qt::Quick)
add_subdirectory(multimediaquick)
endif()
if(TARGET Qt::Quick3D AND QT_FEATURE_spatialaudio)
- add_subdirectory(multimediaquick3d)
+ add_subdirectory(spatialaudioquick3d)
endif()
if(TARGET Qt::Widgets)
diff --git a/src/android/CMakeLists.txt b/src/android/CMakeLists.txt
index d437e8f0a..7fb5207ba 100644
--- a/src/android/CMakeLists.txt
+++ b/src/android/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from android.pro.
add_subdirectory(jar)
diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt
index ed51a1e20..01e0e5a08 100644
--- a/src/android/jar/CMakeLists.txt
+++ b/src/android/jar/CMakeLists.txt
@@ -1,9 +1,15 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from jar.pro.
set(java_sources
src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java
src/org/qtproject/qt/android/multimedia/QtCameraListener.java
+ src/org/qtproject/qt/android/multimedia/QtCamera2.java
+ src/org/qtproject/qt/android/multimedia/QtExifDataHandler.java
src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java
+ src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java
src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java
src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java
src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java
@@ -17,8 +23,10 @@ qt_internal_add_jar(Qt${QtMultimedia_VERSION_MAJOR}AndroidMultimedia
OUTPUT_DIR "${QT_BUILD_DIR}/jar"
)
+qt_path_join(destination ${INSTALL_DATADIR} "jar")
+
install_jar(Qt${QtMultimedia_VERSION_MAJOR}AndroidMultimedia
- DESTINATION jar
+ DESTINATION ${destination}
COMPONENT Devel
)
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java
index ce5dd5008..6ddc64dc2 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer.java
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android.multimedia;
@@ -48,6 +12,7 @@ import java.io.FileInputStream;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaFormat;
+import android.media.PlaybackParams;
import android.media.AudioAttributes;
import android.media.TimedText;
import android.net.Uri;
@@ -759,4 +724,28 @@ public class QtAndroidMediaPlayer
Log.w(TAG, exception);
}
}
+
+ public boolean setPlaybackRate(float rate)
+ {
+ PlaybackParams playbackParams = mMediaPlayer.getPlaybackParams();
+ playbackParams.setSpeed(rate);
+ // According to discussion under the patch from QTBUG-61115: At least with DirectShow
+ // and GStreamer, it changes both speed and pitch. (...) need to be consistent
+ if (rate != 0.0)
+ playbackParams.setPitch(Math.abs(rate));
+
+ try {
+ mMediaPlayer.setPlaybackParams(playbackParams);
+ } catch (IllegalStateException | IllegalArgumentException e) {
+ Log.e(TAG, "Cannot set playback rate " + rate + " :" + e.toString());
+ return false;
+ }
+
+ if ((mState & State.Started) == 0 && mMediaPlayer.isPlaying()) {
+ setState(State.Started);
+ startProgressWatcher();
+ }
+
+ return true;
+ }
}
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java
index 245376480..3bc589de6 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android.multimedia;
@@ -49,13 +13,26 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
import android.media.MediaRecorder;
+import android.util.Log;
public class QtAudioDeviceManager
{
+ private static final String TAG = "QtAudioDeviceManager";
static private AudioManager m_audioManager = null;
static private final AudioDevicesReceiver m_audioDevicesReceiver = new AudioDevicesReceiver();
+ static private AudioRecord m_recorder = null;
+ static private AudioTrack m_streamPlayer = null;
+ static private Thread m_streamingThread = null;
+ static private boolean m_isStreaming = false;
+ static private final int m_sampleRate = 8000;
+ static private final int m_channels = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ static private final int m_audioFormat = AudioFormat.ENCODING_PCM_16BIT;
+ static private final int m_bufferSize = AudioRecord.getMinBufferSize(m_sampleRate, m_channels, m_audioFormat);
public static native void onAudioInputDevicesUpdated();
public static native void onAudioOutputDevicesUpdated();
@@ -87,6 +64,11 @@ public class QtAudioDeviceManager
context.registerReceiver(m_audioDevicesReceiver, audioDevicesFilter);
}
+ public static void unregisterAudioHeadsetStateReceiver(Context context)
+ {
+ context.unregisterReceiver(m_audioDevicesReceiver);
+ }
+
static public void setContext(Context context)
{
m_audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
@@ -150,6 +132,7 @@ public class QtAudioDeviceManager
private static String audioDeviceTypeToString(int type)
{
+ // API <= 23 types
switch (type)
{
case AudioDeviceInfo.TYPE_AUX_LINE:
@@ -187,11 +170,16 @@ public class QtAudioDeviceManager
return "Wired headphones";
case AudioDeviceInfo.TYPE_WIRED_HEADSET:
return "Wired headset";
- case AudioDeviceInfo.TYPE_TELEPHONY:
- case AudioDeviceInfo.TYPE_UNKNOWN:
- default:
- return "Unknown-Type";
}
+
+ // API 24
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ if (type == AudioDeviceInfo.TYPE_BUS)
+ return "Bus";
+ }
+
+ return "Unknown-Type";
+
}
private static String[] getAudioDevices(int type)
@@ -251,7 +239,7 @@ public class QtAudioDeviceManager
setAudioOutput(AudioManager.MODE_IN_COMMUNICATION, true, false);
return true;
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
- setAudioOutput(AudioManager.MODE_NORMAL, false, true);
+ setAudioOutput(AudioManager.MODE_IN_COMMUNICATION, false, true);
return true;
case AudioDeviceInfo.TYPE_WIRED_HEADSET:
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
@@ -261,8 +249,15 @@ public class QtAudioDeviceManager
// It doesn't work when WIRED HEADPHONES are connected
// Earpiece has the lowest priority and setWiredHeadsetOn(boolean)
// method to force it is deprecated
+ Log.w(TAG, "Built in Earpiece may not work when "
+ + "Wired Headphones are connected");
setAudioOutput(AudioManager.MODE_IN_CALL, false, false);
return true;
+ case AudioDeviceInfo.TYPE_HDMI:
+ case AudioDeviceInfo.TYPE_HDMI_ARC:
+ case AudioDeviceInfo.TYPE_HDMI_EARC:
+ setAudioOutput(AudioManager.MODE_NORMAL, false, false);
+ return true;
default:
return false;
}
@@ -283,4 +278,65 @@ public class QtAudioDeviceManager
m_audioManager.setSpeakerphoneOn(speakerOn);
}
+
+ private static void streamSound()
+ {
+ byte data[] = new byte[m_bufferSize];
+ while (m_isStreaming) {
+ m_recorder.read(data, 0, m_bufferSize);
+ m_streamPlayer.play();
+ m_streamPlayer.write(data, 0, m_bufferSize);
+ m_streamPlayer.stop();
+ }
+ }
+
+ private static void startSoundStreaming(int inputId, int outputId)
+ {
+ if (m_isStreaming)
+ stopSoundStreaming();
+
+ m_recorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, m_sampleRate, m_channels,
+ m_audioFormat, m_bufferSize);
+ m_streamPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, m_sampleRate, m_channels,
+ m_audioFormat, m_bufferSize, AudioTrack.MODE_STREAM);
+
+ final AudioDeviceInfo[] devices = m_audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
+ for (AudioDeviceInfo deviceInfo : devices) {
+ if (deviceInfo.getId() == outputId) {
+ m_streamPlayer.setPreferredDevice(deviceInfo);
+ } else if (deviceInfo.getId() == inputId) {
+ m_recorder.setPreferredDevice(deviceInfo);
+ }
+ }
+
+ m_recorder.startRecording();
+ m_isStreaming = true;
+
+ m_streamingThread = new Thread(new Runnable() {
+ public void run() {
+ streamSound();
+ }
+ });
+
+ m_streamingThread.start();
+ }
+
+ private static void stopSoundStreaming()
+ {
+ if (!m_isStreaming)
+ return;
+
+ m_isStreaming = false;
+ try {
+ m_streamingThread.join();
+ m_streamingThread = null;
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ m_recorder.stop();
+ m_recorder.release();
+ m_streamPlayer.release();
+ m_streamPlayer = null;
+ m_recorder = null;
+ }
}
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java
new file mode 100644
index 000000000..ac8140197
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java
@@ -0,0 +1,526 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+package org.qtproject.qt.android.multimedia;
+
+import org.qtproject.qt.android.multimedia.QtVideoDeviceManager;
+import org.qtproject.qt.android.multimedia.QtExifDataHandler;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
+import android.media.ImageReader;
+import android.graphics.ImageFormat;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.util.Range;
+import android.view.Surface;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import java.lang.Thread;
+import java.util.ArrayList;
+import java.util.List;
+
+@TargetApi(23)
+public class QtCamera2 {
+
+ CameraDevice mCameraDevice = null;
+ QtVideoDeviceManager mVideoDeviceManager = null;
+ HandlerThread mBackgroundThread;
+ Handler mBackgroundHandler;
+ ImageReader mImageReader = null;
+ ImageReader mCapturedPhotoReader = null;
+ CameraManager mCameraManager;
+ CameraCaptureSession mCaptureSession;
+ CaptureRequest.Builder mPreviewRequestBuilder;
+ CaptureRequest mPreviewRequest;
+ String mCameraId;
+ List<Surface> mTargetSurfaces = new ArrayList<>();
+
+ private static final int STATE_PREVIEW = 0;
+ private static final int STATE_WAITING_LOCK = 1;
+ private static final int STATE_WAITING_PRECAPTURE = 2;
+ private static final int STATE_WAITING_NON_PRECAPTURE = 3;
+ private static final int STATE_PICTURE_TAKEN = 4;
+
+ private int mState = STATE_PREVIEW;
+ private Object mStartMutex = new Object();
+ private boolean mIsStarted = false;
+ private static int MaxNumberFrames = 12;
+ private int mFlashMode = CaptureRequest.CONTROL_AE_MODE_ON;
+ private int mTorchMode = CameraMetadata.FLASH_MODE_OFF;
+ private int mAFMode = CaptureRequest.CONTROL_AF_MODE_OFF;
+ private float mZoomFactor = 1.0f;
+ private Range<Integer> mFpsRange = null;
+ private QtExifDataHandler mExifDataHandler = null;
+
+ native void onCameraOpened(String cameraId);
+ native void onCameraDisconnect(String cameraId);
+ native void onCameraError(String cameraId, int error);
+ CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
+ @Override
+ public void onOpened(CameraDevice cameraDevice) {
+ if (mCameraDevice != null)
+ mCameraDevice.close();
+ mCameraDevice = cameraDevice;
+ onCameraOpened(mCameraId);
+ }
+ @Override
+ public void onDisconnected(CameraDevice cameraDevice) {
+ cameraDevice.close();
+ if (mCameraDevice == cameraDevice)
+ mCameraDevice = null;
+ onCameraDisconnect(mCameraId);
+ }
+ @Override
+ public void onError(CameraDevice cameraDevice, int error) {
+ cameraDevice.close();
+ if (mCameraDevice == cameraDevice)
+ mCameraDevice = null;
+ onCameraError(mCameraId, error);
+ }
+ };
+
+ native void onCaptureSessionConfigured(String cameraId);
+ native void onCaptureSessionConfigureFailed(String cameraId);
+ CameraCaptureSession.StateCallback mCaptureStateCallback = new CameraCaptureSession.StateCallback() {
+ @Override
+ public void onConfigured(CameraCaptureSession cameraCaptureSession) {
+ mCaptureSession = cameraCaptureSession;
+ onCaptureSessionConfigured(mCameraId);
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
+ onCaptureSessionConfigureFailed(mCameraId);
+ }
+
+ @Override
+ public void onActive(CameraCaptureSession cameraCaptureSession) {
+ super.onActive(cameraCaptureSession);
+ onSessionActive(mCameraId);
+ }
+
+ @Override
+ public void onClosed(CameraCaptureSession cameraCaptureSession) {
+ super.onClosed(cameraCaptureSession);
+ onSessionClosed(mCameraId);
+ }
+ };
+
+ native void onSessionActive(String cameraId);
+ native void onSessionClosed(String cameraId);
+ native void onCaptureSessionFailed(String cameraId, int reason, long frameNumber);
+ CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
+ public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) {
+ super.onCaptureFailed(session, request, failure);
+ onCaptureSessionFailed(mCameraId, failure.getReason(), failure.getFrameNumber());
+ }
+
+ private void process(CaptureResult result) {
+ switch (mState) {
+ case STATE_WAITING_LOCK: {
+ Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
+ if (afState == null) {
+ capturePhoto();
+ } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
+ CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
+ Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+ if (aeState == null ||
+ aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
+ mState = STATE_PICTURE_TAKEN;
+ capturePhoto();
+ } else {
+ try {
+ mPreviewRequestBuilder.set(
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
+ mState = STATE_WAITING_PRECAPTURE;
+ mCaptureSession.capture(mPreviewRequestBuilder.build(),
+ mCaptureCallback,
+ mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ Log.w("QtCamera2", "Cannot get access to the camera: " + e);
+ }
+ }
+ }
+ break;
+ }
+ case STATE_WAITING_PRECAPTURE: {
+ Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+ if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
+ mState = STATE_WAITING_NON_PRECAPTURE;
+ }
+ break;
+ }
+ case STATE_WAITING_NON_PRECAPTURE: {
+ Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+ if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
+ mState = STATE_PICTURE_TAKEN;
+ capturePhoto();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onCaptureProgressed(CameraCaptureSession s, CaptureRequest r, CaptureResult partialResult) {
+ process(partialResult);
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession s, CaptureRequest r, TotalCaptureResult result) {
+ process(result);
+ }
+ };
+
+ public QtCamera2(Context context) {
+ mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ mVideoDeviceManager = new QtVideoDeviceManager(context);
+ startBackgroundThread();
+ }
+
+ void startBackgroundThread() {
+ mBackgroundThread = new HandlerThread("CameraBackground");
+ mBackgroundThread.start();
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+ }
+
+ void stopBackgroundThread() {
+ mBackgroundThread.quitSafely();
+ try {
+ mBackgroundThread.join();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ public boolean open(String cameraId) {
+ try {
+ mCameraId = cameraId;
+ mCameraManager.openCamera(cameraId,mStateCallback,mBackgroundHandler);
+ return true;
+ } catch (Exception e){
+ Log.w("QtCamera2", "Failed to open camera:" + e);
+ }
+
+ return false;
+ }
+
+ native void onPhotoAvailable(String cameraId, Image frame);
+
+ ImageReader.OnImageAvailableListener mOnPhotoAvailableListener = new ImageReader.OnImageAvailableListener() {
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ QtCamera2.this.onPhotoAvailable(mCameraId, reader.acquireLatestImage());
+ }
+ };
+
+ native void onFrameAvailable(String cameraId, Image frame);
+
+ ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ try {
+ Image img = reader.acquireLatestImage();
+ if (img != null)
+ QtCamera2.this.onFrameAvailable(mCameraId, img);
+ } catch (IllegalStateException e) {
+ // It seems that ffmpeg is processing images for too long (and does not close it)
+ // Give it a little more time. Restarting the camera session if it doesn't help
+ Log.e("QtCamera2", "Image processing taking too long. Let's wait 0,5s more " + e);
+ try {
+ Thread.sleep(500);
+ QtCamera2.this.onFrameAvailable(mCameraId, reader.acquireLatestImage());
+ } catch (IllegalStateException | InterruptedException e2) {
+ Log.e("QtCamera2", "Will not wait anymore. Restart camera session. " + e2);
+ // Remember current used camera ID, because stopAndClose will clear the value
+ String cameraId = mCameraId;
+ stopAndClose();
+ addImageReader(mImageReader.getWidth(), mImageReader.getHeight(),
+ mImageReader.getImageFormat());
+ open(cameraId);
+ }
+ }
+ }
+ };
+
+
+ public void prepareCamera(int width, int height, int format, int minFps, int maxFps) {
+
+ addImageReader(width, height, format);
+ setFrameRate(minFps, maxFps);
+ }
+
+ private void addImageReader(int width, int height, int format) {
+
+ if (mImageReader != null)
+ removeSurface(mImageReader.getSurface());
+
+ if (mCapturedPhotoReader != null)
+ removeSurface(mCapturedPhotoReader.getSurface());
+
+ mImageReader = ImageReader.newInstance(width, height, format, MaxNumberFrames);
+ mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
+ addSurface(mImageReader.getSurface());
+
+ mCapturedPhotoReader = ImageReader.newInstance(width, height, format, MaxNumberFrames);
+ mCapturedPhotoReader.setOnImageAvailableListener(mOnPhotoAvailableListener, mBackgroundHandler);
+ addSurface(mCapturedPhotoReader.getSurface());
+ }
+
+ private void setFrameRate(int minFrameRate, int maxFrameRate) {
+
+ if (minFrameRate <= 0 || maxFrameRate <= 0)
+ mFpsRange = null;
+ else
+ mFpsRange = new Range<>(minFrameRate, maxFrameRate);
+ }
+
+ public boolean addSurface(Surface surface) {
+ if (mTargetSurfaces.contains(surface))
+ return true;
+
+ return mTargetSurfaces.add(surface);
+ }
+
+ public boolean removeSurface(Surface surface) {
+ return mTargetSurfaces.remove(surface);
+ }
+
+ public void clearSurfaces() {
+ mTargetSurfaces.clear();
+ }
+
+ public boolean createSession() {
+ if (mCameraDevice == null)
+ return false;
+
+ try {
+ mCameraDevice.createCaptureSession(mTargetSurfaces, mCaptureStateCallback, mBackgroundHandler);
+ return true;
+ } catch (Exception exception) {
+ Log.w("QtCamera2", "Failed to create a capture session:" + exception);
+ }
+ return false;
+ }
+
+ public boolean start(int template) {
+
+ if (mCameraDevice == null)
+ return false;
+
+ if (mCaptureSession == null)
+ return false;
+
+ synchronized (mStartMutex) {
+ try {
+ mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(template);
+ mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
+ mAFMode = CaptureRequest.CONTROL_AF_MODE_OFF;
+ for (int mode : mVideoDeviceManager.getSupportedAfModes(mCameraId)) {
+ if (mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
+ mAFMode = mode;
+ break;
+ }
+ }
+
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
+ mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, mTorchMode);
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mAFMode);
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CameraMetadata.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
+ if (mZoomFactor != 1.0f)
+ mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, getScalerCropRegion());
+ if (mFpsRange != null)
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, mFpsRange);
+ mPreviewRequest = mPreviewRequestBuilder.build();
+ mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
+ mIsStarted = true;
+ return true;
+
+ } catch (Exception exception) {
+ Log.w("QtCamera2", "Failed to start preview:" + exception);
+ }
+ return false;
+ }
+ }
+
+ public void stopAndClose() {
+ synchronized (mStartMutex) {
+ try {
+ if (null != mCaptureSession) {
+ mCaptureSession.close();
+ mCaptureSession = null;
+ }
+ if (null != mCameraDevice) {
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ mCameraId = "";
+ mTargetSurfaces.clear();
+ } catch (Exception exception) {
+ Log.w("QtCamera2", "Failed to stop and close:" + exception);
+ }
+ mIsStarted = false;
+ }
+ }
+
+ private void capturePhoto() {
+ try {
+ final CaptureRequest.Builder captureBuilder =
+ mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+ captureBuilder.addTarget(mCapturedPhotoReader.getSurface());
+ captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
+ if (mZoomFactor != 1.0f)
+ captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, getScalerCropRegion());
+
+ CameraCaptureSession.CaptureCallback captureCallback
+ = new CameraCaptureSession.CaptureCallback() {
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ try {
+ mExifDataHandler = new QtExifDataHandler(result);
+ // Reset the focus/flash and go back to the normal state of preview.
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
+ CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
+ mPreviewRequest = mPreviewRequestBuilder.build();
+ mState = STATE_PREVIEW;
+ mCaptureSession.setRepeatingRequest(mPreviewRequest,
+ mCaptureCallback,
+ mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+
+ mCaptureSession.capture(captureBuilder.build(), captureCallback, mBackgroundHandler);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void takePhoto() {
+ try {
+ if (mAFMode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
+ mState = STATE_WAITING_LOCK;
+ mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
+ } else {
+ capturePhoto();
+ }
+ } catch (CameraAccessException e) {
+ Log.w("QtCamera2", "Cannot get access to the camera: " + e);
+ }
+ }
+
+ public void saveExifToFile(String path)
+ {
+ if (mExifDataHandler != null)
+ mExifDataHandler.save(path);
+ else
+ Log.e("QtCamera2", "No Exif data that could be saved to " + path);
+ }
+
+ private Rect getScalerCropRegion()
+ {
+ Rect activePixels = mVideoDeviceManager.getActiveArraySize(mCameraId);
+ float zoomRatio = 1.0f;
+ if (mZoomFactor != 0.0f)
+ zoomRatio = 1.0f/mZoomFactor;
+ int croppedWidth = activePixels.width() - (int)(activePixels.width() * zoomRatio);
+ int croppedHeight = activePixels.height() - (int)(activePixels.height() * zoomRatio);
+ return new Rect(croppedWidth/2, croppedHeight/2, activePixels.width() - croppedWidth/2,
+ activePixels.height() - croppedHeight/2);
+ }
+
+ public void zoomTo(float factor)
+ {
+ synchronized (mStartMutex) {
+ mZoomFactor = factor;
+
+ if (!mIsStarted) {
+ Log.w("QtCamera2", "Cannot set zoom on invalid camera");
+ return;
+ }
+
+ mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, getScalerCropRegion());
+ mPreviewRequest = mPreviewRequestBuilder.build();
+
+ try {
+ mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
+ } catch (Exception exception) {
+ Log.w("QtCamera2", "Failed to set zoom:" + exception);
+ }
+ }
+ }
+ public void setFlashMode(String flashMode)
+ {
+ synchronized (mStartMutex) {
+
+ int flashModeValue = mVideoDeviceManager.stringToControlAEMode(flashMode);
+ if (flashModeValue < 0) {
+ Log.w("QtCamera2", "Unknown flash mode");
+ return;
+ }
+ mFlashMode = flashModeValue;
+
+ if (!mIsStarted)
+ return;
+
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
+ mPreviewRequest = mPreviewRequestBuilder.build();
+
+ try {
+ mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
+ } catch (Exception exception) {
+ Log.w("QtCamera2", "Failed to set flash mode:" + exception);
+ }
+ }
+ }
+
+ private int getTorchModeValue(boolean mode)
+ {
+ return mode ? CameraMetadata.FLASH_MODE_TORCH : CameraMetadata.FLASH_MODE_OFF;
+ }
+
+ public void setTorchMode(boolean torchMode)
+ {
+ synchronized (mStartMutex) {
+ mTorchMode = getTorchModeValue(torchMode);
+
+ if (mIsStarted) {
+ mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, mTorchMode);
+ mPreviewRequest = mPreviewRequestBuilder.build();
+
+ try {
+ mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
+ } catch (Exception exception) {
+ Log.w("QtCamera2", "Failed to set flash mode:" + exception);
+ }
+ }
+ }
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java
index 7f5361e77..3e35eb416 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCameraListener.java
@@ -1,59 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android.multimedia;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
+import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.util.Log;
-import java.io.File;
-import java.io.FileOutputStream;
import java.lang.Math;
-import android.media.ExifInterface;
import java.io.ByteArrayOutputStream;
-import java.lang.String;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Matrix;
public class QtCameraListener implements Camera.ShutterCallback,
Camera.PictureCallback,
@@ -73,6 +33,7 @@ public class QtCameraListener implements Camera.ShutterCallback,
private Camera.Size m_previewSize = null;
private int m_previewFormat = ImageFormat.NV21; // Default preview format on all devices
private int m_previewBytesPerLine = -1;
+ private int m_rotation = 0;
private QtCameraListener(int id)
{
@@ -125,6 +86,11 @@ public class QtCameraListener implements Camera.ShutterCallback,
camera.setPreviewCallbackWithBuffer(null);
}
+ public void setPhotoRotation(int rotation)
+ {
+ m_rotation = rotation;
+ }
+
public void setupPreviewCallback(Camera camera)
{
// Clear previous callback (also clears added buffers)
@@ -208,69 +174,22 @@ public class QtCameraListener implements Camera.ShutterCallback,
@Override
public void onPictureTaken(byte[] data, Camera camera)
{
- File outputFile = null;
- try {
- outputFile = File.createTempFile("pic_", ".jpg", QtMultimediaUtils.getCacheDirectory());
- FileOutputStream out = new FileOutputStream(outputFile);
-
- // we just want to read the exif...
- BitmapFactory.decodeByteArray(data, 0, data.length)
- .compress(Bitmap.CompressFormat.JPEG, 10, out);
-
- ExifInterface exif = new ExifInterface(outputFile.getAbsolutePath());
- int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
- ExifInterface.ORIENTATION_UNDEFINED);
-
- int degree = 0;
-
- switch (orientation) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- degree = 90;
- break;
- case ExifInterface.ORIENTATION_ROTATE_180:
- degree = 180;
- break;
- case ExifInterface.ORIENTATION_ROTATE_270:
- degree = 270;
- break;
- }
-
- Camera.CameraInfo info = new Camera.CameraInfo();
- Camera.getCameraInfo(m_cameraId, info);
-
- int rotation = (info.orientation - degree + 360) % 360;
-
- Bitmap source = BitmapFactory.decodeByteArray(data, 0, data.length);
- Matrix matrix = new Matrix();
- matrix.postRotate(rotation);
-
- if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
- matrix.postScale(-1, 1, source.getWidth() / 2.0f, source.getHeight() / 2.0f);
- }
-
- Bitmap rotatedBitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(),
- source.getHeight(), matrix, true);
-
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
- byte[] byteArray = outputStream.toByteArray();
-
- rotatedBitmap.recycle();
- source.recycle();
-
- notifyPictureCaptured(m_cameraId, byteArray);
-
- return;
-
- } catch (Exception e) {
- Log.w(TAG, "Error fixing bitmap orientation.");
- e.printStackTrace();
- } finally {
- if (outputFile != null && outputFile.exists())
- outputFile.delete();
+ Camera.CameraInfo info = new Camera.CameraInfo();
+ Camera.getCameraInfo(m_cameraId, info);
+ Bitmap source = BitmapFactory.decodeByteArray(data, 0, data.length);
+ Matrix matrix = new Matrix();
+ matrix.postRotate(m_rotation);
+ if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
+ matrix.postScale(-1, 1, source.getWidth() / 2.0f, source.getHeight() / 2.0f);
}
-
- notifyPictureCaptured(m_cameraId, data);
+ Bitmap rotatedBitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(),
+ source.getHeight(), matrix, true);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
+ byte[] byteArray = outputStream.toByteArray();
+ rotatedBitmap.recycle();
+ source.recycle();
+ notifyPictureCaptured(m_cameraId, byteArray);
}
@Override
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtExifDataHandler.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtExifDataHandler.java
new file mode 100644
index 000000000..c2699eb1d
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtExifDataHandler.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+package org.qtproject.qt.android.multimedia;
+
+import android.hardware.camera2.CaptureResult;
+import android.media.ExifInterface;
+import android.os.Build;
+import android.util.Log;
+
+import java.io.IOException;
+
+public class QtExifDataHandler {
+
+ private int mFlashFired = 0;
+ private long mExposureTime = 0L;
+ private float mFocalLength = 0;
+ private static String mModel = Build.MANUFACTURER + " " + Build.MODEL;
+
+ public QtExifDataHandler(CaptureResult r)
+ {
+ Integer flash = r.get(CaptureResult.FLASH_STATE);
+ if (flash != null && flash == CaptureResult.FLASH_STATE_FIRED)
+ mFlashFired = 1;
+
+ Long exposureTime = r.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+ if (exposureTime != null)
+ mExposureTime = exposureTime/1000000000;
+ mFocalLength = r.get(CaptureResult.LENS_FOCAL_LENGTH);
+ }
+
+ public void save(String path)
+ {
+ ExifInterface exif;
+ try {
+ exif = new ExifInterface(path);
+ } catch ( IOException e ) {
+ Log.e("QtExifDataHandler", "Cannot open file: " + path + "\n" + e);
+ return;
+ }
+ exif.setAttribute(ExifInterface.TAG_FLASH, String.valueOf(mFlashFired));
+ exif.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, String.valueOf(mExposureTime));
+ exif.setAttribute(ExifInterface.TAG_FOCAL_LENGTH, String.valueOf(mFocalLength));
+ exif.setAttribute(ExifInterface.TAG_MODEL, mModel);
+
+ try {
+ exif.saveAttributes();
+ } catch ( IOException e ) {
+ Log.e("QtExifDataHandler", "Cannot save file: " + path + "\n" + e);
+ }
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java
index bf1763dee..97d317119 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtMediaRecorderListener.java
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android.multimedia;
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java
index f2c6113b4..bfac6670f 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtMultimediaUtils.java
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android.multimedia;
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java
index 62000716b..30dad68d5 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback.java
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android.multimedia;
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java
index ea7a41505..59406ca59 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder.java
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the (whatever) of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android.multimedia;
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java
index 4d929c6ad..4974f9301 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtSurfaceTextureListener.java
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android.multimedia;
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java
new file mode 100644
index 000000000..3339bddc9
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java
@@ -0,0 +1,229 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android.multimedia;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.MediaCodecList;
+import android.media.MediaCodecInfo;
+import android.util.Range;
+import android.util.Size;
+import android.util.Log;
+
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+public class QtVideoDeviceManager {
+
+ CameraManager mCameraManager;
+ Map<String, CameraCharacteristics> cache;
+
+ public QtVideoDeviceManager(Context context) {
+ mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ cache = new WeakHashMap<String, CameraCharacteristics>();
+ }
+
+ public CameraCharacteristics getCameraCharacteristics(String cameraId) {
+
+ if (cache.containsKey(cameraId))
+ return cache.get(cameraId);
+
+ try {
+ CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(cameraId);
+ cache.put(cameraId, cameraCharacteristics);
+ return cameraCharacteristics;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ static private boolean isSoftwareCodec(String longCodecName) {
+ longCodecName = longCodecName.toLowerCase();
+ return longCodecName.startsWith("omx.google.") || longCodecName.startsWith("c2.android.")
+ || !(longCodecName.startsWith("omx.") || longCodecName.startsWith("c2."));
+ }
+
+ private enum CODEC {
+ DECODER,
+ ENCODER
+ }
+ static private String[] getHWVideoCodecs(CODEC expectedType) {
+ MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ MediaCodecInfo[] mediaCodecInfo = mediaCodecList.getCodecInfos();
+ Set<String> codecs = new HashSet<String>();
+
+ for (MediaCodecInfo codecInfo : mediaCodecInfo) {
+ CODEC currentType = codecInfo.isEncoder() ? CODEC.ENCODER : CODEC.DECODER;
+ if (currentType == expectedType && !isSoftwareCodec(codecInfo.getName())) {
+ String[] supportedTypes = codecInfo.getSupportedTypes();
+ for (String type : supportedTypes) {
+ if (type.startsWith("video/"))
+ codecs.add(type.substring(6));
+ }
+ }
+ }
+ return codecs.toArray(new String[codecs.size()]);
+ }
+
+ static public String[] getHWVideoDecoders() { return getHWVideoCodecs(CODEC.DECODER); }
+ static public String[] getHWVideoEncoders() { return getHWVideoCodecs(CODEC.ENCODER); }
+
+ public String[] getCameraIdList() {
+ try {
+ return mCameraManager.getCameraIdList();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public int getSensorOrientation(String cameraId) {
+ CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics == null)
+ return 0;
+ return characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+ }
+
+ public int getLensFacing(String cameraId) {
+ CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics == null)
+ return 0;
+ return characteristics.get(CameraCharacteristics.LENS_FACING);
+ }
+
+ public String[] getFpsRange(String cameraId) {
+
+ CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics == null)
+ return new String[0];
+
+ Range<Integer>[] ranges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+
+ String[] fps = new String[ranges.length];
+
+ for (int index = 0; index < ranges.length; index++) {
+ fps[index] = ranges[index].toString();
+ }
+
+ return fps;
+ }
+
+ public float getMaxZoom(String cameraId) {
+
+ float maxZoom = 1.0f;
+ final CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics != null)
+ maxZoom = characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
+ return maxZoom;
+ }
+
+ public Rect getActiveArraySize(String cameraId) {
+ Rect activeArraySize = new Rect();
+ final CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics != null)
+ activeArraySize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ return activeArraySize;
+ }
+
+ static final int maxResolution = 3840*2160; // 4k resolution
+ public String[] getStreamConfigurationsSizes(String cameraId, int imageFormat) {
+
+ CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics == null)
+ return new String[0];
+
+ StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ Size[] sizes = map.getOutputSizes(imageFormat);
+ if (sizes == null)
+ return new String[0];
+
+ ArrayList<String> stream = new ArrayList<>();
+
+ for (int index = 0; index < sizes.length; index++) {
+ if (sizes[index].getWidth() * sizes[index].getHeight() <= maxResolution)
+ stream.add(sizes[index].toString());
+ }
+
+ return stream.toArray(new String[0]);
+ }
+
+ public int stringToControlAEMode(String mode) {
+ switch (mode) {
+ case "off":
+ return CaptureRequest.CONTROL_AE_MODE_ON;
+ case "auto":
+ return CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH;
+ case "on":
+ return CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH;
+ case "redeye":
+ return CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
+ case "external":
+ return CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH;
+ default:
+ return -1;
+ }
+ }
+
+ public String controlAEModeToString(int mode) {
+ switch (mode) {
+ case CaptureRequest.CONTROL_AE_MODE_ON:
+ return "off";
+ case CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH:
+ return "auto";
+ case CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH:
+ return "on";
+ case CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
+ return "redeye";
+ case CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH:
+ return "external";
+ case CaptureRequest.CONTROL_AE_MODE_OFF:
+ default:
+ return "unknown";
+ }
+ }
+
+ public int[] getSupportedAfModes(String cameraId) {
+
+ CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics == null)
+ return new int[0];
+
+ return characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
+ }
+
+ public String[] getSupportedFlashModes(String cameraId) {
+
+ CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics == null)
+ return new String[0];
+
+ int supportedFlashModes[] = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
+ ArrayList<String> supportedFlashModesList = new ArrayList<String>();
+ for (int index = 0; index < supportedFlashModes.length; index++) {
+ supportedFlashModesList.add(controlAEModeToString(supportedFlashModes[index]));
+ }
+
+ String[] ret = new String[ supportedFlashModesList.size() ];
+ return supportedFlashModesList.toArray(ret);
+ }
+
+ public boolean isTorchModeSupported(String cameraId) {
+ boolean ret = false;
+ final CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
+ if (characteristics != null)
+ ret = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+ return ret;
+ }
+}
diff --git a/src/multimedia/CMakeLists.txt b/src/multimedia/CMakeLists.txt
index 4c295072c..8c58545b5 100644
--- a/src/multimedia/CMakeLists.txt
+++ b/src/multimedia/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from multimedia.pro.
#####################################################################
@@ -15,9 +18,9 @@ qt_internal_find_apple_system_framework(FWAudioToolbox AudioToolbox) # special c
qt_internal_add_module(Multimedia
PLUGIN_TYPES multimedia
SOURCES
- audio/qaudio.cpp audio/qaudio.h
+ audio/qtaudio.cpp audio/qtaudio.h audio/qaudio.h
audio/qaudiobuffer.cpp audio/qaudiobuffer.h
- audio/qaudiodecoder.cpp audio/qaudiodecoder.h
+ audio/qaudiodecoder.cpp audio/qaudiodecoder.h audio/qaudiodecoder_p.h
audio/qaudiodevice.cpp audio/qaudiodevice.h audio/qaudiodevice_p.h
audio/qaudioinput.cpp audio/qaudioinput.h
audio/qaudiooutput.cpp audio/qaudiooutput.h
@@ -26,16 +29,22 @@ qt_internal_add_module(Multimedia
audio/qaudiosource.cpp audio/qaudiosource.h
audio/qaudiosink.cpp audio/qaudiosink.h
audio/qaudiosystem.cpp audio/qaudiosystem_p.h
+ audio/qaudiostatemachine.cpp audio/qaudiostatemachine_p.h
+ audio/qaudiostatemachineutils_p.h
audio/qsamplecache_p.cpp audio/qsamplecache_p.h
audio/qsoundeffect.cpp audio/qsoundeffect.h
audio/qwavedecoder.cpp audio/qwavedecoder.h
camera/qcamera.cpp camera/qcamera.h camera/qcamera_p.h
camera/qcameradevice.cpp camera/qcameradevice.h camera/qcameradevice_p.h
camera/qimagecapture.cpp camera/qimagecapture.h
+ compat/removed_api.cpp
platform/qplatformaudiodecoder.cpp platform/qplatformaudiodecoder_p.h
platform/qplatformaudioinput_p.h
platform/qplatformaudiooutput_p.h
+ platform/qplatformaudioresampler_p.h
platform/qplatformcamera.cpp platform/qplatformcamera_p.h
+ platform/qplatformvideosource.cpp platform/qplatformvideosource_p.h
+ platform/qplatformsurfacecapture.cpp platform/qplatformsurfacecapture_p.h
platform/qplatformimagecapture.cpp platform/qplatformimagecapture_p.h
platform/qplatformmediacapture.cpp platform/qplatformmediacapture_p.h
platform/qplatformmediadevices.cpp platform/qplatformmediadevices_p.h
@@ -43,10 +52,11 @@ qt_internal_add_module(Multimedia
platform/qplatformmediaformatinfo.cpp platform/qplatformmediaformatinfo_p.h
platform/qplatformmediaintegration.cpp platform/qplatformmediaintegration_p.h
platform/qplatformmediaplayer.cpp platform/qplatformmediaplayer_p.h
- platform/qplatformmediaplugin_p.h
+ platform/qplatformmediaplugin.cpp platform/qplatformmediaplugin_p.h
platform/qplatformvideodevices.cpp platform/qplatformvideodevices_p.h
platform/qplatformvideosink.cpp platform/qplatformvideosink_p.h
playback/qmediaplayer.cpp playback/qmediaplayer.h playback/qmediaplayer_p.h
+ platform/qplatformcapturablewindows_p.h
qmediadevices.cpp qmediadevices.h
qmediaenumdebug.h
qmediaformat.cpp qmediaformat.h
@@ -54,12 +64,18 @@ qt_internal_add_module(Multimedia
qmediastoragelocation.cpp qmediastoragelocation_p.h
qmediatimerange.cpp qmediatimerange.h
qmultimediautils.cpp qmultimediautils_p.h
+ qmaybe_p.h
qtmultimediaglobal.h qtmultimediaglobal_p.h
- recording/qmediacapturesession.cpp recording/qmediacapturesession.h
+ qerrorinfo_p.h
+ recording/qmediacapturesession.cpp recording/qmediacapturesession.h recording/qmediacapturesession_p.h
recording/qmediarecorder.cpp recording/qmediarecorder.h recording/qmediarecorder_p.h
+ recording/qscreencapture.cpp recording/qscreencapture.h
+ recording/qwindowcapture.cpp recording/qwindowcapture.h
+ recording/qcapturablewindow.cpp recording/qcapturablewindow.h recording/qcapturablewindow_p.h
video/qabstractvideobuffer.cpp video/qabstractvideobuffer_p.h
video/qmemoryvideobuffer.cpp video/qmemoryvideobuffer_p.h
- video/qvideoframe.cpp video/qvideoframe.h
+ video/qimagevideobuffer.cpp video/qimagevideobuffer_p.h
+ video/qvideoframe.cpp video/qvideoframe.h video/qvideoframe_p.h
video/qvideosink.cpp video/qvideosink.h
video/qvideotexturehelper.cpp video/qvideotexturehelper_p.h
video/qvideoframeconversionhelper.cpp video/qvideoframeconversionhelper_p.h
@@ -67,6 +83,7 @@ qt_internal_add_module(Multimedia
video/qvideoframeconverter.cpp video/qvideoframeconverter_p.h
video/qvideoframeformat.cpp video/qvideoframeformat.h
video/qvideowindow.cpp video/qvideowindow_p.h
+ video/qtvideo.cpp video/qtvideo.h
INCLUDE_DIRECTORIES
audio
camera
@@ -74,7 +91,6 @@ qt_internal_add_module(Multimedia
platform
playback
recording
- spatial
video
LIBRARIES
Qt::CorePrivate
@@ -86,28 +102,11 @@ qt_internal_add_module(Multimedia
PRIVATE_MODULE_INTERFACE
Qt::CorePrivate
Qt::GuiPrivate
+ NO_PCH_SOURCES
+ compat/removed_api.cpp
GENERATE_CPP_EXPORTS
)
-
-qt_internal_extend_target(Multimedia CONDITION QT_FEATURE_spatialaudio
- SOURCES
- spatial/qambisonicdecoder.cpp spatial/qambisonicdecoder_p.h spatial/qambisonicdecoderdata_p.h
-# spatial/qacousticgeometry.cpp spatial/qacousticgeometry_p.h
- spatial/qspatialaudioengine.cpp spatial/qspatialaudioengine.h spatial/qspatialaudioengine_p.h
- spatial/qspatialaudiolistener.cpp spatial/qspatialaudiolistener.h
- spatial/qspatialaudioroom.cpp spatial/qspatialaudioroom.h spatial/qspatialaudioroom_p.h
- spatial/qspatialaudiosoundsource.cpp spatial/qspatialaudiosoundsource.h spatial/qspatialaudiosoundsource_p.h
- spatial/qspatialaudiostereosource.cpp spatial/qspatialaudiostereosource.h
- INCLUDE_DIRECTORIES
- "../3rdparty/resonance-audio/resonance_audio"
- "../3rdparty/resonance-audio"
- "../resonance-audio"
- "../3rdparty/eigen"
- LIBRARIES
- Qt::BundledResonanceAudio
-)
-
qt_internal_add_simd_part(Multimedia SIMD sse2
SOURCES
video/qvideoframeconversionhelper_sse2.cpp
@@ -168,7 +167,7 @@ qt_internal_extend_target(Multimedia CONDITION ANDROID
OpenSLES
)
-if (ANDROID)
+if(ANDROID)
set_property(TARGET Multimedia APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
jar/Qt${QtMultimedia_VERSION_MAJOR}AndroidMultimedia.jar:org.qtproject.qt.android.multimedia.QtAudioDeviceManager
)
@@ -190,6 +189,11 @@ qt_internal_extend_target(Multimedia CONDITION APPLE
${FWCoreFoundation}
)
+qt_internal_extend_target(Multimedia CONDITION MACOS
+ SOURCES
+ darwin/qmacosaudiodatautils_p.h
+)
+
qt_internal_extend_target(Multimedia CONDITION IOS OR TVOS
SOURCES
darwin/qcoreaudiosessionmanager.mm darwin/qcoreaudiosessionmanager_p.h
@@ -218,20 +222,36 @@ qt_internal_extend_target(Multimedia CONDITION QT_FEATURE_wmf
windows/qwindowsaudiosink.cpp windows/qwindowsaudiosink_p.h
windows/qwindowsaudioutils.cpp windows/qwindowsaudioutils_p.h
windows/qwindowsmediadevices.cpp windows/qwindowsmediadevices_p.h
+ windows/qwindowsmediafoundation.cpp windows/qwindowsmediafoundation_p.h
windows/qwindowsresampler.cpp windows/qwindowsresampler_p.h
windows/qwindowsmultimediautils.cpp windows/qwindowsmultimediautils_p.h
windows/qwindowsmfdefs.cpp windows/qwindowsmfdefs_p.h
- windows/qwindowsiupointer_p.h
+ windows/qcomptr_p.h
+ windows/qcomtaskresource_p.h
INCLUDE_DIRECTORIES
windows
LIBRARIES
- WMF::WMF
- mfreadwrite
winmm
- wmcodecdspuuid
ksuser
)
+# Only on Windows' MinGW/LLVM build, symbols from `strmif.h` and `ddraw.h` conflicts
+# with symbols defined in `ksmedia.h`.
+qt_internal_extend_target(Multimedia CONDITION QT_FEATURE_wmf AND MINGW
+ NO_UNITY_BUILD_SOURCES
+ windows/qwindowsaudiodevice.cpp windows/qwindowsaudiodevice_p.h
+ windows/qwindowsaudiosource.cpp windows/qwindowsaudiosource_p.h
+ windows/qwindowsaudiosink.cpp windows/qwindowsaudiosink_p.h
+ windows/qwindowsaudioutils.cpp windows/qwindowsaudioutils_p.h
+ windows/qwindowsmediadevices.cpp windows/qwindowsmediadevices_p.h
+ windows/qwindowsmediafoundation.cpp windows/qwindowsmediafoundation_p.h
+ windows/qwindowsresampler.cpp windows/qwindowsresampler_p.h
+ windows/qwindowsmultimediautils.cpp windows/qwindowsmultimediautils_p.h
+ windows/qwindowsmfdefs.cpp windows/qwindowsmfdefs_p.h
+ windows/qcomptr_p.h
+ windows/qcomtaskresource_p.h
+)
+
qt_internal_extend_target(Multimedia CONDITION WASM
SOURCES
wasm/qwasmmediadevices.cpp wasm/qwasmmediadevices_p.h
@@ -242,6 +262,9 @@ qt_internal_extend_target(Multimedia CONDITION WASM
wasm
PUBLIC_LIBRARIES
openal
+ NO_UNITY_BUILD_SOURCES
+ wasm/qwasmaudiosink.cpp
+ # To avoid collision between symbols defined in wasm/qwasmaudiosource.cpp.
)
set(VIDEO_VERTEX_SHADERS
@@ -270,7 +293,7 @@ set(VIDEO_SHADERS
"shaders/nv12_bt2020_hlg.frag"
)
-qt_internal_add_shaders(Multimedia "shaders"
+qt_internal_add_shaders(Multimedia "qtmultimedia_shaders"
SILENT
BATCHABLE
PRECOMPILE
@@ -284,7 +307,7 @@ qt_internal_add_shaders(Multimedia "shaders"
string(REPLACE ".frag" "_linear.frag.qsb" LINEAR_VIDEO_SHADERS "${VIDEO_SHADERS}")
-qt_internal_add_shaders(Multimedia "shaders_linear"
+qt_internal_add_shaders(Multimedia "qtmultimedia_shaders_linear"
SILENT
BATCHABLE
PRECOMPILE
@@ -299,7 +322,7 @@ qt_internal_add_shaders(Multimedia "shaders_linear"
QMM_OUTPUTSURFACE_LINEAR
)
-qt_internal_add_shaders(Multimedia "shaders_gl_macos"
+qt_internal_add_shaders(Multimedia "qtmultimedia_shaders_gl_macos"
SILENT
BATCHABLE
PRECOMPILE
@@ -315,7 +338,7 @@ qt_internal_add_shaders(Multimedia "shaders_gl_macos"
"shaders/rectsampler_bgra.frag"
)
-qt_internal_add_shaders(Multimedia "shaders_gl_macos_linear"
+qt_internal_add_shaders(Multimedia "qtmultimedia_shaders_gl_macos_linear"
SILENT
BATCHABLE
PRECOMPILE
@@ -331,3 +354,8 @@ qt_internal_add_shaders(Multimedia "shaders_gl_macos_linear"
OUTPUTS
"shaders/rectsampler_bgra_linear.frag.qsb"
)
+
+if(DEFINED QT_DEFAULT_MEDIA_BACKEND)
+ target_compile_definitions(Multimedia
+ PRIVATE QT_DEFAULT_MEDIA_BACKEND="${QT_DEFAULT_MEDIA_BACKEND}")
+endif()
diff --git a/src/multimedia/alsa/qalsaaudiodevice.cpp b/src/multimedia/alsa/qalsaaudiodevice.cpp
index d38a872ec..f5d4a2209 100644
--- a/src/multimedia/alsa/qalsaaudiodevice.cpp
+++ b/src/multimedia/alsa/qalsaaudiodevice.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
diff --git a/src/multimedia/alsa/qalsaaudiodevice_p.h b/src/multimedia/alsa/qalsaaudiodevice_p.h
index 4f7bc5757..f82ea4f5a 100644
--- a/src/multimedia/alsa/qalsaaudiodevice_p.h
+++ b/src/multimedia/alsa/qalsaaudiodevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
diff --git a/src/multimedia/alsa/qalsaaudiosink.cpp b/src/multimedia/alsa/qalsaaudiosink.cpp
index b6c75b7e9..e515219a2 100644
--- a/src/multimedia/alsa/qalsaaudiosink.cpp
+++ b/src/multimedia/alsa/qalsaaudiosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -57,42 +21,22 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcAlsaOutput, "qt.multimedia.alsa.output")
+static Q_LOGGING_CATEGORY(lcAlsaOutput, "qt.multimedia.alsa.output")
//#define DEBUG_AUDIO 1
-QAlsaAudioSink::QAlsaAudioSink(const QByteArray &device)
+QAlsaAudioSink::QAlsaAudioSink(const QByteArray &device, QObject *parent)
+ : QPlatformAudioSink(parent)
{
- bytesAvailable = 0;
- handle = 0;
- access = SND_PCM_ACCESS_RW_INTERLEAVED;
- pcmformat = SND_PCM_FORMAT_S16;
- buffer_frames = 0;
- period_frames = 0;
- buffer_size = 0;
- period_size = 0;
- buffer_time = 100000;
- period_time = 20000;
- totalTimeValue = 0;
- audioBuffer = 0;
- errorState = QAudio::NoError;
- deviceState = QAudio::StoppedState;
- audioSource = 0;
- pullMode = true;
- resuming = false;
- opened = false;
-
- m_volume = 1.0f;
-
m_device = device;
timer = new QTimer(this);
- connect(timer,SIGNAL(timeout()),SLOT(userFeed()));
+ connect(timer, &QTimer::timeout, this, &QAlsaAudioSink::userFeed);
}
QAlsaAudioSink::~QAlsaAudioSink()
{
close();
- disconnect(timer, SIGNAL(timeout()));
+ disconnect(timer, &QTimer::timeout, this, &QAlsaAudioSink::userFeed);
QCoreApplication::processEvents();
delete timer;
}
@@ -171,21 +115,22 @@ int QAlsaAudioSink::setFormat()
break;
case QAudioFormat::Int16:
if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- pcmformat = SND_PCM_FORMAT_S16_LE;
- else
pcmformat = SND_PCM_FORMAT_S16_BE;
+ else
+ pcmformat = SND_PCM_FORMAT_S16_LE;
break;
case QAudioFormat::Int32:
if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- pcmformat = SND_PCM_FORMAT_S32_LE;
- else
pcmformat = SND_PCM_FORMAT_S32_BE;
+ else
+ pcmformat = SND_PCM_FORMAT_S32_LE;
break;
case QAudioFormat::Float:
if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- pcmformat = SND_PCM_FORMAT_FLOAT_LE;
- else
pcmformat = SND_PCM_FORMAT_FLOAT_BE;
+ else
+ pcmformat = SND_PCM_FORMAT_FLOAT_LE;
+ break;
default:
break;
}
@@ -213,6 +158,11 @@ void QAlsaAudioSink::start(QIODevice* device)
pullMode = true;
audioSource = device;
+ connect(audioSource, &QIODevice::readyRead, timer, [this] {
+ if (!timer->isActive()) {
+ timer->start(period_time / 1000);
+ }
+ });
deviceState = QAudio::ActiveState;
open();
@@ -586,8 +536,7 @@ void QAlsaAudioSink::resume()
}
resuming = true;
- deviceState = pullMode ? QAudio::ActiveState : QAudio::IdleState;
-
+ deviceState = suspendedInState;
errorState = QAudio::NoError;
timer->start(period_time/1000);
emit stateChanged(deviceState);
@@ -607,6 +556,7 @@ QAudioFormat QAlsaAudioSink::format() const
void QAlsaAudioSink::suspend()
{
if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) {
+ suspendedInState = deviceState;
snd_pcm_drain(handle);
timer->stop();
deviceState = QAudio::SuspendedState;
@@ -662,11 +612,13 @@ bool QAlsaAudioSink::deviceReady()
} else if(l == 0) {
// Did not get any data to output
+ timer->stop();
+ snd_pcm_drain(handle);
bytesAvailable = bytesFree();
if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
// Underrun
if (deviceState != QAudio::IdleState) {
- errorState = QAudio::UnderrunError;
+ errorState = audioSource->atEnd() ? QAudio::NoError : QAudio::UnderrunError;
emit errorChanged(errorState);
deviceState = QAudio::IdleState;
emit stateChanged(deviceState);
diff --git a/src/multimedia/alsa/qalsaaudiosink_p.h b/src/multimedia/alsa/qalsaaudiosink_p.h
index 9c6da2557..0f5a5aa5a 100644
--- a/src/multimedia/alsa/qalsaaudiosink_p.h
+++ b/src/multimedia/alsa/qalsaaudiosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -72,7 +36,7 @@ class QAlsaAudioSink : public QPlatformAudioSink
friend class AlsaOutputPrivate;
Q_OBJECT
public:
- QAlsaAudioSink(const QByteArray &device);
+ QAlsaAudioSink(const QByteArray &device, QObject *parent);
~QAlsaAudioSink();
qint64 write( const char *data, qint64 len );
@@ -95,10 +59,11 @@ public:
qreal volume() const override;
- QIODevice* audioSource;
+ QIODevice* audioSource = nullptr;
QAudioFormat settings;
- QAudio::Error errorState;
- QAudio::State deviceState;
+ QAudio::Error errorState = QAudio::NoError;
+ QAudio::State deviceState = QAudio::StoppedState;
+ QAudio::State suspendedInState = QAudio::SuspendedState;
private slots:
void userFeed();
@@ -108,14 +73,14 @@ signals:
void processMore();
private:
- bool opened;
- bool pullMode;
- bool resuming;
- int buffer_size;
- int period_size;
- qint64 totalTimeValue;
- unsigned int buffer_time;
- unsigned int period_time;
+ bool opened = false;
+ bool pullMode = true;
+ bool resuming = false;
+ int buffer_size = 0;
+ int period_size = 0;
+ qint64 totalTimeValue = 0;
+ unsigned int buffer_time = 100000;
+ unsigned int period_time = 20000;
snd_pcm_uframes_t buffer_frames;
snd_pcm_uframes_t period_frames;
int xrun_recovery(int err);
@@ -124,16 +89,15 @@ private:
bool open();
void close();
- QTimer* timer;
+ QTimer* timer = nullptr;
QByteArray m_device;
- int bytesAvailable;
- qint64 elapsedTimeOffset;
- char* audioBuffer;
- snd_pcm_t* handle;
- snd_pcm_access_t access;
- snd_pcm_format_t pcmformat;
- snd_pcm_hw_params_t *hwparams;
- qreal m_volume;
+ int bytesAvailable = 0;
+ qint64 elapsedTimeOffset = 0;
+ char* audioBuffer = nullptr;
+ snd_pcm_t* handle = nullptr;
+ snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ snd_pcm_hw_params_t *hwparams = nullptr;
+ qreal m_volume = 1.0f;
};
class AlsaOutputPrivate : public QIODevice
diff --git a/src/multimedia/alsa/qalsaaudiosource.cpp b/src/multimedia/alsa/qalsaaudiosource.cpp
index 9302894d1..ebf6e24e2 100644
--- a/src/multimedia/alsa/qalsaaudiosource.cpp
+++ b/src/multimedia/alsa/qalsaaudiosource.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -52,13 +16,13 @@
#include <QtCore/qvarlengtharray.h>
#include <QtMultimedia/private/qaudiohelpers_p.h>
#include "qalsaaudiosource_p.h"
-#include "qalsaaudiodevice_p.h"
QT_BEGIN_NAMESPACE
//#define DEBUG_AUDIO 1
-QAlsaAudioSource::QAlsaAudioSource(const QByteArray &device)
+QAlsaAudioSource::QAlsaAudioSource(const QByteArray &device, QObject *parent)
+ : QPlatformAudioSource(parent)
{
bytesAvailable = 0;
handle = 0;
@@ -80,13 +44,13 @@ QAlsaAudioSource::QAlsaAudioSource(const QByteArray &device)
m_device = device;
timer = new QTimer(this);
- connect(timer,SIGNAL(timeout()),SLOT(userFeed()));
+ connect(timer, &QTimer::timeout, this, &QAlsaAudioSource::userFeed);
}
QAlsaAudioSource::~QAlsaAudioSource()
{
close();
- disconnect(timer, SIGNAL(timeout()));
+ disconnect(timer, &QTimer::timeout, this, &QAlsaAudioSource::userFeed);
QCoreApplication::processEvents();
delete timer;
}
@@ -178,21 +142,22 @@ int QAlsaAudioSource::setFormat()
break;
case QAudioFormat::Int16:
if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- pcmformat = SND_PCM_FORMAT_S16_LE;
- else
pcmformat = SND_PCM_FORMAT_S16_BE;
+ else
+ pcmformat = SND_PCM_FORMAT_S16_LE;
break;
case QAudioFormat::Int32:
if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- pcmformat = SND_PCM_FORMAT_S32_LE;
- else
pcmformat = SND_PCM_FORMAT_S32_BE;
+ else
+ pcmformat = SND_PCM_FORMAT_S32_LE;
break;
case QAudioFormat::Float:
if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- pcmformat = SND_PCM_FORMAT_FLOAT_LE;
- else
pcmformat = SND_PCM_FORMAT_FLOAT_BE;
+ else
+ pcmformat = SND_PCM_FORMAT_FLOAT_LE;
+ break;
default:
break;
}
@@ -405,7 +370,7 @@ bool QAlsaAudioSource::open()
bytesAvailable = checkBytesReady();
if(pullMode)
- connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed()));
+ connect(audioSource, &QIODevice::readyRead, this, &QAlsaAudioSource::userFeed);
// Step 6: Start audio processing
chunks = buffer_size/period_size;
diff --git a/src/multimedia/alsa/qalsaaudiosource_p.h b/src/multimedia/alsa/qalsaaudiosource_p.h
index 7d360544f..87487a6ad 100644
--- a/src/multimedia/alsa/qalsaaudiosource_p.h
+++ b/src/multimedia/alsa/qalsaaudiosource_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -98,7 +62,7 @@ class QAlsaAudioSource : public QPlatformAudioSource
{
Q_OBJECT
public:
- QAlsaAudioSource(const QByteArray &device);
+ QAlsaAudioSource(const QByteArray &device, QObject *parent);
~QAlsaAudioSource();
qint64 read(char* data, qint64 len);
diff --git a/src/multimedia/alsa/qalsamediadevices.cpp b/src/multimedia/alsa/qalsamediadevices.cpp
index e2561e66a..9466fa0cd 100644
--- a/src/multimedia/alsa/qalsamediadevices.cpp
+++ b/src/multimedia/alsa/qalsamediadevices.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qalsamediadevices_p.h"
#include "qmediadevices.h"
@@ -49,6 +13,26 @@
QT_BEGIN_NAMESPACE
+namespace {
+
+struct free_char
+{
+ void operator()(char *c) const { ::free(c); }
+};
+
+using unique_str = std::unique_ptr<char, free_char>;
+
+bool operator==(const unique_str &str, std::string_view sv)
+{
+ return std::string_view{ str.get() } == sv;
+}
+bool operator!=(const unique_str &str, std::string_view sv)
+{
+ return !(str == sv);
+}
+
+} // namespace
+
QAlsaMediaDevices::QAlsaMediaDevices()
: QPlatformMediaDevices()
{
@@ -58,45 +42,66 @@ static QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode)
{
QList<QAudioDevice> devices;
- QByteArray filter;
-
// Create a list of all current audio devices that support mode
- void **hints, **n;
- char *name, *descr, *io;
-
- if(snd_device_name_hint(-1, "pcm", &hints) < 0) {
+ void **hints;
+ if (snd_device_name_hint(-1, "pcm", &hints) < 0) {
qWarning() << "no alsa devices available";
return devices;
}
- n = hints;
- if(mode == QAudioDevice::Input) {
- filter = "Input";
- } else {
- filter = "Output";
- }
+ std::string_view filter = (mode == QAudioDevice::Input) ? "Input" : "Output";
+
+ QAlsaAudioDeviceInfo *sysdefault = nullptr;
+ auto makeDeviceInfo = [&filter, mode](void *entry) -> QAlsaAudioDeviceInfo * {
+ unique_str name{ snd_device_name_get_hint(entry, "NAME") };
+ if (name && name != "null") {
+ unique_str descr{ snd_device_name_get_hint(entry, "DESC") };
+ unique_str io{ snd_device_name_get_hint(entry, "IOID") };
+
+ if (descr && (!io || (io == filter))) {
+ auto *infop = new QAlsaAudioDeviceInfo{
+ name.get(),
+ QString::fromUtf8(descr.get()),
+ mode,
+ };
+ return infop;
+ }
+ }
+ return nullptr;
+ };
+
+ bool hasDefault = false;
+ void **n = hints;
while (*n != NULL) {
- name = snd_device_name_get_hint(*n, "NAME");
- if (name != 0 && qstrcmp(name, "null") != 0) {
- descr = snd_device_name_get_hint(*n, "DESC");
- io = snd_device_name_get_hint(*n, "IOID");
-
- if ((descr != NULL) && ((io == NULL) || (io == filter))) {
- auto *infop = new QAlsaAudioDeviceInfo(name, QString::fromUtf8(descr), mode);
- devices.append(infop->create());
- if (strcmp(name, "default") == 0)
- infop->isDefault = true;
+ QAlsaAudioDeviceInfo *infop = makeDeviceInfo(*n++);
+
+ if (infop) {
+ devices.append(infop->create());
+ if (!hasDefault && infop->id.startsWith("default")) {
+ infop->isDefault = true;
+ hasDefault = true;
}
+ if (!sysdefault && infop->id.startsWith("sysdefault"))
+ sysdefault = infop;
+ }
+ }
- free(descr);
- free(io);
+ if (!hasDefault && sysdefault) {
+ // Make "sysdefault" the default device if there is no "default" device exists
+ sysdefault->isDefault = true;
+ hasDefault = true;
+ }
+ if (!hasDefault && devices.size() > 0) {
+ // forcefully declare the first device as "default"
+ QAlsaAudioDeviceInfo *infop = makeDeviceInfo(hints[0]);
+ if (infop) {
+ infop->isDefault = true;
+ devices.prepend(infop->create());
}
- free(name);
- ++n;
}
- snd_device_name_free_hint(hints);
+ snd_device_name_free_hint(hints);
return devices;
}
@@ -110,19 +115,16 @@ QList<QAudioDevice> QAlsaMediaDevices::audioOutputs() const
return availableDevices(QAudioDevice::Output);
}
-QList<QCameraDevice> QAlsaMediaDevices::videoInputs() const
-{
- return {};
-}
-
-QPlatformAudioSource *QAlsaMediaDevices::createAudioSource(const QAudioDevice &deviceInfo)
+QPlatformAudioSource *QAlsaMediaDevices::createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QAlsaAudioSource(deviceInfo.id());
+ return new QAlsaAudioSource(deviceInfo.id(), parent);
}
-QPlatformAudioSink *QAlsaMediaDevices::createAudioSink(const QAudioDevice &deviceInfo)
+QPlatformAudioSink *QAlsaMediaDevices::createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QAlsaAudioSink(deviceInfo.id());
+ return new QAlsaAudioSink(deviceInfo.id(), parent);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/alsa/qalsamediadevices_p.h b/src/multimedia/alsa/qalsamediadevices_p.h
index 54df9c851..d9fbb7c97 100644
--- a/src/multimedia/alsa/qalsamediadevices_p.h
+++ b/src/multimedia/alsa/qalsamediadevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QALSAMEDIADEVICES_H
#define QALSAMEDIADEVICES_H
@@ -66,9 +30,10 @@ public:
QList<QAudioDevice> audioInputs() const override;
QList<QAudioDevice> audioOutputs() const override;
- QList<QCameraDevice> videoInputs() const override;
- QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override;
- QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override;
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/android/qandroidaudiodevice.cpp b/src/multimedia/android/qandroidaudiodevice.cpp
index fcd979a5a..576774fd8 100644
--- a/src/multimedia/android/qandroidaudiodevice.cpp
+++ b/src/multimedia/android/qandroidaudiodevice.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidaudiodevice_p.h"
diff --git a/src/multimedia/android/qandroidaudiodevice_p.h b/src/multimedia/android/qandroidaudiodevice_p.h
index 39aec7311..d448306fe 100644
--- a/src/multimedia/android/qandroidaudiodevice_p.h
+++ b/src/multimedia/android/qandroidaudiodevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOPENSLESDEVICEINFO_H
#define QOPENSLESDEVICEINFO_H
diff --git a/src/multimedia/android/qandroidaudiosink.cpp b/src/multimedia/android/qandroidaudiosink.cpp
index bd7005a5b..4da4c5fdc 100644
--- a/src/multimedia/android/qandroidaudiosink.cpp
+++ b/src/multimedia/android/qandroidaudiosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidaudiosink_p.h"
#include "qopenslesengine_p.h"
@@ -48,8 +12,6 @@
#include <SLES/OpenSLES_AndroidConfiguration.h>
#endif // ANDROID
-#define BUFFER_COUNT 2
-
QT_BEGIN_NAMESPACE
static inline void openSlDebugInfo()
@@ -64,26 +26,9 @@ static inline void openSlDebugInfo()
<< "\nDefault buffer size: " << QOpenSLESEngine::getDefaultBufferSize(format);
}
-QAndroidAudioSink::QAndroidAudioSink(const QByteArray &device)
- : m_deviceName(device),
- m_state(QAudio::StoppedState),
- m_error(QAudio::NoError),
- m_outputMixObject(nullptr),
- m_playerObject(nullptr),
- m_playItf(nullptr),
- m_volumeItf(nullptr),
- m_bufferQueueItf(nullptr),
- m_audioSource(nullptr),
- m_buffers(nullptr),
- m_volume(1.0),
- m_pullMode(false),
- m_nextBuffer(0),
- m_bufferSize(0),
- m_elapsedTime(0),
- m_processedBytes(0),
- m_availableBuffers(BUFFER_COUNT),
- m_eventMask(SL_PLAYEVENT_HEADATEND),
- m_startRequiresInit(true)
+QAndroidAudioSink::QAndroidAudioSink(const QByteArray &device, QObject *parent)
+ : QPlatformAudioSink(parent),
+ m_deviceName(device)
{
#ifndef ANDROID
m_streamType = -1;
@@ -117,16 +62,17 @@ void QAndroidAudioSink::start(QIODevice *device)
if (!preparePlayer())
return;
+ m_endSound = false;
m_pullMode = true;
m_audioSource = device;
m_nextBuffer = 0;
m_processedBytes = 0;
- m_availableBuffers = BUFFER_COUNT;
+ m_availableBuffers = BufferCount;
setState(QAudio::ActiveState);
setError(QAudio::NoError);
// Attempt to fill buffers first.
- for (int i = 0; i != BUFFER_COUNT; ++i) {
+ for (int i = 0; i != BufferCount; ++i) {
const int index = i * m_bufferSize;
const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize);
if (readSize && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf,
@@ -145,6 +91,16 @@ void QAndroidAudioSink::start(QIODevice *device)
// Change the state to playing.
// We need to do this after filling the buffers or processedBytes might get corrupted.
startPlayer();
+ connect(m_audioSource, &QIODevice::readyRead, this, &QAndroidAudioSink::readyRead);
+}
+
+void QAndroidAudioSink::readyRead()
+{
+ if (m_pullMode && m_state == QAudio::IdleState) {
+ setState(QAudio::ActiveState);
+ setError(QAudio::NoError);
+ QMetaObject::invokeMethod(this, "bufferAvailable", Qt::QueuedConnection);
+ }
}
QIODevice *QAndroidAudioSink::start()
@@ -157,7 +113,7 @@ QIODevice *QAndroidAudioSink::start()
m_pullMode = false;
m_processedBytes = 0;
- m_availableBuffers = BUFFER_COUNT;
+ m_availableBuffers = BufferCount;
m_audioSource = new SLIODevicePrivate(this);
m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
@@ -222,7 +178,7 @@ void QAndroidAudioSink::resume()
return;
}
- setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState);
+ setState(m_suspendedInState);
setError(QAudio::NoError);
}
@@ -248,6 +204,7 @@ void QAndroidAudioSink::suspend()
return;
}
+ m_suspendedInState = m_state;
setState(QAudio::SuspendedState);
setError(QAudio::NoError);
}
@@ -291,18 +248,15 @@ void QAndroidAudioSink::onBytesProcessed(qint64 bytes)
m_processedBytes += bytes;
}
-void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex)
+void QAndroidAudioSink::bufferAvailable()
{
- Q_UNUSED(count);
- Q_UNUSED(playIndex);
-
if (m_state == QAudio::StoppedState)
return;
if (!m_pullMode) { // We're in push mode.
// Signal that there is a new open slot in the buffer and return
const int val = m_availableBuffers.fetchAndAddRelease(1) + 1;
- if (val == BUFFER_COUNT)
+ if (val == BufferCount)
QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection);
return;
@@ -310,7 +264,23 @@ void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex)
// We're in pull mode.
const int index = m_nextBuffer * m_bufferSize;
- const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize);
+ qint64 readSize = 0;
+ if (m_audioSource->atEnd()) {
+ // The whole sound was passed to player buffer, but setting SL_PLAYSTATE_STOPPED state
+ // too quickly will result in cutting of end of the sound. Therefore, we make it a little
+ // longer with empty data to make sure they will be played correctly
+ if (m_endSound) {
+ m_endSound = false;
+ setState(QAudio::IdleState);
+ return;
+ }
+ m_endSound = true;
+ readSize = m_bufferSize;
+ memset(m_buffers + index, 0, readSize);
+ } else {
+ readSize = m_audioSource->read(m_buffers + index, m_bufferSize);
+ }
+
if (readSize < 1) {
QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection);
@@ -326,8 +296,10 @@ void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex)
return;
}
- m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
- QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize));
+ m_nextBuffer = (m_nextBuffer + 1) % BufferCount;
+ if (!m_endSound) {
+ QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize));
+ }
}
void QAndroidAudioSink::playCallback(SLPlayItf player, void *ctx, SLuint32 event)
@@ -340,10 +312,9 @@ void QAndroidAudioSink::playCallback(SLPlayItf player, void *ctx, SLuint32 event
void QAndroidAudioSink::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx)
{
- SLBufferQueueState state;
- (*bufferQueue)->GetState(bufferQueue, &state);
+ Q_UNUSED(bufferQueue);
QAndroidAudioSink *audioOutput = reinterpret_cast<QAndroidAudioSink *>(ctx);
- audioOutput->bufferAvailable(state.count, state.playIndex);
+ QMetaObject::invokeMethod(audioOutput, "bufferAvailable", Qt::QueuedConnection);
}
bool QAndroidAudioSink::preparePlayer()
@@ -353,6 +324,9 @@ bool QAndroidAudioSink::preparePlayer()
else
return true;
+ if (!QOpenSLESEngine::setAudioOutput(m_deviceName))
+ qWarning() << "Unable to set up Audio Output Device";
+
SLEngineItf engine = QOpenSLESEngine::instance()->slEngine();
if (!engine) {
qWarning() << "No engine";
@@ -360,7 +334,7 @@ bool QAndroidAudioSink::preparePlayer()
return false;
}
- SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BUFFER_COUNT };
+ SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BufferCount };
SLAndroidDataFormat_PCM_EX pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format);
SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat };
@@ -492,7 +466,7 @@ bool QAndroidAudioSink::preparePlayer()
}
if (!m_buffers)
- m_buffers = new char[BUFFER_COUNT * m_bufferSize];
+ m_buffers = new char[BufferCount * m_bufferSize];
setError(QAudio::NoError);
m_startRequiresInit = false;
@@ -525,7 +499,7 @@ void QAndroidAudioSink::destroyPlayer()
m_buffers = nullptr;
m_processedBytes = 0;
m_nextBuffer = 0;
- m_availableBuffers.storeRelease(BUFFER_COUNT);
+ m_availableBuffers.storeRelease(BufferCount);
m_playItf = nullptr;
m_volumeItf = nullptr;
m_bufferQueueItf = nullptr;
@@ -536,10 +510,14 @@ void QAndroidAudioSink::stopPlayer()
{
setState(QAudio::StoppedState);
- if (m_audioSource && !m_pullMode) {
- m_audioSource->close();
- delete m_audioSource;
- m_audioSource = nullptr;
+ if (m_audioSource) {
+ if (m_pullMode) {
+ disconnect(m_audioSource, &QIODevice::readyRead, this, &QAndroidAudioSink::readyRead);
+ } else {
+ m_audioSource->close();
+ delete m_audioSource;
+ m_audioSource = nullptr;
+ }
}
// We need to change the state manually...
@@ -599,7 +577,7 @@ qint64 QAndroidAudioSink::writeData(const char *data, qint64 len)
m_processedBytes += len;
setState(QAudio::ActiveState);
setError(QAudio::NoError);
- m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT;
+ m_nextBuffer = (m_nextBuffer + 1) % BufferCount;
return len;
}
diff --git a/src/multimedia/android/qandroidaudiosink_p.h b/src/multimedia/android/qandroidaudiosink_p.h
index 2a843eed8..4cb63b252 100644
--- a/src/multimedia/android/qandroidaudiosink_p.h
+++ b/src/multimedia/android/qandroidaudiosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOPENSLESAUDIOOUTPUT_H
#define QOPENSLESAUDIOOUTPUT_H
@@ -65,7 +29,7 @@ class QAndroidAudioSink : public QPlatformAudioSink
Q_OBJECT
public:
- QAndroidAudioSink(const QByteArray &device);
+ QAndroidAudioSink(const QByteArray &device, QObject *parent);
~QAndroidAudioSink();
void start(QIODevice *device) override;
@@ -91,7 +55,7 @@ private:
Q_INVOKABLE void onEOSEvent();
Q_INVOKABLE void onBytesProcessed(qint64 bytes);
- void bufferAvailable(quint32 count, quint32 playIndex);
+ Q_INVOKABLE void bufferAvailable();
static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event);
static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx);
@@ -99,6 +63,7 @@ private:
bool preparePlayer();
void destroyPlayer();
void stopPlayer();
+ void readyRead();
void startPlayer();
qint64 writeData(const char *data, qint64 len);
@@ -107,25 +72,29 @@ private:
SLmillibel adjustVolume(qreal vol);
+ static constexpr int BufferCount = 2;
+
QByteArray m_deviceName;
- QAudio::State m_state;
- QAudio::Error m_error;
- SLObjectItf m_outputMixObject;
- SLObjectItf m_playerObject;
- SLPlayItf m_playItf;
- SLVolumeItf m_volumeItf;
- SLBufferQueueItf m_bufferQueueItf;
- QIODevice *m_audioSource;
- char *m_buffers;
- qreal m_volume;
- bool m_pullMode;
- int m_nextBuffer;
- int m_bufferSize;
- qint64 m_elapsedTime;
- qint64 m_processedBytes;
- QAtomicInt m_availableBuffers;
- SLuint32 m_eventMask;
- bool m_startRequiresInit;
+ QAudio::State m_state = QAudio::StoppedState;
+ QAudio::State m_suspendedInState = QAudio::SuspendedState;
+ QAudio::Error m_error = QAudio::NoError;
+ SLObjectItf m_outputMixObject = nullptr;
+ SLObjectItf m_playerObject = nullptr;
+ SLPlayItf m_playItf = nullptr;
+ SLVolumeItf m_volumeItf = nullptr;
+ SLBufferQueueItf m_bufferQueueItf = nullptr;
+ QIODevice *m_audioSource = nullptr;
+ char *m_buffers = nullptr;
+ qreal m_volume = 1.0;
+ bool m_pullMode = false;
+ int m_nextBuffer = 0;
+ int m_bufferSize = 0;
+ qint64 m_elapsedTime = 0;
+ qint64 m_processedBytes = 0;
+ bool m_endSound = false;
+ QAtomicInt m_availableBuffers = BufferCount;
+ SLuint32 m_eventMask = SL_PLAYEVENT_HEADATEND;
+ bool m_startRequiresInit = true;
qint32 m_streamType;
QAudioFormat m_format;
diff --git a/src/multimedia/android/qandroidaudiosource.cpp b/src/multimedia/android/qandroidaudiosource.cpp
index 121c6294a..3a7332fd1 100644
--- a/src/multimedia/android/qandroidaudiosource.cpp
+++ b/src/multimedia/android/qandroidaudiosource.cpp
@@ -1,55 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidaudiosource_p.h"
#include "qopenslesengine_p.h"
#include <private/qaudiohelpers_p.h>
-#include <QtCore/private/qandroidextras_p.h>
#include <qbuffer.h>
#include <qdebug.h>
+#include <qloggingcategory.h>
#ifdef ANDROID
#include <SLES/OpenSLES_AndroidConfiguration.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qpermissions.h>
#endif
+static Q_LOGGING_CATEGORY(qLcAndroidAudioSource, "qt.multimedia.android.audiosource")
+
QT_BEGIN_NAMESPACE
#define NUM_BUFFERS 2
@@ -59,20 +26,13 @@ QT_BEGIN_NAMESPACE
#ifdef ANDROID
static bool hasRecordingPermission()
{
- if (QNativeInterface::QAndroidApplication::sdkVersion() < 23)
- return true;
-
- const auto key = QStringLiteral("android.permission.RECORD_AUDIO");
- // Permission already granted?
- if (QtAndroidPrivate::checkPermission(key).result() == QtAndroidPrivate::Authorized)
- return true;
+ QMicrophonePermission permission;
- if (QtAndroidPrivate::requestPermission(key).result() != QtAndroidPrivate::Authorized) {
- qDebug("Microphone permission denied by user!");
- return false;
- }
+ const bool permitted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+ if (!permitted)
+ qCWarning(qLcAndroidAudioSource, "Missing microphone permission!");
- return true;
+ return permitted;
}
static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context)
@@ -84,8 +44,9 @@ static void bufferQueueCallback(SLBufferQueueItf, void *context)
QMetaObject::invokeMethod(reinterpret_cast<QAndroidAudioSource*>(context), "processBuffer");
}
-QAndroidAudioSource::QAndroidAudioSource(const QByteArray &device)
- : m_device(device)
+QAndroidAudioSource::QAndroidAudioSource(const QByteArray &device, QObject *parent)
+ : QPlatformAudioSource(parent)
+ , m_device(device)
, m_engine(QOpenSLESEngine::instance())
, m_recorderObject(0)
, m_recorder(0)
@@ -180,7 +141,7 @@ QIODevice *QAndroidAudioSource::start()
m_pullMode = false;
m_pushBuffer.clear();
- m_bufferIODevice = new QBuffer(&m_pushBuffer);
+ m_bufferIODevice = new QBuffer(&m_pushBuffer, this);
m_bufferIODevice->open(QIODevice::ReadOnly);
if (startRecording()) {
@@ -398,6 +359,10 @@ void QAndroidAudioSource::processBuffer()
QByteArray *processedBuffer = &m_buffers[m_currentBuffer];
writeDataToDevice(processedBuffer->constData(), processedBuffer->size());
+ // Make sure that it was not stopped from writeDataToDevice
+ if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState)
+ return;
+
// Re-enqueue the buffer
SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue,
processedBuffer->data(),
diff --git a/src/multimedia/android/qandroidaudiosource_p.h b/src/multimedia/android/qandroidaudiosource_p.h
index 727f1746f..13578509c 100644
--- a/src/multimedia/android/qandroidaudiosource_p.h
+++ b/src/multimedia/android/qandroidaudiosource_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOPENSLESAUDIOINPUT_H
#define QOPENSLESAUDIOINPUT_H
@@ -76,7 +40,7 @@ class QAndroidAudioSource : public QPlatformAudioSource
Q_OBJECT
public:
- QAndroidAudioSource(const QByteArray &device);
+ QAndroidAudioSource(const QByteArray &device, QObject *parent);
~QAndroidAudioSource();
void start(QIODevice *device);
diff --git a/src/multimedia/android/qandroidmediadevices.cpp b/src/multimedia/android/qandroidmediadevices.cpp
index 8a38bef83..55533621c 100644
--- a/src/multimedia/android/qandroidmediadevices.cpp
+++ b/src/multimedia/android/qandroidmediadevices.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidmediadevices_p.h"
#include "qmediadevices.h"
@@ -53,7 +17,26 @@
QT_BEGIN_NAMESPACE
-QAndroidMediaDevices::QAndroidMediaDevices() : QPlatformMediaDevices() { }
+Q_DECLARE_JNI_CLASS(QtAudioDeviceManager,
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager");
+
+
+QAndroidMediaDevices::QAndroidMediaDevices() : QPlatformMediaDevices()
+{
+ QtJniTypes::QtAudioDeviceManager::callStaticMethod<void>(
+ "registerAudioHeadsetStateReceiver",
+ QNativeInterface::QAndroidApplication::context());
+}
+
+QAndroidMediaDevices::~QAndroidMediaDevices()
+{
+ // Object of QAndroidMediaDevices type is static. Unregistering will happend only when closing
+ // the application. In such case it is probably not needed, but let's leave it for
+ // compatibility with Android documentation
+ QtJniTypes::QtAudioDeviceManager::callStaticMethod<void>(
+ "unregisterAudioHeadsetStateReceiver",
+ QNativeInterface::QAndroidApplication::context());
+}
QList<QAudioDevice> QAndroidMediaDevices::audioInputs() const
{
@@ -65,34 +48,36 @@ QList<QAudioDevice> QAndroidMediaDevices::audioOutputs() const
return QOpenSLESEngine::availableDevices(QAudioDevice::Output);
}
-QPlatformAudioSource *QAndroidMediaDevices::createAudioSource(const QAudioDevice &deviceInfo)
+QPlatformAudioSource *QAndroidMediaDevices::createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QAndroidAudioSource(deviceInfo.id());
+ return new QAndroidAudioSource(deviceInfo.id(), parent);
}
-QPlatformAudioSink *QAndroidMediaDevices::createAudioSink(const QAudioDevice &deviceInfo)
+QPlatformAudioSink *QAndroidMediaDevices::createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QAndroidAudioSink(deviceInfo.id());
+ return new QAndroidAudioSink(deviceInfo.id(), parent);
}
void QAndroidMediaDevices::forwardAudioOutputsChanged()
{
- audioOutputsChanged();
+ emit audioOutputsChanged();
}
void QAndroidMediaDevices::forwardAudioInputsChanged()
{
- audioInputsChanged();
+ emit audioInputsChanged();
}
static void onAudioInputDevicesUpdated(JNIEnv */*env*/, jobject /*thiz*/)
{
- static_cast<QAndroidMediaDevices*>(QPlatformMediaDevices::instance())->forwardAudioInputsChanged();
+ static_cast<QAndroidMediaDevices*>(QPlatformMediaIntegration::instance()->mediaDevices())->forwardAudioInputsChanged();
}
static void onAudioOutputDevicesUpdated(JNIEnv */*env*/, jobject /*thiz*/)
{
- static_cast<QAndroidMediaDevices*>(QPlatformMediaDevices::instance())->forwardAudioOutputsChanged();
+ static_cast<QAndroidMediaDevices*>(QPlatformMediaIntegration::instance()->mediaDevices())->forwardAudioOutputsChanged();
}
Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
@@ -126,11 +111,6 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
if (!registered)
return JNI_ERR;
- QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
- "registerAudioHeadsetStateReceiver",
- "(Landroid/content/Context;)V",
- QNativeInterface::QAndroidApplication::context());
-
return JNI_VERSION_1_6;
}
diff --git a/src/multimedia/android/qandroidmediadevices_p.h b/src/multimedia/android/qandroidmediadevices_p.h
index 25de67f54..a77ed0451 100644
--- a/src/multimedia/android/qandroidmediadevices_p.h
+++ b/src/multimedia/android/qandroidmediadevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDMEDIADEVICES_H
#define QANDROIDMEDIADEVICES_H
@@ -61,10 +25,13 @@ class QAndroidMediaDevices : public QPlatformMediaDevices
public:
QAndroidMediaDevices();
+ ~QAndroidMediaDevices();
QList<QAudioDevice> audioInputs() const override;
QList<QAudioDevice> audioOutputs() const override;
- QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override;
- QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override;
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
void forwardAudioOutputsChanged();
void forwardAudioInputsChanged();
diff --git a/src/multimedia/android/qopenslesengine.cpp b/src/multimedia/android/qopenslesengine.cpp
index 94de56327..738161ab7 100644
--- a/src/multimedia/android/qopenslesengine.cpp
+++ b/src/multimedia/android/qopenslesengine.cpp
@@ -1,48 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qopenslesengine_p.h"
#include "qandroidaudiosource_p.h"
#include "qandroidaudiodevice_p.h"
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qjniobject.h>
+#include <QtCore/qpermissions.h>
#include <QtCore/private/qandroidextras_p.h>
#include <qdebug.h>
@@ -83,6 +49,22 @@ QOpenSLESEngine *QOpenSLESEngine::instance()
return openslesEngine();
}
+static SLuint32 getChannelMask(unsigned channelCount)
+{
+ switch (channelCount) {
+ case 1: return SL_SPEAKER_FRONT_CENTER;
+ case 2: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+ case 3: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_FRONT_CENTER;
+ case 4: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT
+ | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT;
+ case 5: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_BACK_LEFT
+ | SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_FRONT_CENTER;
+ case 6: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_BACK_LEFT
+ | SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY;
+ default: return 0; // Default to 0 for an unsupported or unknown number of channels
+ }
+}
+
SLAndroidDataFormat_PCM_EX QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &format)
{
SLAndroidDataFormat_PCM_EX format_pcm;
@@ -91,9 +73,7 @@ SLAndroidDataFormat_PCM_EX QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudi
format_pcm.sampleRate = format.sampleRate() * 1000;
format_pcm.bitsPerSample = format.bytesPerSample() * 8;
format_pcm.containerSize = format.bytesPerSample() * 8;
- format_pcm.channelMask = (format.channelCount() == 1 ?
- SL_SPEAKER_FRONT_CENTER :
- SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
+ format_pcm.channelMask = getChannelMask(format_pcm.numChannels);
format_pcm.endianness = (QSysInfo::ByteOrder == QSysInfo::LittleEndian ?
SL_BYTEORDER_LITTLEENDIAN :
SL_BYTEORDER_BIGENDIAN);
@@ -147,15 +127,22 @@ QList<QAudioDevice> QOpenSLESEngine::availableDevices(QAudioDevice::Mode mode)
return devices;
}
+bool QOpenSLESEngine::setAudioOutput(const QByteArray &deviceId)
+{
+ return QJniObject::callStaticMethod<jboolean>(
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
+ "setAudioOutput",
+ deviceId.toInt());
+}
+
static bool hasRecordPermission()
{
- const auto recordPerm = QtAndroidPrivate::checkPermission(QStringLiteral("android.permission.RECORD_AUDIO"));
- return recordPerm.result() == QtAndroidPrivate::Authorized;
+ return qApp->checkPermission(QMicrophonePermission{}) == Qt::PermissionStatus::Granted;
}
QList<int> QOpenSLESEngine::supportedChannelCounts(QAudioDevice::Mode mode) const
{
- if (mode == QAudioDevice::Input && hasRecordPermission()) {
+ if (mode == QAudioDevice::Input) {
if (!m_checkedInputFormats)
const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats();
return m_supportedInputChannelCounts;
@@ -166,7 +153,7 @@ QList<int> QOpenSLESEngine::supportedChannelCounts(QAudioDevice::Mode mode) cons
QList<int> QOpenSLESEngine::supportedSampleRates(QAudioDevice::Mode mode) const
{
- if (mode == QAudioDevice::Input && hasRecordPermission()) {
+ if (mode == QAudioDevice::Input) {
if (!m_checkedInputFormats)
const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats();
return m_supportedInputSampleRates;
@@ -374,6 +361,10 @@ bool QOpenSLESEngine::inputFormatIsSupported(SLAndroidDataFormat_PCM_EX format)
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
SLDataSink audioSnk = { &loc_bq, &format };
+ // only ask permission when it is about to create the audiorecorder
+ if (!hasRecordPermission())
+ return false;
+
result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0);
if (result == SL_RESULT_SUCCESS)
result = (*recorder)->Realize(recorder, false);
diff --git a/src/multimedia/android/qopenslesengine_p.h b/src/multimedia/android/qopenslesengine_p.h
index 3adf34594..9918ac888 100644
--- a/src/multimedia/android/qopenslesengine_p.h
+++ b/src/multimedia/android/qopenslesengine_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOPENSLESENGINE_H
#define QOPENSLESENGINE_H
@@ -75,6 +39,7 @@ public:
static SLAndroidDataFormat_PCM_EX audioFormatToSLFormatPCM(const QAudioFormat &format);
static QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode);
+ static bool setAudioOutput(const QByteArray &deviceId);
QList<int> supportedChannelCounts(QAudioDevice::Mode mode) const;
QList<int> supportedSampleRates(QAudioDevice::Mode mode) const;
diff --git a/src/multimedia/audio/qaudio.h b/src/multimedia/audio/qaudio.h
index 868ef2f6a..5ae994b9f 100644
--- a/src/multimedia/audio/qaudio.h
+++ b/src/multimedia/audio/qaudio.h
@@ -1,46 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIO_H
#define QAUDIO_H
+#if 0
+#pragma qt_class(QAudio)
+#endif
+
#include <QtMultimedia/qtmultimediaglobal.h>
#include <QtCore/qmetatype.h>
@@ -51,7 +18,12 @@ QT_BEGIN_NAMESPACE
// Class forward declaration required for QDoc bug
class QString;
+
+#if defined(Q_QDOC)
+namespace QtAudio
+#else
namespace QAudio
+#endif
{
enum Error { NoError, OpenError, IOError, UnderrunError, FatalError };
enum State { ActiveState, SuspendedState, StoppedState, IdleState };
@@ -66,10 +38,14 @@ namespace QAudio
Q_MULTIMEDIA_EXPORT float convertVolume(float volume, VolumeScale from, VolumeScale to);
}
+#if !defined(Q_QDOC)
+namespace QtAudio = QAudio;
+#endif
+
#ifndef QT_NO_DEBUG_STREAM
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::Error error);
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::State state);
-Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QAudio::VolumeScale role);
+Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QtAudio::Error error);
+Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QtAudio::State state);
+Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug dbg, QtAudio::VolumeScale role);
#endif
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiobuffer.cpp b/src/multimedia/audio/qaudiobuffer.cpp
index f839dc68b..69adcc5b7 100644
--- a/src/multimedia/audio/qaudiobuffer.cpp
+++ b/src/multimedia/audio/qaudiobuffer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaudiobuffer.h"
@@ -48,9 +12,7 @@ class QAudioBufferPrivate : public QSharedData
{
public:
QAudioBufferPrivate(const QAudioFormat &f, const QByteArray &d, qint64 start)
- : format(f),
- data(d),
- startTime(start)
+ : format(f), data(d), startTime(start)
{
}
@@ -71,11 +33,12 @@ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QAudioBufferPrivate);
\inmodule QtMultimedia
\ingroup multimedia
\ingroup multimedia_audio
- \brief The QAudioBuffer class represents a collection of audio samples with a specific format and sample rate.
+ \brief The QAudioBuffer class represents a collection of audio samples with a specific format
+ and sample rate.
- QAudioBuffer is used by the QAudioDecoder class to hand decoded audio data over to the application. An audio buffer
- contains data in a certain QAudioFormat that can be queried using format(). It is also tagged with timing and duration
- information.
+ QAudioBuffer is used by the QAudioDecoder class to hand decoded audio data over to the
+ application. An audio buffer contains data in a certain QAudioFormat that can be queried using
+ format(). It is also tagged with timing and duration information.
To access the data stored inside the buffer, use the data() or constData() methods.
@@ -140,6 +103,12 @@ QAudioBuffer::QAudioBuffer(int numFrames, const QAudioFormat &format, qint64 sta
*/
/*!
+ \fn void QAudioBuffer::swap(QAudioBuffer &other) noexcept
+
+ Swaps the audio buffer with \a other.
+*/
+
+/*!
\fn QAudioBuffer &QAudioBuffer::operator=(QAudioBuffer &&other)
Moves \a other into this QAudioBuffer.
@@ -148,7 +117,7 @@ QAudioBuffer::QAudioBuffer(int numFrames, const QAudioFormat &format, qint64 sta
/*!
Assigns the \a other buffer to this.
*/
-QAudioBuffer &QAudioBuffer::operator =(const QAudioBuffer &other) = default;
+QAudioBuffer &QAudioBuffer::operator=(const QAudioBuffer &other) = default;
/*!
Destroys this audio buffer.
@@ -245,15 +214,15 @@ qint64 QAudioBuffer::startTime() const noexcept
}
/*!
+ \fn template <typename T> const T* QAudioBuffer::constData() const
+
Returns a pointer to this buffer's data. You can only read it.
This method is preferred over the const version of \l data() to
prevent unnecessary copying.
- There is also a templatized version of this constData() function that
- allows you to retrieve a specific type of read-only pointer to
- the data. Note that there is no checking done on the format of
- the audio buffer - this is simply a convenience function.
+ Note that there is no checking done on the format of the audio
+ buffer - this is simply a convenience function.
\code
// With a 16bit sample buffer:
@@ -261,7 +230,8 @@ qint64 QAudioBuffer::startTime() const noexcept
\endcode
*/
-const void* QAudioBuffer::constData() const noexcept
+
+const void *QAudioBuffer::constData() const noexcept
{
if (!d)
return nullptr;
@@ -269,14 +239,14 @@ const void* QAudioBuffer::constData() const noexcept
}
/*!
+ \fn template <typename T> const T* QAudioBuffer::data() const
+
Returns a pointer to this buffer's data. You can only read it.
You should use the \l constData() function rather than this
to prevent accidental deep copying.
- There is also a templatized version of this data() function that
- allows you to retrieve a specific type of read-only pointer to
- the data. Note that there is no checking done on the format of
+ Note that there is no checking done on the format of
the audio buffer - this is simply a convenience function.
\code
@@ -284,36 +254,32 @@ const void* QAudioBuffer::constData() const noexcept
const quint16 *data = buffer->data<quint16>();
\endcode
*/
-const void* QAudioBuffer::data() const noexcept
+
+const void *QAudioBuffer::data() const noexcept
{
if (!d)
return nullptr;
return d->data.constData();
}
-
-/*
- Template data/constData functions caused override problems with qdoc,
- so moved their docs into the non template versions.
-*/
-
/*!
+ \fn template <typename T> T* QAudioBuffer::data()
+
Returns a pointer to this buffer's data. You can modify the
data through the returned pointer.
Since QAudioBuffer objects are explicitly shared, you should usually
call detach() before modifying the data through this function.
- There is also a templatized version of data() allows you to retrieve
- a specific type of pointer to the data. Note that there is no
- checking done on the format of the audio buffer - this is
- simply a convenience function.
+ Note that there is no checking done on the format of the audio
+ buffer - this is simply a convenience function.
\code
// With a 16bit sample buffer:
quint16 *data = buffer->data<quint16>(); // May cause deep copy
\endcode
*/
+
void *QAudioBuffer::data()
{
if (!d)
@@ -328,4 +294,33 @@ void *QAudioBuffer::data()
channel is a \e {signed short}.
*/
+/*!
+ \typedef QAudioBuffer::U8M
+
+ This is a predefined specialization for an unsigned 8 bit mono sample.
+*/
+/*!
+ \typedef QAudioBuffer::S16M
+ This is a predefined specialization for a signed 16 bit mono sample.
+i*/
+/*!
+ \typedef QAudioBuffer::S32M
+ This is a predefined specialization for a signed 32 bit mono sample.
+*/
+/*!
+ \typedef QAudioBuffer::F32M
+ This is a predefined specialization for a 32 bit float mono sample.
+*/
+/*!
+ \typedef QAudioBuffer::U8S
+ This is a predifined specialization for an unsiged 8 bit stereo sample.
+*/
+/*!
+ \typedef QAudioBuffer::S32S
+ This is a predifined specialization for a siged 32 bit stereo sample.
+*/
+/*!
+ \typedef QAudioBuffer::F32S
+ This is a predifined specialization for a 32 bit float stereo sample.
+*/
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiobuffer.h b/src/multimedia/audio/qaudiobuffer.h
index 624c8aabe..822619f31 100644
--- a/src/multimedia/audio/qaudiobuffer.h
+++ b/src/multimedia/audio/qaudiobuffer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOBUFFER_H
#define QAUDIOBUFFER_H
@@ -44,7 +8,7 @@
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qtaudio.h>
#include <QtMultimedia/qaudioformat.h>
QT_BEGIN_NAMESPACE
diff --git a/src/multimedia/audio/qaudiodecoder.cpp b/src/multimedia/audio/qaudiodecoder.cpp
index e0587d31a..f555f46ed 100644
--- a/src/multimedia/audio/qaudiodecoder.cpp
+++ b/src/multimedia/audio/qaudiodecoder.cpp
@@ -1,56 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qtmultimediaglobal_p.h"
-#include "qaudiodecoder.h"
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "private/qplatformaudiodecoder_p.h"
+#include "qaudiodecoder.h"
+#include <private/qaudiodecoder_p.h>
+#include <private/qmultimediautils_p.h>
+#include <private/qplatformaudiodecoder_p.h>
#include <private/qplatformmediaintegration_p.h>
-#include <private/qobject_p.h>
#include <QtCore/qcoreevent.h>
+#include <QtCore/qdebug.h>
#include <QtCore/qmetaobject.h>
+#include <QtCore/qpointer.h>
#include <QtCore/qtimer.h>
#include <QtCore/qurl.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -74,12 +37,19 @@ QT_BEGIN_NAMESPACE
/*!
Construct an QAudioDecoder instance with \a parent.
*/
-QAudioDecoder::QAudioDecoder(QObject *parent)
- : QObject(parent)
+QAudioDecoder::QAudioDecoder(QObject *parent) : QObject{ *new QAudioDecoderPrivate, parent }
{
- decoder = QPlatformMediaIntegration::instance()->createAudioDecoder(this);
-}
+ QT6_ONLY(Q_UNUSED(unused))
+
+ Q_D(QAudioDecoder);
+ auto maybeDecoder = QPlatformMediaIntegration::instance()->createAudioDecoder(this);
+ if (maybeDecoder) {
+ d->decoder.reset(maybeDecoder.value());
+ } else {
+ qWarning() << "Failed to initialize QAudioDecoder" << maybeDecoder.error();
+ }
+}
/*!
Destroys the audio decoder object.
@@ -91,7 +61,9 @@ QAudioDecoder::~QAudioDecoder() = default;
*/
bool QAudioDecoder::isSupported() const
{
- return decoder != nullptr;
+ Q_D(const QAudioDecoder);
+
+ return bool(d->decoder);
}
/*!
@@ -100,29 +72,33 @@ bool QAudioDecoder::isSupported() const
*/
bool QAudioDecoder::isDecoding() const
{
- return decoder && decoder->isDecoding();
+ Q_D(const QAudioDecoder);
+
+ return d->decoder && d->decoder->isDecoding();
}
/*!
- \property QAudioDecoder::error
- \brief The current error state.
+
+ Returns the current error state of the QAudioDecoder.
*/
QAudioDecoder::Error QAudioDecoder::error() const
{
- if (!decoder)
- return NotSupportedError;
- return decoder->error();
+ Q_D(const QAudioDecoder);
+ return d->decoder ? d->decoder->error() : NotSupportedError;
}
/*!
+ \property QAudioDecoder::error
+
Returns a human readable description of the current error, or
an empty string is there is no error.
*/
QString QAudioDecoder::errorString() const
{
- if (!decoder)
+ Q_D(const QAudioDecoder);
+ if (!d->decoder)
return tr("QAudioDecoder not supported.");
- return decoder->errorString();
+ return d->decoder->errorString();
}
/*!
@@ -139,13 +115,14 @@ QString QAudioDecoder::errorString() const
*/
void QAudioDecoder::start()
{
- if (decoder == nullptr)
+ Q_D(QAudioDecoder);
+
+ if (!d->decoder)
return;
// Reset error conditions
- decoder->clearError();
-
- decoder->start();
+ d->decoder->clearError();
+ d->decoder->start();
}
/*!
@@ -153,10 +130,10 @@ void QAudioDecoder::start()
*/
void QAudioDecoder::stop()
{
- if (!decoder)
- return;
+ Q_D(QAudioDecoder);
- decoder->stop();
+ if (d->decoder)
+ d->decoder->stop();
}
/*!
@@ -166,9 +143,8 @@ void QAudioDecoder::stop()
*/
QUrl QAudioDecoder::source() const
{
- if (decoder)
- return decoder->source();
- return QString();
+ Q_D(const QAudioDecoder);
+ return d->unresolvedUrl;
}
/*!
@@ -182,11 +158,16 @@ QUrl QAudioDecoder::source() const
*/
void QAudioDecoder::setSource(const QUrl &fileName)
{
- if (!decoder)
+ Q_D(QAudioDecoder);
+
+ if (!d->decoder)
return;
- decoder->clearError();
- decoder->setSource(fileName);
+ d->decoder->clearError();
+ d->unresolvedUrl = fileName;
+ d->decoder->setSourceDevice(nullptr);
+ QUrl url = qMediaFromUserInput(fileName);
+ d->decoder->setSource(url);
}
/*!
@@ -195,9 +176,8 @@ void QAudioDecoder::setSource(const QUrl &fileName)
*/
QIODevice *QAudioDecoder::sourceDevice() const
{
- if (decoder)
- return decoder->sourceDevice();
- return nullptr;
+ Q_D(const QAudioDecoder);
+ return d->decoder ? d->decoder->sourceDevice() : nullptr;
}
/*!
@@ -211,9 +191,11 @@ QIODevice *QAudioDecoder::sourceDevice() const
*/
void QAudioDecoder::setSourceDevice(QIODevice *device)
{
- if (!decoder)
- return;
- decoder->setSourceDevice(device);
+ Q_D(QAudioDecoder);
+ if (d->decoder) {
+ d->unresolvedUrl = QUrl{};
+ d->decoder->setSourceDevice(device);
+ }
}
/*!
@@ -226,9 +208,8 @@ void QAudioDecoder::setSourceDevice(QIODevice *device)
*/
QAudioFormat QAudioDecoder::audioFormat() const
{
- if (decoder)
- return decoder->audioFormat();
- return QAudioFormat();
+ Q_D(const QAudioDecoder);
+ return d->decoder ? d->decoder->audioFormat() : QAudioFormat{};
}
/*!
@@ -248,15 +229,18 @@ QAudioFormat QAudioDecoder::audioFormat() const
audio file, you can specify an invalid \a format.
\warning Setting a desired audio format is not yet supported
- on Android.
+ on the Android backend. It does work with the default FFMPEG
+ backend.
*/
void QAudioDecoder::setAudioFormat(const QAudioFormat &format)
{
if (isDecoding())
return;
- if (decoder != nullptr)
- decoder->setAudioFormat(format);
+ Q_D(QAudioDecoder);
+
+ if (d->decoder)
+ d->decoder->setAudioFormat(format);
}
/*!
@@ -266,9 +250,8 @@ void QAudioDecoder::setAudioFormat(const QAudioFormat &format)
*/
bool QAudioDecoder::bufferAvailable() const
{
- if (decoder)
- return decoder->bufferAvailable();
- return false;
+ Q_D(const QAudioDecoder);
+ return d->decoder && d->decoder->bufferAvailable();
}
/*!
@@ -278,9 +261,8 @@ bool QAudioDecoder::bufferAvailable() const
qint64 QAudioDecoder::position() const
{
- if (decoder)
- return decoder->position();
- return -1;
+ Q_D(const QAudioDecoder);
+ return d->decoder ? d->decoder->position() : -1;
}
/*!
@@ -290,9 +272,8 @@ qint64 QAudioDecoder::position() const
qint64 QAudioDecoder::duration() const
{
- if (decoder)
- return decoder->duration();
- return -1;
+ Q_D(const QAudioDecoder);
+ return d->decoder ? d->decoder->duration() : -1;
}
/*!
@@ -307,10 +288,8 @@ qint64 QAudioDecoder::duration() const
QAudioBuffer QAudioDecoder::read() const
{
- if (decoder)
- return decoder->read();
-
- return QAudioBuffer();
+ Q_D(const QAudioDecoder);
+ return d->decoder ? d->decoder->read() : QAudioBuffer{};
}
// Enums
@@ -328,7 +307,7 @@ QAudioBuffer QAudioDecoder::read() const
// Signals
/*!
- \fn QAudioDecoder::error(QAudioDecoder::Error error)
+ \fn void QAudioDecoder::error(QAudioDecoder::Error error)
Signals that an \a error condition has occurred.
@@ -394,7 +373,6 @@ QAudioBuffer QAudioDecoder::read() const
\sa positionChanged()
*/
-
// Properties
/*!
\property QAudioDecoder::source
diff --git a/src/multimedia/audio/qaudiodecoder.h b/src/multimedia/audio/qaudiodecoder.h
index 451c3a657..9044b6617 100644
--- a/src/multimedia/audio/qaudiodecoder.h
+++ b/src/multimedia/audio/qaudiodecoder.h
@@ -1,53 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIODECODER_H
#define QAUDIODECODER_H
#include <QtCore/qobject.h>
#include <QtMultimedia/qmediaenumdebug.h>
-
#include <QtMultimedia/qaudiobuffer.h>
QT_BEGIN_NAMESPACE
-class QPlatformAudioDecoder;
+class QAudioDecoderPrivate;
class Q_MULTIMEDIA_EXPORT QAudioDecoder : public QObject
{
Q_OBJECT
@@ -112,7 +75,10 @@ Q_SIGNALS:
private:
Q_DISABLE_COPY(QAudioDecoder)
- QPlatformAudioDecoder *decoder;
+ Q_DECLARE_PRIVATE(QAudioDecoder)
+
+ // ### Qt7: remove unused member
+ QT6_ONLY(void *unused = nullptr;) // for ABI compatibility
};
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiodecoder_p.h b/src/multimedia/audio/qaudiodecoder_p.h
new file mode 100644
index 000000000..fa7311457
--- /dev/null
+++ b/src/multimedia/audio/qaudiodecoder_p.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIODECODER_P_H
+#define QAUDIODECODER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qobject_p.h>
+#include <QtMultimedia/private/qplatformaudiodecoder_p.h>
+
+#include <memory.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioDecoder;
+
+class QAudioDecoderPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAudioDecoder)
+
+public:
+ QAudioDecoderPrivate() = default;
+
+ QUrl unresolvedUrl;
+ std::unique_ptr<QPlatformAudioDecoder> decoder;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIODECODER_P_H
diff --git a/src/multimedia/audio/qaudiodevice.cpp b/src/multimedia/audio/qaudiodevice.cpp
index 5d06cdbe0..4b1e182cb 100644
--- a/src/multimedia/audio/qaudiodevice.cpp
+++ b/src/multimedia/audio/qaudiodevice.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaudiosystem_p.h"
#include "qaudiodevice_p.h"
@@ -52,12 +16,14 @@ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QAudioDevicePrivate);
/*!
\class QAudioDevice
- \brief The QAudioDevice class provides an information about audio devices and their functionality.
+ \brief The QAudioDevice class provides an information about audio devices and their
+ functionality.
\inmodule QtMultimedia
\ingroup multimedia
\ingroup multimedia_audio
- QAudioDevice describes an audio device available in the system, either for input or for playback.
+ QAudioDevice describes an audio device available in the system, either for input or for
+ playback.
A QAudioDevice is used by Qt to construct
classes that communicate with the device -- such as
@@ -87,7 +53,7 @@ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QAudioDevicePrivate);
sound, i.e., play an audio stream in a supported format. For each device we
find, we simply print the deviceName().
- \sa QAudioSink, QAudioSource QAudioFormat
+ \sa QAudioSink, QAudioSource, QAudioFormat
*/
/*!
@@ -127,14 +93,18 @@ QAudioDevice::QAudioDevice() = default;
/*!
Constructs a copy of \a other.
*/
-QAudioDevice::QAudioDevice(const QAudioDevice& other) = default;
+QAudioDevice::QAudioDevice(const QAudioDevice &other) = default;
/*!
\fn QAudioDevice::QAudioDevice(QAudioDevice &&other)
Move constructs from \a other.
*/
+/*!
+ \fn void QAudioDevice::swap(QAudioDevice &other) noexcept
+ Swaps the audio device with the \a other.
+*/
/*!
Destroy this audio device info.
*/
@@ -143,7 +113,7 @@ QAudioDevice::~QAudioDevice() = default;
/*!
Sets the QAudioDevice object to be equal to \a other.
*/
-QAudioDevice& QAudioDevice::operator=(const QAudioDevice &other) = default;
+QAudioDevice &QAudioDevice::operator=(const QAudioDevice &other) = default;
/*!
\fn QAudioDevice& QAudioDevice::operator=(QAudioDevice &&other)
@@ -155,13 +125,13 @@ QAudioDevice& QAudioDevice::operator=(const QAudioDevice &other) = default;
Returns true if this QAudioDevice class represents the
same audio device as \a other.
*/
-bool QAudioDevice::operator ==(const QAudioDevice &other) const
+bool QAudioDevice::operator==(const QAudioDevice &other) const
{
if (d == other.d)
return true;
if (!d || !other.d)
return false;
- if (d->mode == other.d->mode && d->id == other.d->id)
+ if (d->mode == other.d->mode && d->id == other.d->id && d->isDefault == other.d->isDefault)
return true;
return false;
}
@@ -170,7 +140,7 @@ bool QAudioDevice::operator ==(const QAudioDevice &other) const
Returns true if this QAudioDevice class represents a
different audio device than \a other
*/
-bool QAudioDevice::operator !=(const QAudioDevice &other) const
+bool QAudioDevice::operator!=(const QAudioDevice &other) const
{
return !operator==(other);
}
@@ -194,6 +164,8 @@ bool QAudioDevice::isNull() const
*/
/*!
+ \property QAudioDevice::id
+
Returns an identifier for the audio device.
Device names vary depending on the platform/audio plugin being used.
@@ -214,6 +186,8 @@ QByteArray QAudioDevice::id() const
*/
/*!
+ \property QAudioDevice::description
+
Returns a human readable name of the audio device.
Use this string to present the device to the user.
@@ -230,6 +204,8 @@ QString QAudioDevice::description() const
*/
/*!
+ \property QAudioDevice::isDefault
+
Returns true if this is the default audio device.
*/
bool QAudioDevice::isDefault() const
@@ -245,9 +221,11 @@ bool QAudioDevice::isFormatSupported(const QAudioFormat &settings) const
{
if (isNull())
return false;
- if (settings.sampleRate() < d->minimumSampleRate || settings.sampleRate() > d->maximumSampleRate)
+ if (settings.sampleRate() < d->minimumSampleRate
+ || settings.sampleRate() > d->maximumSampleRate)
return false;
- if (settings.channelCount() < d->minimumChannelCount || settings.channelCount() > d->maximumChannelCount)
+ if (settings.channelCount() < d->minimumChannelCount
+ || settings.channelCount() > d->maximumChannelCount)
return false;
if (!d->supportedSampleFormats.contains(settings.sampleFormat()))
return false;
@@ -259,7 +237,7 @@ bool QAudioDevice::isFormatSupported(const QAudioFormat &settings) const
These settings are provided by the platform/audio plugin being used.
- They are also dependent on the \l {QAudio}::Mode being used.
+ They are also dependent on the \l {QtAudio}::Mode being used.
A typical audio system would provide something like:
\list
@@ -325,16 +303,18 @@ QAudioFormat::ChannelConfig QAudioDevice::channelConfiguration() const
}
/*!
+ \fn QAudioDevicePrivate QAudioDevice::handle() const
\internal
*/
-QAudioDevice::QAudioDevice(QAudioDevicePrivate *p)
- : d(p)
-{}
+/*!
+ \internal
+*/
+QAudioDevice::QAudioDevice(QAudioDevicePrivate *p) : d(p) { }
/*!
\enum QAudioDevice::Mode
- Describes the mode of a QAudioDevice
+ Describes the mode of this device.
\value Null
A null device.
@@ -359,7 +339,9 @@ QAudioDevice::QAudioDevice(QAudioDevicePrivate *p)
*/
/*!
- returns whether this device is an input or output device.
+ \property QAudioDevice::mode
+
+ Returns whether this device is an input or output device.
*/
QAudioDevice::Mode QAudioDevice::mode() const
{
@@ -387,3 +369,5 @@ QDebug operator<<(QDebug dbg, QAudioDevice::Mode mode)
#endif
QT_END_NAMESPACE
+
+#include "moc_qaudiodevice.cpp"
diff --git a/src/multimedia/audio/qaudiodevice.h b/src/multimedia/audio/qaudiodevice.h
index 3098c6df1..abd1b654c 100644
--- a/src/multimedia/audio/qaudiodevice.h
+++ b/src/multimedia/audio/qaudiodevice.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIODEVICEINFO_H
@@ -49,7 +13,7 @@
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qtaudio.h>
#include <QtMultimedia/qaudioformat.h>
QT_BEGIN_NAMESPACE
diff --git a/src/multimedia/audio/qaudiodevice_p.h b/src/multimedia/audio/qaudiodevice_p.h
index 3348e89e4..c59856d72 100644
--- a/src/multimedia/audio/qaudiodevice_p.h
+++ b/src/multimedia/audio/qaudiodevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIODEVICEINFO_P_H
@@ -65,7 +29,7 @@ public:
mode(m)
{}
virtual ~QAudioDevicePrivate();
- QByteArray id;
+ QByteArray id;
QAudioDevice::Mode mode = QAudioDevice::Output;
bool isDefault = false;
@@ -76,7 +40,19 @@ public:
int minimumChannelCount = 0;
int maximumChannelCount = 0;
QList<QAudioFormat::SampleFormat> supportedSampleFormats;
- QAudioFormat::ChannelConfig channelConfiguration;
+ QAudioFormat::ChannelConfig channelConfiguration = QAudioFormat::ChannelConfigUnknown;
+
+ bool operator == (const QAudioDevicePrivate &other) const
+ {
+ return id == other.id && mode == other.mode && isDefault == other.isDefault
+ && preferredFormat == other.preferredFormat && description == other.description
+ && minimumSampleRate == other.minimumSampleRate
+ && maximumSampleRate == other.maximumSampleRate
+ && minimumChannelCount == other.minimumChannelCount
+ && maximumChannelCount == other.maximumChannelCount
+ && supportedSampleFormats == other.supportedSampleFormats
+ && channelConfiguration == other.channelConfiguration;
+ }
QAudioDevice create() { return QAudioDevice(this); }
};
diff --git a/src/multimedia/audio/qaudioformat.cpp b/src/multimedia/audio/qaudioformat.cpp
index ecc1de1ff..9f51759b5 100644
--- a/src/multimedia/audio/qaudioformat.cpp
+++ b/src/multimedia/audio/qaudioformat.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QDebug>
#include <qaudioformat.h>
#include <qalgorithms.h>
@@ -117,9 +81,15 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QAudioFormat& QAudioFormat::operator=(const QAudioFormat &other)
+ \fn bool QAudioFormat::operator==(const QAudioFormat &a, const QAudioFormat &b)
- Assigns \a other to this QAudioFormat implementation.
+ Returns \c true if audio format \a a is equal to \a b, otherwise returns \c false.
+*/
+
+/*!
+ \fn bool QAudioFormat::operator!=(const QAudioFormat &a, const QAudioFormat &b)
+
+ Returns \c true if audio format \a a is not equal to \a b, otherwise returns \c false.
*/
/*!
@@ -172,7 +142,10 @@ QT_BEGIN_NAMESPACE
\value BottomFrontLeft
\value BottomFrontRight
*/
-
+/*!
+ \variable QAudioFormat::NChannelPositions
+ \internal
+*/
/*!
\enum QAudioFormat::ChannelConfig
@@ -182,13 +155,22 @@ QT_BEGIN_NAMESPACE
and 7.1 surround configurations.
\value ChannelConfigUnknown The channel configuration is not known.
- \value ChannelConfigMono The audio has one Center channel
- \value ChannelConfigStereo The audio has two channels, Left and Right
- \value ChannelConfig2Dot1 The audio has three channels, Left, Right and LFE (low frequency effect)
- \value ChannelConfigSurround5Dot0 The audio has five channels, Left, Right, Center, BackLeft, BackRight
- \value ChannelConfigSurround5Dot1 The audio has 6 channels, Left, Right, Center, LFE, BackLeft and BackRight
- \value ChannelConfigSurround7Dot0 The audio has 7 channels, Left, Right, Center, BackLeft, BackRight, SideLeft and SideRight
- \value ChannelConfigSurround7Dot1 The audio has 8 channels, Left, Right, Center, LFE, BackLeft, BackRight, SideLeft and SideRight
+ \value ChannelConfigMono The audio has one Center channel.
+ \value ChannelConfigStereo The audio has two channels, Left and Right.
+ \value ChannelConfig2Dot1 The audio has three channels, Left, Right and
+ LFE (low frequency effect).
+ \value ChannelConfig3Dot0 The audio has three channels, Left, Right, and
+ Center.
+ \value ChannelConfig3Dot1 The audio has four channels, Left, Right, Center,
+ and LFE (low frequency effect).
+ \value ChannelConfigSurround5Dot0 The audio has five channels, Left, Right,
+ Center, BackLeft, and BackRight.
+ \value ChannelConfigSurround5Dot1 The audio has 6 channels, Left, Right,
+ Center, LFE, BackLeft, and BackRight.
+ \value ChannelConfigSurround7Dot0 The audio has 7 channels, Left, Right,
+ Center, BackLeft, BackRight, SideLeft, and SideRight.
+ \value ChannelConfigSurround7Dot1 The audio has 8 channels, Left, Right,
+ Center, LFE, BackLeft, BackRight, SideLeft, and SideRight.
*/
/*!
@@ -228,7 +210,11 @@ int QAudioFormat::channelOffset(AudioChannelPosition channel) const noexcept
config to ChannelConfigUnknown.
*/
+/*!
+ \fn template <typename... Args> QAudioFormat::ChannelConfig QAudioFormat::channelConfig(Args... channels)
+ Returns the current channel configuration for the given \a channels.
+*/
/*!
\fn QAudioFormat::ChannelConfig QAudioFormat::channelConfig() const noexcept
@@ -374,11 +360,15 @@ float QAudioFormat::normalizedSampleValue(const void *sample) const
{
switch (m_sampleFormat) {
case UInt8:
- return ((float)*reinterpret_cast<const quint8 *>(sample))/(float)std::numeric_limits<qint8>::max() - 1.;
+ return ((float)*reinterpret_cast<const quint8 *>(sample))
+ / (float)std::numeric_limits<qint8>::max()
+ - 1.;
case Int16:
- return ((float)*reinterpret_cast<const qint16 *>(sample))/(float)std::numeric_limits<qint16>::max();
+ return ((float)*reinterpret_cast<const qint16 *>(sample))
+ / (float)std::numeric_limits<qint16>::max();
case Int32:
- return ((float)*reinterpret_cast<const qint32 *>(sample))/(float)std::numeric_limits<qint32>::max();
+ return ((float)*reinterpret_cast<const qint32 *>(sample))
+ / (float)std::numeric_limits<qint32>::max();
case Float:
return *reinterpret_cast<const float *>(sample);
case Unknown:
@@ -401,6 +391,9 @@ QAudioFormat::ChannelConfig QAudioFormat::defaultChannelConfigForChannelCount(in
{
QAudioFormat::ChannelConfig config;
switch (channelCount) {
+ case 0:
+ config = QAudioFormat::ChannelConfigUnknown;
+ break;
case 1:
config = QAudioFormat::ChannelConfigMono;
break;
@@ -442,7 +435,7 @@ QAudioFormat::ChannelConfig QAudioFormat::defaultChannelConfigForChannelCount(in
QAudioBuffer.
\value Unknown Not Set
- \value UInt8 Samples are unsigned 8 bit signed integers
+ \value UInt8 Samples are 8 bit unsigned integers
\value Int16 Samples are 16 bit signed integers
\value Int32 Samples are 32 bit signed integers
\value Float Samples are floats
@@ -476,12 +469,10 @@ QDebug operator<<(QDebug dbg, QAudioFormat::SampleFormat type)
QDebug operator<<(QDebug dbg, const QAudioFormat &f)
{
- dbg << "QAudioFormat(" << f.sampleRate() << "Hz, " << f.channelCount() << "Channels, " << f.sampleFormat() << "Format )";
+ dbg << "QAudioFormat(" << f.sampleRate() << "Hz, " << f.channelCount() << "Channels, "
+ << f.sampleFormat() << "Format )";
return dbg;
}
#endif
-
-
QT_END_NAMESPACE
-
diff --git a/src/multimedia/audio/qaudioformat.h b/src/multimedia/audio/qaudioformat.h
index b4cbb6428..a04ab6e1a 100644
--- a/src/multimedia/audio/qaudioformat.h
+++ b/src/multimedia/audio/qaudioformat.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOFORMAT_H
@@ -104,6 +68,8 @@ public:
ChannelConfigMono = QtPrivate::channelConfig(FrontCenter),
ChannelConfigStereo = QtPrivate::channelConfig(FrontLeft, FrontRight),
ChannelConfig2Dot1 = QtPrivate::channelConfig(FrontLeft, FrontRight, LFE),
+ ChannelConfig3Dot0 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter),
+ ChannelConfig3Dot1 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, LFE),
ChannelConfigSurround5Dot0 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, BackLeft, BackRight),
ChannelConfigSurround5Dot1 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, LFE, BackLeft, BackRight),
ChannelConfigSurround7Dot0 = QtPrivate::channelConfig(FrontLeft, FrontRight, FrontCenter, BackLeft, BackRight, SideLeft, SideRight),
diff --git a/src/multimedia/audio/qaudiohelpers.cpp b/src/multimedia/audio/qaudiohelpers.cpp
index 1e7c4ac81..c2d8681c6 100644
--- a/src/multimedia/audio/qaudiohelpers.cpp
+++ b/src/multimedia/audio/qaudiohelpers.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaudiohelpers_p.h"
@@ -60,7 +24,7 @@ template<class T> struct signedVersion {};
template<> struct signedVersion<quint8>
{
using TS = qint8;
- enum {offset = 0x80};
+ static constexpr int offset = 0x80;
};
template<class T> void adjustUnsignedSamples(qreal factor, const void *src, void *dst, int samples)
diff --git a/src/multimedia/audio/qaudiohelpers_p.h b/src/multimedia/audio/qaudiohelpers_p.h
index 04896a2c3..81e6c6382 100644
--- a/src/multimedia/audio/qaudiohelpers_p.h
+++ b/src/multimedia/audio/qaudiohelpers_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOHELPERS_H
#define QAUDIOHELPERS_H
diff --git a/src/multimedia/audio/qaudioinput.cpp b/src/multimedia/audio/qaudioinput.cpp
index 8472ceb6c..dc7d7335f 100644
--- a/src/multimedia/audio/qaudioinput.cpp
+++ b/src/multimedia/audio/qaudioinput.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qaudioinput.h>
#include <qaudiodevice.h>
@@ -69,10 +33,10 @@
}
\endqml
- You can use AudioInput together with a QtMultiMedia::CaptureSession to capture audio from an audio
- input device.
+ You can use AudioInput together with a QtMultiMedia::CaptureSession to capture audio from an
+ audio input device.
- \sa Camera AudioOutput
+ \sa Camera, AudioOutput
*/
/*!
@@ -87,16 +51,22 @@
to be used, muting the channel, and changing the channel's volume.
*/
-QAudioInput::QAudioInput(QObject *parent)
- : QAudioInput(QMediaDevices::defaultAudioInput(), parent)
-{}
+QAudioInput::QAudioInput(QObject *parent) : QAudioInput(QMediaDevices::defaultAudioInput(), parent)
+{
+}
QAudioInput::QAudioInput(const QAudioDevice &device, QObject *parent)
- : QObject(parent),
- d(QPlatformMediaIntegration::instance()->createAudioInput(this))
+ : QObject(parent)
{
- d->device = device.mode() == QAudioDevice::Input ? device : QMediaDevices::defaultAudioInput();
- d->setAudioDevice(d->device);
+ auto maybeAudioInput = QPlatformMediaIntegration::instance()->createAudioInput(this);
+ if (maybeAudioInput) {
+ d = maybeAudioInput.value();
+ d->device = device.mode() == QAudioDevice::Input ? device : QMediaDevices::defaultAudioInput();
+ d->setAudioDevice(d->device);
+ } else {
+ d = new QPlatformAudioInput(nullptr);
+ qWarning() << "Failed to initialize QAudioInput" << maybeAudioInput.error();
+ }
}
QAudioInput::~QAudioInput()
@@ -116,7 +86,12 @@ QAudioInput::~QAudioInput()
UI volume controls should usually be scaled non-linearly. For example,
using a logarithmic scale will produce linear changes in perceived loudness,
which is what a user would normally expect from a volume control.
- \sa QAudio::convertVolume()
+ \sa QtAudio::convertVolume()
+*/
+/*!
+ \property QAudioInput::volume
+
+ The property returns the volume of the audio input.
*/
float QAudioInput::volume() const
{
diff --git a/src/multimedia/audio/qaudioinput.h b/src/multimedia/audio/qaudioinput.h
index 95e337fe2..6f4f6b099 100644
--- a/src/multimedia/audio/qaudioinput.h
+++ b/src/multimedia/audio/qaudioinput.h
@@ -1,48 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOINPUTDEVICE_H
#define QAUDIOINPUTDEVICE_H
#include <QtCore/qobject.h>
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qtaudio.h>
#include <functional>
diff --git a/src/multimedia/audio/qaudiooutput.cpp b/src/multimedia/audio/qaudiooutput.cpp
index 209ed63ca..3bb52a4d2 100644
--- a/src/multimedia/audio/qaudiooutput.cpp
+++ b/src/multimedia/audio/qaudiooutput.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qaudiooutput.h>
#include <qaudiodevice.h>
@@ -67,10 +31,11 @@
}
\endqml
- You can use AudioOutput together with a QtMultiMedia::MediaPlayer to play audio content, or you can use it
- in conjunction with a MultiMedia::CaptureSession to monitor the audio processed by the capture session.
+ You can use AudioOutput together with a QtMultiMedia::MediaPlayer to play audio content, or you
+ can use it in conjunction with a MultiMedia::CaptureSession to monitor the audio processed by the
+ capture session.
- \sa VideoOutput AudioInput
+ \sa VideoOutput, AudioInput
*/
/*!
@@ -88,14 +53,21 @@
*/
QAudioOutput::QAudioOutput(QObject *parent)
: QAudioOutput(QMediaDevices::defaultAudioOutput(), parent)
-{}
+{
+}
QAudioOutput::QAudioOutput(const QAudioDevice &device, QObject *parent)
- : QObject(parent),
- d(QPlatformMediaIntegration::instance()->createAudioOutput(this))
+ : QObject(parent)
{
- d->device = device.mode() == QAudioDevice::Output ? device : QMediaDevices::defaultAudioOutput();
- d->setAudioDevice(d->device);
+ auto maybeAudioOutput = QPlatformMediaIntegration::instance()->createAudioOutput(this);
+ if (maybeAudioOutput) {
+ d = maybeAudioOutput.value();
+ d->device = device.mode() == QAudioDevice::Output ? device : QMediaDevices::defaultAudioOutput();
+ d->setAudioDevice(d->device);
+ } else {
+ d = new QPlatformAudioOutput(nullptr);
+ qWarning() << "Failed to initialize QAudioOutput" << maybeAudioOutput.error();
+ }
}
QAudioOutput::~QAudioOutput()
@@ -104,7 +76,6 @@ QAudioOutput::~QAudioOutput()
delete d;
}
-
/*!
\qmlproperty real QtMultimedia::AudioOutput::volume
@@ -120,7 +91,7 @@ QAudioOutput::~QAudioOutput()
using a logarithmic scale will produce linear changes in perceived \l{loudness},
which is what a user would normally expect from a volume control.
- See \l {QAudio::convertVolume()}{QtMultimedia.convertVolume()}
+ See \l {QtAudio::convertVolume()}{QtMultimedia.convertVolume()}
for more details.
*/
@@ -138,7 +109,7 @@ QAudioOutput::~QAudioOutput()
using a logarithmic scale will produce linear changes in perceived loudness,
which is what a user would normally expect from a volume control.
- \sa QAudio::convertVolume()
+ \sa QtAudio::convertVolume()
*/
float QAudioOutput::volume() const
{
@@ -235,5 +206,4 @@ void QAudioOutput::setDisconnectFunction(std::function<void()> disconnectFunctio
d->disconnectFunction = std::move(disconnectFunction);
}
-
#include "moc_qaudiooutput.cpp"
diff --git a/src/multimedia/audio/qaudiooutput.h b/src/multimedia/audio/qaudiooutput.h
index cfb3d65d2..643d19f94 100644
--- a/src/multimedia/audio/qaudiooutput.h
+++ b/src/multimedia/audio/qaudiooutput.h
@@ -1,48 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOOUTPUTDEVICE_H
#define QAUDIOOUTPUTDEVICE_H
#include <QtCore/qobject.h>
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qtaudio.h>
#include <functional>
diff --git a/src/multimedia/audio/qaudiosink.cpp b/src/multimedia/audio/qaudiosink.cpp
index 15d865996..12263d32a 100644
--- a/src/multimedia/audio/qaudiosink.cpp
+++ b/src/multimedia/audio/qaudiosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaudio.h"
@@ -81,21 +45,22 @@ QT_BEGIN_NAMESPACE
After the file has finished playing, we need to stop the device:
- \snippet multimedia-snippets/audio.cpp Audio output state changed
+ \snippet multimedia-snippets/audio.cpp Audio output stop
At any given time, the QAudioSink will be in one of four states:
active, suspended, stopped, or idle. These states are described
- by the QAudio::State enum.
+ by the QtAudio::State enum.
State changes are reported through the stateChanged() signal. You
can use this signal to, for instance, update the GUI of the
application; the mundane example here being changing the state of
a \c { play/pause } button. You request a state change directly
with suspend(), stop(), reset(), resume(), and start().
- If an error occurs, you can fetch the \l{QAudio::Error}{error
- type} with the error() function. Please see the QAudio::Error enum
- for a description of the possible errors that are reported. When
- an error is encountered, the state changes to QAudio::StoppedState.
+ If an error occurs, you can fetch the \l{QtAudio::Error}{error
+ type} with the error() function. Please see the QtAudio::Error enum
+ for a description of the possible errors that are reported. When
+ QtAudio::UnderrunError is encountered, the state changes to QtAudio::IdleState,
+ when another error is encountered, the state changes to QtAudio::StoppedState.
You can check for errors by connecting to the stateChanged()
signal:
@@ -122,14 +87,26 @@ QAudioSink::QAudioSink(const QAudioFormat &format, QObject *parent)
QAudioSink::QAudioSink(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent):
QObject(parent)
{
- d = QPlatformMediaDevices::instance()->audioOutputDevice(format, audioDevice);
+ d = QPlatformMediaIntegration::instance()->mediaDevices()->audioOutputDevice(format, audioDevice, parent);
if (d)
- connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
+ connect(d, &QPlatformAudioSink::stateChanged, this, [this](QAudio::State state) {
+ // if the signal has been emitted from another thread,
+ // the state may be already changed by main one
+ if (state == d->state())
+ emit stateChanged(state);
+ });
else
qWarning() << ("No audio device detected");
}
/*!
+ \fn bool QAudioSink::isNull() const
+
+ Returns \c true is the QAudioSink instance is \c null, otherwise returns
+ \c false.
+*/
+
+/*!
Destroys this audio output.
This will release any system resources used and free any buffers.
@@ -154,11 +131,11 @@ QAudioFormat QAudioSink::format() const
\l{QIODevice::ReadWrite}{ReadWrite} modes.
If the QAudioSink is able to successfully output audio data, state() returns
- QAudio::ActiveState, error() returns QAudio::NoError
+ QtAudio::ActiveState, error() returns QtAudio::NoError
and the stateChanged() signal is emitted.
- If a problem occurs during this process, error() returns QAudio::OpenError,
- state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
+ If a problem occurs during this process, error() returns QtAudio::OpenError,
+ state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
\sa QIODevice
*/
@@ -179,11 +156,11 @@ void QAudioSink::start(QIODevice* device)
if you start another stream.
If the QAudioSink is able to access the system's audio device, state() returns
- QAudio::IdleState, error() returns QAudio::NoError
+ QtAudio::IdleState, error() returns QtAudio::NoError
and the stateChanged() signal is emitted.
- If a problem occurs during this process, error() returns QAudio::OpenError,
- state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
+ If a problem occurs during this process, error() returns QtAudio::OpenError,
+ state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
\sa QIODevice
*/
@@ -198,7 +175,7 @@ QIODevice* QAudioSink::start()
/*!
Stops the audio output, detaching from the system resource.
- Sets error() to QAudio::NoError, state() to QAudio::StoppedState and
+ Sets error() to QtAudio::NoError, state() to QtAudio::StoppedState and
emit stateChanged() signal.
*/
void QAudioSink::stop()
@@ -220,7 +197,7 @@ void QAudioSink::reset()
/*!
Stops processing audio data, preserving buffered audio data.
- Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and
+ Sets error() to QtAudio::NoError, state() to QtAudio::SuspendedState and
emits stateChanged() signal.
*/
void QAudioSink::suspend()
@@ -232,10 +209,9 @@ void QAudioSink::suspend()
/*!
Resumes processing audio data after a suspend().
- Sets error() to QAudio::NoError.
- Sets state() to QAudio::ActiveState if you previously called start(QIODevice*).
- Sets state() to QAudio::IdleState if you previously called start().
- emits stateChanged() signal.
+ Sets state() to the state the sink had when suspend() was called, and sets
+ error() to QAudioError::NoError. This function does nothing if the audio sink's
+ state is not QtAudio::SuspendedState.
*/
void QAudioSink::resume()
{
@@ -246,7 +222,7 @@ void QAudioSink::resume()
/*!
Returns the number of free bytes available in the audio buffer.
- \note The returned value is only valid while in QAudio::ActiveState or QAudio::IdleState
+ \note The returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
state, otherwise returns zero.
*/
qsizetype QAudioSink::bytesFree() const
@@ -303,7 +279,7 @@ qint64 QAudioSink::elapsedUSecs() const
/*!
Returns the error state.
*/
-QAudio::Error QAudioSink::error() const
+QtAudio::Error QAudioSink::error() const
{
return d ? d->error() : QAudio::OpenError;
}
@@ -311,7 +287,7 @@ QAudio::Error QAudioSink::error() const
/*!
Returns the state of audio processing.
*/
-QAudio::State QAudioSink::state() const
+QtAudio::State QAudioSink::state() const
{
return d ? d->state() : QAudio::StoppedState;
}
@@ -330,7 +306,7 @@ QAudio::State QAudioSink::state() const
UI volume controls should usually be scaled non-linearly. For example, using
a logarithmic scale will produce linear changes in perceived loudness, which
is what a user would normally expect from a volume control. See
- QAudio::convertVolume() for more details.
+ QtAudio::convertVolume() for more details.
*/
void QAudioSink::setVolume(qreal volume)
{
@@ -349,9 +325,13 @@ qreal QAudioSink::volume() const
}
/*!
- \fn QAudioSink::stateChanged(QAudio::State state)
+ \fn QAudioSink::stateChanged(QtAudio::State state)
This signal is emitted when the device \a state has changed.
This is the current state of the audio output.
+
+ \note The QtAudio namespace was named QAudio up to and including Qt 6.6.
+ String-based connections to this signal have to use \c{QAudio::State} as
+ the parameter type: \c{connect(source, SIGNAL(stateChanged(QAudio::State)), ...);}
*/
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiosink.h b/src/multimedia/audio/qaudiosink.h
index 049011e98..e071e6fbc 100644
--- a/src/multimedia/audio/qaudiosink.h
+++ b/src/multimedia/audio/qaudiosink.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOOUTPUT_H
@@ -45,7 +9,7 @@
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qtaudio.h>
#include <QtMultimedia/qaudioformat.h>
#include <QtMultimedia/qaudiodevice.h>
@@ -85,14 +49,19 @@ public:
qint64 processedUSecs() const;
qint64 elapsedUSecs() const;
- QAudio::Error error() const;
- QAudio::State state() const;
+ QtAudio::Error error() const;
+ QtAudio::State state() const;
void setVolume(qreal);
qreal volume() const;
Q_SIGNALS:
+#if defined(Q_QDOC)
+ void stateChanged(QtAudio::State state);
+#else
+ // use QAudio here to keep string-based connections working
void stateChanged(QAudio::State state);
+#endif
private:
Q_DISABLE_COPY(QAudioSink)
diff --git a/src/multimedia/audio/qaudiosource.cpp b/src/multimedia/audio/qaudiosource.cpp
index 57af98b39..1ed5e82bc 100644
--- a/src/multimedia/audio/qaudiosource.cpp
+++ b/src/multimedia/audio/qaudiosource.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qaudio.h"
@@ -89,7 +53,7 @@ QT_BEGIN_NAMESPACE
At any point in time, QAudioSource will be in one of four states:
active, suspended, stopped, or idle. These states are specified by
- the QAudio::State enum. You can request a state change directly through
+ the QtAudio::State enum. You can request a state change directly through
suspend(), resume(), stop(), reset(), and start(). The current
state is reported by state(). QAudioSink will also signal you
when the state changes (stateChanged()).
@@ -102,8 +66,8 @@ QT_BEGIN_NAMESPACE
which states the QAudioSource has been in.
If an error should occur, you can fetch its reason with error().
- The possible error reasons are described by the QAudio::Error
- enum. The QAudioSource will enter the \l{QAudio::}{StoppedState} when
+ The possible error reasons are described by the QtAudio::Error
+ enum. The QAudioSource will enter the \l{QtAudio::}{StoppedState} when
an error is encountered. Connect to the stateChanged() signal to
handle the error:
@@ -132,15 +96,27 @@ QAudioSource::QAudioSource(const QAudioFormat &format, QObject *parent)
QAudioSource::QAudioSource(const QAudioDevice &audioDevice, const QAudioFormat &format, QObject *parent):
QObject(parent)
{
- d = QPlatformMediaDevices::instance()->audioInputDevice(format, audioDevice);
- if (d)
- connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
+ d = QPlatformMediaIntegration::instance()->mediaDevices()->audioInputDevice(format, audioDevice, parent);
+ if (d) {
+ connect(d, &QPlatformAudioSource::stateChanged, this, [this](QAudio::State state) {
+ // if the signal has been emitted from another thread,
+ // the state may be already changed by main one
+ if (state == d->state())
+ emit stateChanged(state);
+ });
+ }
else
qWarning() << ("No audio device detected");
}
/*!
+ \fn bool QAudioSource::isNull() const
+
+ Returns \c true if the audio source is \c null, otherwise returns \c false.
+*/
+
+/*!
Destroy this audio input.
*/
@@ -155,11 +131,11 @@ QAudioSource::~QAudioSource()
\l{QIODevice::Append}{Append} or \l{QIODevice::ReadWrite}{ReadWrite} modes.
If the QAudioSource is able to successfully get audio data, state() returns
- either QAudio::ActiveState or QAudio::IdleState, error() returns QAudio::NoError
+ either QtAudio::ActiveState or QtAudio::IdleState, error() returns QtAudio::NoError
and the stateChanged() signal is emitted.
- If a problem occurs during this process, error() returns QAudio::OpenError,
- state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
+ If a problem occurs during this process, error() returns QtAudio::OpenError,
+ state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
\sa QIODevice
*/
@@ -181,11 +157,11 @@ void QAudioSource::start(QIODevice* device)
if you start another stream.
If the QAudioSource is able to access the system's audio device, state() returns
- QAudio::IdleState, error() returns QAudio::NoError
+ QtAudio::IdleState, error() returns QtAudio::NoError
and the stateChanged() signal is emitted.
- If a problem occurs during this process, error() returns QAudio::OpenError,
- state() returns QAudio::StoppedState and the stateChanged() signal is emitted.
+ If a problem occurs during this process, error() returns QtAudio::OpenError,
+ state() returns QtAudio::StoppedState and the stateChanged() signal is emitted.
\sa QIODevice
*/
@@ -210,7 +186,7 @@ QAudioFormat QAudioSource::format() const
/*!
Stops the audio input, detaching from the system resource.
- Sets error() to QAudio::NoError, state() to QAudio::StoppedState and
+ Sets error() to QtAudio::NoError, state() to QtAudio::StoppedState and
emit stateChanged() signal.
*/
@@ -233,7 +209,7 @@ void QAudioSource::reset()
/*!
Stops processing audio data, preserving buffered audio data.
- Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and
+ Sets error() to QtAudio::NoError, state() to QtAudio::SuspendedState and
emit stateChanged() signal.
*/
@@ -246,9 +222,9 @@ void QAudioSource::suspend()
/*!
Resumes processing audio data after a suspend().
- Sets error() to QAudio::NoError.
- Sets state() to QAudio::ActiveState if you previously called start(QIODevice*).
- Sets state() to QAudio::IdleState if you previously called start().
+ Sets error() to QtAudio::NoError.
+ Sets state() to QtAudio::ActiveState if you previously called start(QIODevice*).
+ Sets state() to QtAudio::IdleState if you previously called start().
emits stateChanged() signal.
*/
@@ -292,7 +268,7 @@ qsizetype QAudioSource::bufferSize() const
/*!
Returns the amount of audio data available to read in bytes.
- Note: returned value is only valid while in QAudio::ActiveState or QAudio::IdleState
+ Note: returned value is only valid while in QtAudio::ActiveState or QtAudio::IdleState
state, otherwise returns zero.
*/
@@ -364,7 +340,7 @@ qint64 QAudioSource::elapsedUSecs() const
Returns the error state.
*/
-QAudio::Error QAudioSource::error() const
+QtAudio::Error QAudioSource::error() const
{
return d ? d->error() : QAudio::OpenError;
}
@@ -373,14 +349,18 @@ QAudio::Error QAudioSource::error() const
Returns the state of audio processing.
*/
-QAudio::State QAudioSource::state() const
+QtAudio::State QAudioSource::state() const
{
return d ? d->state() : QAudio::StoppedState;
}
/*!
- \fn QAudioSource::stateChanged(QAudio::State state)
+ \fn QAudioSource::stateChanged(QtAudio::State state)
This signal is emitted when the device \a state has changed.
+
+ \note The QtAudio namespace was named QAudio up to and including Qt 6.6.
+ String-based connections to this signal have to use \c{QAudio::State} as
+ the parameter type: \c{connect(source, SIGNAL(stateChanged(QAudio::State)), ...);}
*/
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiosource.h b/src/multimedia/audio/qaudiosource.h
index 9c11b85d3..f0ad0ceb5 100644
--- a/src/multimedia/audio/qaudiosource.h
+++ b/src/multimedia/audio/qaudiosource.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOINPUT_H
@@ -45,7 +9,7 @@
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qtaudio.h>
#include <QtMultimedia/qaudioformat.h>
#include <QtMultimedia/qaudiodevice.h>
@@ -86,11 +50,16 @@ public:
qint64 processedUSecs() const;
qint64 elapsedUSecs() const;
- QAudio::Error error() const;
- QAudio::State state() const;
+ QtAudio::Error error() const;
+ QtAudio::State state() const;
Q_SIGNALS:
+#if defined(Q_QDOC)
+ void stateChanged(QtAudio::State state);
+#else
+ // use QAudio here to keep string-based connections working
void stateChanged(QAudio::State state);
+#endif
private:
Q_DISABLE_COPY(QAudioSource)
diff --git a/src/multimedia/audio/qaudiostatemachine.cpp b/src/multimedia/audio/qaudiostatemachine.cpp
new file mode 100644
index 000000000..2d42200e6
--- /dev/null
+++ b/src/multimedia/audio/qaudiostatemachine.cpp
@@ -0,0 +1,150 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qaudiostatemachine_p.h"
+#include "qaudiosystem_p.h"
+#include <qpointer.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+using Notifier = QAudioStateMachine::Notifier;
+using namespace AudioStateMachineUtils;
+
+QAudioStateMachine::QAudioStateMachine(QAudioStateChangeNotifier &notifier) : m_notifier(&notifier)
+{
+}
+
+QAudioStateMachine::~QAudioStateMachine() = default;
+
+QAudio::State QAudioStateMachine::state() const
+{
+ return toAudioState(m_state.load(std::memory_order_acquire));
+}
+
+QAudio::Error QAudioStateMachine::error() const
+{
+ return toAudioError(m_state.load(std::memory_order_acquire));
+}
+
+template <typename StatesChecker, typename NewState>
+Notifier QAudioStateMachine::changeState(const StatesChecker &checker, const NewState &newState)
+{
+ if constexpr (std::is_same_v<RawState, NewState>)
+ return changeState(checker, [newState](RawState) { return newState; });
+ else {
+ RawState prevState = m_state.load(std::memory_order_relaxed);
+ const auto exchanged = multipleCompareExchange(m_state, prevState, checker, newState);
+
+ if (Q_LIKELY(exchanged))
+ return { this, newState(prevState), prevState };
+
+ return {};
+ }
+}
+
+Notifier QAudioStateMachine::stop(QAudio::Error error, bool shouldDrain, bool forceUpdateError)
+{
+ auto statesChecker =
+ makeStatesChecker(QAudio::ActiveState, QAudio::IdleState, QAudio::SuspendedState,
+ forceUpdateError ? QAudio::StoppedState : QAudio::ActiveState);
+
+ const auto state = toRawState(QAudio::StoppedState, error);
+ auto getNewState = [&](RawState prevState) {
+ const bool shouldAddFlag = shouldDrain && toAudioState(prevState) == QAudio::ActiveState;
+ return shouldAddFlag ? addDrainingFlag(state) : state;
+ };
+
+ return changeState(statesChecker, getNewState);
+}
+
+Notifier QAudioStateMachine::start(bool active)
+{
+ return changeState(makeStatesChecker(QAudio::StoppedState),
+ toRawState(active ? QAudio::ActiveState : QAudio::IdleState));
+}
+
+bool QAudioStateMachine::isActiveOrIdle() const
+{
+ const auto state = this->state();
+ return state == QAudio::ActiveState || state == QAudio::IdleState;
+}
+
+bool QAudioStateMachine::onDrained()
+{
+ return changeState(isDrainingState, removeDrainingFlag);
+}
+
+bool QAudioStateMachine::isDraining() const
+{
+ return isDrainingState(m_state.load(std::memory_order_acquire));
+}
+
+std::pair<bool, bool> QAudioStateMachine::getDrainedAndStopped() const
+{
+ const auto state = m_state.load(std::memory_order_acquire);
+ return { !isDrainingState(state), toAudioState(state) == QAudio::StoppedState };
+}
+
+Notifier QAudioStateMachine::suspend()
+{
+ // Due to the current documentation, we set QAudio::NoError.
+ // TBD: leave the previous error should be more reasonable (IgnoreError)
+ const auto error = QAudio::NoError;
+ auto result = changeState(makeStatesChecker(QAudio::ActiveState, QAudio::IdleState),
+ toRawState(QAudio::SuspendedState, error));
+
+ if (result)
+ m_suspendedInState = result.prevAudioState();
+
+ return result;
+}
+
+Notifier QAudioStateMachine::resume()
+{
+ // Due to the current documentation, we set QAudio::NoError.
+ // TBD: leave the previous error should be more reasonable (IgnoreError)
+ const auto error = QAudio::NoError;
+ return changeState(makeStatesChecker(QAudio::SuspendedState),
+ toRawState(m_suspendedInState, error));
+}
+
+Notifier QAudioStateMachine::activateFromIdle()
+{
+ return changeState(makeStatesChecker(QAudio::IdleState), toRawState(QAudio::ActiveState));
+}
+
+Notifier QAudioStateMachine::updateActiveOrIdle(bool isActive, QAudio::Error error)
+{
+ const auto state = isActive ? QAudio::ActiveState : QAudio::IdleState;
+ return changeState(makeStatesChecker(QAudio::ActiveState, QAudio::IdleState),
+ toRawState(state, error));
+}
+
+Notifier QAudioStateMachine::setError(QAudio::Error error)
+{
+ auto fixState = [error](RawState prevState) { return setStateError(prevState, error); };
+ return changeState([](RawState) { return true; }, fixState);
+}
+
+Notifier QAudioStateMachine::forceSetState(QAudio::State state, QAudio::Error error)
+{
+ return changeState([](RawState) { return true; }, toRawState(state, error));
+}
+
+void QAudioStateMachine::reset(RawState state, RawState prevState)
+{
+ auto notifier = m_notifier;
+
+ const auto audioState = toAudioState(state);
+ const auto audioError = toAudioError(state);
+
+ if (toAudioState(prevState) != audioState && notifier)
+ emit notifier->stateChanged(audioState);
+
+ // check the notifier in case the object was deleted in
+ if (toAudioError(prevState) != audioError && notifier)
+ emit notifier->errorChanged(audioError);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiostatemachine_p.h b/src/multimedia/audio/qaudiostatemachine_p.h
new file mode 100644
index 000000000..385453020
--- /dev/null
+++ b/src/multimedia/audio/qaudiostatemachine_p.h
@@ -0,0 +1,149 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOSTATEMACHINE_P_H
+#define QAUDIOSTATEMACHINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qaudiostatemachineutils_p.h"
+
+#include <qpointer.h>
+#include <atomic>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioStateChangeNotifier;
+
+/* QAudioStateMachine provides an opportunity to
+ * toggle QAudio::State with QAudio::Error in
+ * a thread-safe manner.
+ * The toggling functions return a notifier,
+ * which notifies about the change via
+ * QAudioStateChangeNotifier::stateChanged and errorChanged.
+ *
+ * The state machine is supposed to be used mostly in
+ * QAudioSink and QAudioSource implementations.
+ */
+class Q_MULTIMEDIA_EXPORT QAudioStateMachine
+{
+public:
+ using RawState = AudioStateMachineUtils::RawState;
+ class Notifier
+ {
+ public:
+ void reset()
+ {
+ if (auto stateMachine = std::exchange(m_stateMachine, nullptr))
+ stateMachine->reset(m_state, m_prevState);
+ }
+
+ ~Notifier() { reset(); }
+
+ Notifier(const Notifier &) = delete;
+ Notifier(Notifier &&other) noexcept
+ : m_stateMachine(std::exchange(other.m_stateMachine, nullptr)),
+ m_state(other.m_state),
+ m_prevState(other.m_prevState)
+ {
+ }
+
+ operator bool() const { return m_stateMachine != nullptr; }
+
+ QAudio::State prevAudioState() const { return AudioStateMachineUtils::toAudioState(m_prevState); }
+
+ QAudio::State audioState() const { return AudioStateMachineUtils::toAudioState(m_state); }
+
+ bool isDraining() const { return AudioStateMachineUtils::isDrainingState(m_state); }
+
+ bool isStateChanged() const { return prevAudioState() != audioState(); }
+
+ private:
+ Notifier(QAudioStateMachine *stateMachine = nullptr, RawState state = QAudio::StoppedState,
+ RawState prevState = QAudio::StoppedState)
+ : m_stateMachine(stateMachine), m_state(state), m_prevState(prevState)
+ {
+ }
+
+ private:
+ QAudioStateMachine *m_stateMachine;
+ RawState m_state;
+ const RawState m_prevState;
+
+ friend class QAudioStateMachine;
+ };
+
+ QAudioStateMachine(QAudioStateChangeNotifier &notifier);
+
+ ~QAudioStateMachine();
+
+ QAudio::State state() const;
+
+ QAudio::Error error() const;
+
+ bool isActiveOrIdle() const;
+
+ bool isDraining() const;
+
+ // atomicaly checks if the state is stopped and marked as drained
+ std::pair<bool, bool> getDrainedAndStopped() const;
+
+ // Stopped[draining] -> Stopped
+ bool onDrained();
+
+ // Active/Idle/Suspended -> Stopped
+ // or Active -> Stopped[draining] for shouldDrain = true
+ Notifier stop(QAudio::Error error = QAudio::NoError, bool shouldDrain = false,
+ bool forceUpdateError = false);
+
+ // Active/Idle/Suspended -> Stopped
+ Notifier stopOrUpdateError(QAudio::Error error = QAudio::NoError)
+ {
+ return stop(error, false, true);
+ }
+
+ // Stopped -> Active/Idle
+ Notifier start(bool isActive = true);
+
+ // Active/Idle -> Suspended + saves the exchanged state
+ Notifier suspend();
+
+ // Suspended -> saved state (Active/Idle)
+ Notifier resume();
+
+ // Idle -> Active
+ Notifier activateFromIdle();
+
+ // Active/Idle -> Active/Idle + updateError
+ Notifier updateActiveOrIdle(bool isActive, QAudio::Error error = QAudio::NoError);
+
+ // Any -> Any; better use more strict methods
+ Notifier forceSetState(QAudio::State state, QAudio::Error error = QAudio::NoError);
+
+ // force set the error
+ Notifier setError(QAudio::Error error);
+
+private:
+ template <typename StatesChecker, typename NewState>
+ Notifier changeState(const StatesChecker &statesChecker, const NewState &newState);
+
+ void reset(RawState state, RawState prevState);
+
+private:
+ QPointer<QAudioStateChangeNotifier> m_notifier;
+ std::atomic<RawState> m_state = QAudio::StoppedState;
+ QAudio::State m_suspendedInState = QAudio::SuspendedState;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOSTATEMACHINE_P_H
diff --git a/src/multimedia/audio/qaudiostatemachineutils_p.h b/src/multimedia/audio/qaudiostatemachineutils_p.h
new file mode 100644
index 000000000..f80517f77
--- /dev/null
+++ b/src/multimedia/audio/qaudiostatemachineutils_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOSTATEMACHINEUTILS_P_H
+#define QAUDIOSTATEMACHINEUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qaudio.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace AudioStateMachineUtils {
+
+using RawState = int;
+
+constexpr uint32_t AudioStateBitsCount = 8;
+constexpr RawState AudioStateMask = 0xFF;
+constexpr RawState AudioErrorMask = 0xFF00;
+constexpr RawState DrainingFlag = 0x10000;
+
+static_assert(!(AudioStateMask & DrainingFlag) && !(AudioStateMask & AudioErrorMask)
+ && !(AudioErrorMask & DrainingFlag),
+ "Invalid masks");
+
+constexpr bool isDrainingState(RawState state)
+{
+ return (state & DrainingFlag) != 0;
+}
+
+constexpr RawState addDrainingFlag(RawState state)
+{
+ return state | DrainingFlag;
+}
+
+constexpr RawState removeDrainingFlag(RawState state)
+{
+ return state & ~DrainingFlag;
+}
+
+constexpr QAudio::State toAudioState(RawState state)
+{
+ return QAudio::State(state & AudioStateMask);
+}
+
+constexpr QAudio::Error toAudioError(RawState state)
+{
+ return QAudio::Error((state & AudioErrorMask) >> AudioStateBitsCount);
+}
+
+constexpr RawState toRawState(QAudio::State state, QAudio::Error error = QAudio::NoError)
+{
+ return state | (error << AudioStateBitsCount);
+}
+
+constexpr RawState setStateError(RawState state, QAudio::Error error)
+{
+ return (error << AudioStateBitsCount) | (state & ~AudioErrorMask);
+}
+
+template <typename... States>
+constexpr auto makeStatesChecker(States... states)
+{
+ return [=](RawState state) {
+ state &= (AudioStateMask | DrainingFlag);
+ return (... || (state == states));
+ };
+}
+
+// ensures compareExchange (testAndSet) operation with opportunity
+// to check several states, can be considered as atomic
+template <typename T, typename Predicate, typename NewValueGetter>
+bool multipleCompareExchange(std::atomic<T> &target, T &prevValue, Predicate predicate,
+ NewValueGetter newValueGetter)
+{
+ while (predicate(prevValue))
+ if (target.compare_exchange_strong(prevValue, newValueGetter(prevValue),
+ std::memory_order_acq_rel))
+ return true;
+
+ return false;
+}
+} // namespace AudioStateMachineUtils
+
+QT_END_NAMESPACE
+
+#endif // QAUDIOSTATEMACHINEUTILS_P_H
diff --git a/src/multimedia/audio/qaudiosystem.cpp b/src/multimedia/audio/qaudiosystem.cpp
index f1fd5bb30..355771f6b 100644
--- a/src/multimedia/audio/qaudiosystem.cpp
+++ b/src/multimedia/audio/qaudiosystem.cpp
@@ -1,254 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <private/qtmultimediaglobal_p.h>
#include "qaudiosystem_p.h"
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QPlatformAudioSink
- \brief The QPlatformAudioSink class is a base class for audio backends.
-
- \ingroup multimedia
- \inmodule QtMultimedia
-
- QPlatformAudioSink implements audio functionality for
- QAudioSink, i.e., QAudioSink routes function calls to
- QPlatformAudioSink. For a description of the functionality that
- is implemented, see the QAudioSink class and function
- descriptions.
-
- \sa QAudioSink
-*/
-
-/*!
- \fn void QPlatformAudioSink::start(QIODevice* device)
- Uses the \a device as the QIODevice to transfer data.
-*/
-
-/*!
- \fn QIODevice* QPlatformAudioSink::start()
- Returns a pointer to the QIODevice being used to handle
- the data transfer. This QIODevice can be used to write() audio data directly.
-*/
-
-/*!
- \fn void QPlatformAudioSink::stop()
- Stops the audio output.
-*/
-
-/*!
- \fn void QPlatformAudioSink::reset()
- Drops all audio data in the buffers, resets buffers to zero.
-*/
-
-/*!
- \fn void QPlatformAudioSink::suspend()
- Stops processing audio data, preserving buffered audio data.
-*/
-
-/*!
- \fn void QPlatformAudioSink::resume()
- Resumes processing audio data after a suspend()
-*/
-
-/*!
- \fn qsizetype QPlatformAudioSink::bytesFree() const
- Returns the free space available in bytes in the audio buffer.
-*/
-
-/*!
- \fn void QPlatformAudioSink::setBufferSize(qsizetype value)
- Sets the audio buffer size to \a value in bytes.
-*/
-
-/*!
- \fn qsizetype QPlatformAudioSink::bufferSize() const
- Returns the audio buffer size in bytes.
-*/
-
-/*!
- \fn qint64 QPlatformAudioSink::processedUSecs() const
- Returns the amount of audio data processed since start() was called in milliseconds.
-*/
-
-/*!
- \fn QAudio::Error QPlatformAudioSink::error() const
- Returns the error state.
-*/
-
-/*!
- \fn QAudio::State QPlatformAudioSink::state() const
- Returns the state of audio processing.
-*/
-
-/*!
- \fn void QPlatformAudioSink::setFormat(const QAudioFormat& fmt)
- Set the QAudioFormat to use to \a fmt.
- Setting the format is only allowable while in QAudio::StoppedState.
-*/
-
-/*!
- \fn QAudioFormat QPlatformAudioSink::format() const
- Returns the QAudioFormat being used.
-*/
-
-/*!
- \fn void QPlatformAudioSink::setVolume(qreal volume)
- Sets the volume.
- Where \a volume is between 0.0 and 1.0.
-*/
+#include <private/qplatformmediadevices_p.h>
-/*!
- \fn qreal QPlatformAudioSink::volume() const
- Returns the volume in the range 0.0 and 1.0.
-*/
-
-/*!
- \fn QPlatformAudioSink::errorChanged(QAudio::Error error)
- This signal is emitted when the \a error state has changed.
-*/
-
-/*!
- \fn QPlatformAudioSink::stateChanged(QAudio::State state)
- This signal is emitted when the device \a state has changed.
-*/
-
-/*!
- \class QPlatformAudioSource
- \brief The QPlatformAudioSource class provides access for QAudioSource to access the audio
- device provided by the plugin.
-
- \ingroup multimedia
- \inmodule QtMultimedia
-
- QAudioDeviceInput keeps an instance of QPlatformAudioSource and
- routes calls to functions of the same name to QPlatformAudioSource.
- This means that it is QPlatformAudioSource that implements the
- audio functionality. For a description of the functionality, see
- the QAudioSource class description.
-
- \sa QAudioSource
-*/
-
-/*!
- \fn void QPlatformAudioSource::start(QIODevice* device)
- Uses the \a device as the QIODevice to transfer data.
-*/
-
-/*!
- \fn QIODevice* QPlatformAudioSource::start()
- Returns a pointer to the QIODevice being used to handle
- the data transfer. This QIODevice can be used to read() audio data directly.
-*/
-
-/*!
- \fn void QPlatformAudioSource::stop()
- Stops the audio input.
-*/
-
-/*!
- \fn void QPlatformAudioSource::reset()
- Drops all audio data in the buffers, resets buffers to zero.
-*/
-
-/*!
- \fn void QPlatformAudioSource::suspend()
- Stops processing audio data, preserving buffered audio data.
-*/
-
-/*!
- \fn void QPlatformAudioSource::resume()
- Resumes processing audio data after a suspend().
-*/
-
-/*!
- \fn qsizetype QPlatformAudioSource::bytesReady() const
- Returns the amount of audio data available to read in bytes.
-*/
-
-/*!
- \fn void QPlatformAudioSource::setBufferSize(qsizetype value)
- Sets the audio buffer size to \a value in milliseconds.
-*/
-
-/*!
- \fn qsizetype QPlatformAudioSource::bufferSize() const
- Returns the audio buffer size in milliseconds.
-*/
-
-/*!
- \fn qint64 QPlatformAudioSource::processedUSecs() const
- Returns the amount of audio data processed since start() was called in milliseconds.
-*/
-
-/*!
- \fn QAudio::Error QPlatformAudioSource::error() const
- Returns the error state.
-*/
-
-/*!
- \fn QAudio::State QPlatformAudioSource::state() const
- Returns the state of audio processing.
-*/
+QT_BEGIN_NAMESPACE
-/*!
- \fn void QPlatformAudioSource::setFormat(const QAudioFormat& fmt)
- Set the QAudioFormat to use to \a fmt.
- Setting the format is only allowable while in QAudio::StoppedState.
-*/
+QAudioStateChangeNotifier::QAudioStateChangeNotifier(QObject *parent) : QObject(parent) { }
-/*!
- \fn QAudioFormat QPlatformAudioSource::format() const
- Returns the QAudioFormat being used
-*/
+QPlatformAudioSink::QPlatformAudioSink(QObject *parent) : QAudioStateChangeNotifier(parent) { }
-/*!
- \fn QPlatformAudioSource::errorChanged(QAudio::Error error)
- This signal is emitted when the \a error state has changed.
-*/
+qreal QPlatformAudioSink::volume() const
+{
+ return 1.0;
+}
-/*!
- \fn QPlatformAudioSource::stateChanged(QAudio::State state)
- This signal is emitted when the device \a state has changed.
-*/
+QPlatformAudioSource::QPlatformAudioSource(QObject *parent) : QAudioStateChangeNotifier(parent) { }
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qaudiosystem_p.h b/src/multimedia/audio/qaudiosystem_p.h
index a01d5f8ca..4a0650e80 100644
--- a/src/multimedia/audio/qaudiosystem_p.h
+++ b/src/multimedia/audio/qaudiosystem_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOSYSTEM_H
#define QAUDIOSYSTEM_H
@@ -64,11 +28,23 @@ QT_BEGIN_NAMESPACE
class QIODevice;
-class Q_MULTIMEDIA_EXPORT QPlatformAudioSink : public QObject
+class Q_MULTIMEDIA_EXPORT QAudioStateChangeNotifier : public QObject
+{
+ Q_OBJECT
+public:
+ QAudioStateChangeNotifier(QObject *parent = nullptr);
+
+signals:
+ void errorChanged(QAudio::Error error);
+ void stateChanged(QAudio::State state);
+};
+
+class Q_MULTIMEDIA_EXPORT QPlatformAudioSink : public QAudioStateChangeNotifier
{
Q_OBJECT
public:
+ QPlatformAudioSink(QObject *parent);
virtual void start(QIODevice *device) = 0;
virtual QIODevice* start() = 0;
virtual void stop() = 0;
@@ -84,20 +60,17 @@ public:
virtual void setFormat(const QAudioFormat& fmt) = 0;
virtual QAudioFormat format() const = 0;
virtual void setVolume(qreal) {}
- virtual qreal volume() const { return 1.0; }
+ virtual qreal volume() const;
QElapsedTimer elapsedTime;
-
-Q_SIGNALS:
- void errorChanged(QAudio::Error error);
- void stateChanged(QAudio::State state);
};
-class Q_MULTIMEDIA_EXPORT QPlatformAudioSource : public QObject
+class Q_MULTIMEDIA_EXPORT QPlatformAudioSource : public QAudioStateChangeNotifier
{
Q_OBJECT
public:
+ QPlatformAudioSource(QObject *parent);
virtual void start(QIODevice *device) = 0;
virtual QIODevice* start() = 0;
virtual void stop() = 0;
@@ -116,10 +89,6 @@ public:
virtual qreal volume() const = 0;
QElapsedTimer elapsedTime;
-
-Q_SIGNALS:
- void errorChanged(QAudio::Error error);
- void stateChanged(QAudio::State state);
};
QT_END_NAMESPACE
diff --git a/src/multimedia/audio/qsamplecache_p.cpp b/src/multimedia/audio/qsamplecache_p.cpp
index 7a069ff66..b4be09f72 100644
--- a/src/multimedia/audio/qsamplecache_p.cpp
+++ b/src/multimedia/audio/qsamplecache_p.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsamplecache_p.h"
#include "qwavedecoder.h"
@@ -47,7 +11,7 @@
#include <QtCore/QDebug>
#include <QtCore/qloggingcategory.h>
-Q_LOGGING_CATEGORY(qLcSampleCache, "qt.multimedia.samplecache")
+static Q_LOGGING_CATEGORY(qLcSampleCache, "qt.multimedia.samplecache")
#include <mutex>
@@ -166,6 +130,7 @@ QSample* QSampleCache::requestSample(const QUrl& url)
{
//lock and add first to make sure live loadingThread will not be killed during this function call
m_loadingMutex.lock();
+ const bool needsThreadStart = m_loadingRefCount == 0;
m_loadingRefCount++;
m_loadingMutex.unlock();
@@ -174,11 +139,16 @@ QSample* QSampleCache::requestSample(const QUrl& url)
QMap<QUrl, QSample*>::iterator it = m_samples.find(url);
QSample* sample;
if (it == m_samples.end()) {
- if (!m_loadingThread.isRunning())
+ if (needsThreadStart) {
+ // Previous thread might be finishing, need to wait for it. If not, this is a no-op.
+ m_loadingThread.wait();
m_loadingThread.start();
+ }
sample = new QSample(url, this);
m_samples.insert(url, sample);
+#if QT_CONFIG(thread)
sample->moveToThread(&m_loadingThread);
+#endif
} else {
sample = *it;
}
@@ -336,7 +306,9 @@ void QSample::addRef()
// Called in loading thread
void QSample::readSample()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
+#endif
QMutexLocker m(&m_mutex);
qint64 read = m_waveDecoder->read(m_soundData.data() + m_sampleReadLength,
qMin(m_waveDecoder->bytesAvailable(),
@@ -353,7 +325,9 @@ void QSample::readSample()
// Called in loading thread
void QSample::decoderReady()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
+#endif
QMutexLocker m(&m_mutex);
qCDebug(qLcSampleCache) << "QSample: decoder ready";
m_parent->refresh(m_waveDecoder->size());
@@ -379,21 +353,26 @@ QSample::State QSample::state() const
// Essentially a second ctor, doesn't need locks (?)
void QSample::load()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
+#endif
qCDebug(qLcSampleCache) << "QSample: load [" << m_url << "]";
- m_stream = m_parent->networkAccessManager().get(QNetworkRequest(m_url));
- connect(m_stream, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(loadingError(QNetworkReply::NetworkError)));
+ QNetworkReply *reply = m_parent->networkAccessManager().get(QNetworkRequest(m_url));
+ m_stream = reply;
+ connect(reply, &QNetworkReply::errorOccurred, this, &QSample::loadingError);
m_waveDecoder = new QWaveDecoder(m_stream);
- connect(m_waveDecoder, SIGNAL(formatKnown()), SLOT(decoderReady()));
- connect(m_waveDecoder, SIGNAL(parsingError()), SLOT(decoderError()));
- connect(m_waveDecoder, SIGNAL(readyRead()), SLOT(readSample()));
+ connect(m_waveDecoder, &QWaveDecoder::formatKnown, this, &QSample::decoderReady);
+ connect(m_waveDecoder, &QWaveDecoder::parsingError, this, &QSample::decoderError);
+ connect(m_waveDecoder, &QIODevice::readyRead, this, &QSample::readSample);
m_waveDecoder->open(QIODevice::ReadOnly);
}
void QSample::loadingError(QNetworkReply::NetworkError errorCode)
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
+#endif
QMutexLocker m(&m_mutex);
qCDebug(qLcSampleCache) << "QSample: loading error" << errorCode;
cleanup();
@@ -405,7 +384,9 @@ void QSample::loadingError(QNetworkReply::NetworkError errorCode)
// Called in loading thread
void QSample::decoderError()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
+#endif
QMutexLocker m(&m_mutex);
qCDebug(qLcSampleCache) << "QSample: decoder error";
cleanup();
@@ -417,7 +398,9 @@ void QSample::decoderError()
// Called in loading thread from decoder when sample is done. Locked already.
void QSample::onReady()
{
+#if QT_CONFIG(thread)
Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread"));
+#endif
m_audioFormat = m_waveDecoder->audioFormat();
qCDebug(qLcSampleCache) << "QSample: load ready format:" << m_audioFormat;
cleanup();
diff --git a/src/multimedia/audio/qsamplecache_p.h b/src/multimedia/audio/qsamplecache_p.h
index 2bebf5b3b..3ba0c420c 100644
--- a/src/multimedia/audio/qsamplecache_p.h
+++ b/src/multimedia/audio/qsamplecache_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSAMPLECACHE_P_H
#define QSAMPLECACHE_P_H
diff --git a/src/multimedia/audio/qsoundeffect.cpp b/src/multimedia/audio/qsoundeffect.cpp
index f0bc57520..c12114672 100644
--- a/src/multimedia/audio/qsoundeffect.cpp
+++ b/src/multimedia/audio/qsoundeffect.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtMultimedia/private/qtmultimediaglobal_p.h>
#include "qsoundeffect.h"
@@ -43,14 +7,39 @@
#include "qaudiodevice.h"
#include "qaudiosink.h"
#include "qmediadevices.h"
+#include "qaudiobuffer.h"
#include <QtCore/qloggingcategory.h>
+#include <private/qplatformmediadevices_p.h>
+#include <private/qplatformmediaintegration_p.h>
+#include <private/qplatformaudioresampler_p.h>
-Q_LOGGING_CATEGORY(qLcSoundEffect, "qt.multimedia.soundeffect")
+static Q_LOGGING_CATEGORY(qLcSoundEffect, "qt.multimedia.soundeffect")
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QSampleCache, sampleCache)
+namespace
+{
+struct AudioSinkDeleter
+{
+ void operator ()(QAudioSink* sink) const
+ {
+ sink->stop();
+ // Investigate:should we just delete?
+ sink->deleteLater();
+ }
+};
+
+struct SampleDeleter
+{
+ void operator ()(QSample* sample) const
+ {
+ sample->release();
+ }
+};
+}
+
class QSoundEffectPrivate : public QIODevice
{
public:
@@ -62,13 +51,14 @@ public:
qint64 size() const override {
if (m_sample->state() != QSample::Ready)
return 0;
- return m_loopCount == QSoundEffect::Infinite ? 0 : m_loopCount * m_sample->data().size();
+ return m_loopCount == QSoundEffect::Infinite ? 0 : m_loopCount * m_audioBuffer.byteCount();
}
qint64 bytesAvailable() const override {
if (m_sample->state() != QSample::Ready)
return 0;
- return m_loopCount == QSoundEffect::Infinite
- ? std::numeric_limits<qint64>::max() : m_runningCount * m_sample->data().size() - m_offset;
+ if (m_loopCount == QSoundEffect::Infinite)
+ return std::numeric_limits<qint64>::max();
+ return m_runningCount * m_audioBuffer.byteCount() - m_offset;
}
bool isSequential() const override {
return m_loopCount == QSoundEffect::Infinite;
@@ -92,9 +82,10 @@ public:
int m_loopCount = 1;
int m_runningCount = 0;
bool m_playing = false;
- QSoundEffect::Status m_status = QSoundEffect::Null;
- QAudioSink *m_audioOutput = nullptr;
- QSample *m_sample = nullptr;
+ QSoundEffect::Status m_status = QSoundEffect::Null;
+ std::unique_ptr<QAudioSink, AudioSinkDeleter> m_audioSink;
+ std::unique_ptr<QSample, SampleDeleter> m_sample;
+ QAudioBuffer m_audioBuffer;
bool m_muted = false;
float m_volume = 1.0;
bool m_sampleReady = false;
@@ -108,6 +99,8 @@ QSoundEffectPrivate::QSoundEffectPrivate(QSoundEffect *q, const QAudioDevice &au
, m_audioDevice(audioDevice)
{
open(QIODevice::ReadOnly);
+
+ QPlatformMediaIntegration::instance()->mediaDevices()->prepareAudio();
}
void QSoundEffectPrivate::sampleReady()
@@ -116,30 +109,67 @@ void QSoundEffectPrivate::sampleReady()
return;
qCDebug(qLcSoundEffect) << this << "sampleReady: sample size:" << m_sample->data().size();
- disconnect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
- disconnect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
- if (!m_audioOutput) {
- m_audioOutput = new QAudioSink(m_audioDevice, m_sample->format());
- connect(m_audioOutput, &QAudioSink::stateChanged, this, &QSoundEffectPrivate::stateChanged);
+ disconnect(m_sample.get(), &QSample::error, this, &QSoundEffectPrivate::decoderError);
+ disconnect(m_sample.get(), &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
+ if (!m_audioSink) {
+ const auto audioDevice =
+ m_audioDevice.isNull() ? QMediaDevices::defaultAudioOutput() : m_audioDevice;
+
+ if (audioDevice.isNull()) {
+ // We are likely on a virtual machine, for example in CI
+ qCCritical(qLcSoundEffect) << "Failed to play sound. No audio devices present.";
+ setStatus(QSoundEffect::Error);
+ return;
+ }
+
+ const auto &sampleFormat = m_sample->format();
+ const auto sampleChannelConfig =
+ sampleFormat.channelConfig() == QAudioFormat::ChannelConfigUnknown
+ ? QAudioFormat::defaultChannelConfigForChannelCount(sampleFormat.channelCount())
+ : sampleFormat.channelConfig();
+
+ if (sampleChannelConfig != audioDevice.channelConfiguration()
+ && audioDevice.channelConfiguration() != QAudioFormat::ChannelConfigUnknown) {
+ qCDebug(qLcSoundEffect) << "Create resampler for channels mapping: config"
+ << sampleFormat.channelConfig() << "=> config"
+ << audioDevice.channelConfiguration();
+ auto outputFormat = sampleFormat;
+ outputFormat.setChannelConfig(audioDevice.channelConfiguration());
+
+ const auto resampler = QPlatformMediaIntegration::instance()->createAudioResampler(
+ m_sample->format(), outputFormat);
+ if (resampler)
+ m_audioBuffer = resampler.value()->resample(m_sample->data().constData(),
+ m_sample->data().size());
+ else
+ qCDebug(qLcSoundEffect) << "Cannot create resampler for channels mapping";
+ }
+
+ if (!m_audioBuffer.isValid())
+ m_audioBuffer = QAudioBuffer(m_sample->data(), m_sample->format());
+
+ m_audioSink.reset(new QAudioSink(audioDevice, m_audioBuffer.format()));
+
+ connect(m_audioSink.get(), &QAudioSink::stateChanged, this, &QSoundEffectPrivate::stateChanged);
if (!m_muted)
- m_audioOutput->setVolume(m_volume);
+ m_audioSink->setVolume(m_volume);
else
- m_audioOutput->setVolume(0);
+ m_audioSink->setVolume(0);
}
m_sampleReady = true;
setStatus(QSoundEffect::Ready);
- if (m_playing && m_audioOutput->state() == QAudio::StoppedState) {
+ if (m_playing && m_audioSink->state() == QAudio::StoppedState) {
qCDebug(qLcSoundEffect) << this << "starting playback on audiooutput";
- m_audioOutput->start(this);
+ m_audioSink->start(this);
}
}
void QSoundEffectPrivate::decoderError()
{
qWarning("QSoundEffect(qaudio): Error decoding source %ls", qUtf16Printable(m_url.toString()));
- disconnect(m_sample, &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
- disconnect(m_sample, &QSample::error, this, &QSoundEffectPrivate::decoderError);
+ disconnect(m_sample.get(), &QSample::ready, this, &QSoundEffectPrivate::sampleReady);
+ disconnect(m_sample.get(), &QSample::error, this, &QSoundEffectPrivate::decoderError);
m_playing = false;
setStatus(QSoundEffect::Error);
}
@@ -148,7 +178,7 @@ void QSoundEffectPrivate::stateChanged(QAudio::State state)
{
qCDebug(qLcSoundEffect) << this << "stateChanged " << state;
if ((state == QAudio::IdleState && m_runningCount == 0) || state == QAudio::StoppedState)
- emit q_ptr->stop();
+ q_ptr->stop();
}
qint64 QSoundEffectPrivate::readData(char *data, qint64 len)
@@ -163,8 +193,8 @@ qint64 QSoundEffectPrivate::readData(char *data, qint64 len)
qint64 bytesWritten = 0;
- const int sampleSize = m_sample->data().size();
- const char* sampleData = m_sample->data().constData();
+ const int sampleSize = m_audioBuffer.byteCount();
+ const char *sampleData = m_audioBuffer.constData<char>();
while (len && m_runningCount) {
int toWrite = qMin(sampleSize - m_offset, len);
@@ -214,8 +244,8 @@ void QSoundEffectPrivate::setStatus(QSoundEffect::Status status)
void QSoundEffectPrivate::setPlaying(bool playing)
{
qCDebug(qLcSoundEffect) << this << "setPlaying(" << playing << ")" << m_playing;
- if (m_audioOutput) {
- m_audioOutput->stop();
+ if (m_audioSink) {
+ m_audioSink->stop();
if (playing && !m_sampleReady)
return;
}
@@ -224,8 +254,8 @@ void QSoundEffectPrivate::setPlaying(bool playing)
return;
m_playing = playing;
- if (m_audioOutput && playing)
- m_audioOutput->start(this);
+ if (m_audioSink && playing)
+ m_audioSink->start(this);
emit q_ptr->playingChanged();
}
@@ -316,11 +346,8 @@ QSoundEffect::QSoundEffect(const QAudioDevice &audioDevice, QObject *parent)
QSoundEffect::~QSoundEffect()
{
stop();
- if (d->m_audioOutput) {
- d->m_audioOutput->stop();
- d->m_audioOutput->deleteLater();
- d->m_sample->release();
- }
+ d->m_audioSink.reset();
+ d->m_sample.reset();
delete d;
}
@@ -391,24 +418,22 @@ void QSoundEffect::setSource(const QUrl &url)
if (d->m_sample) {
if (!d->m_sampleReady) {
- QObject::disconnect(d->m_sample, &QSample::error, d, &QSoundEffectPrivate::decoderError);
- QObject::disconnect(d->m_sample, &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
+ disconnect(d->m_sample.get(), &QSample::error, d, &QSoundEffectPrivate::decoderError);
+ disconnect(d->m_sample.get(), &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
}
d->m_sample->release();
d->m_sample = nullptr;
}
- if (d->m_audioOutput) {
- QObject::disconnect(d->m_audioOutput, &QAudioSink::stateChanged, d, &QSoundEffectPrivate::stateChanged);
- d->m_audioOutput->stop();
- d->m_audioOutput->deleteLater();
- d->m_audioOutput = nullptr;
+ if (d->m_audioSink) {
+ disconnect(d->m_audioSink.get(), &QAudioSink::stateChanged, d, &QSoundEffectPrivate::stateChanged);
+ d->m_audioSink.reset();
}
d->setStatus(QSoundEffect::Loading);
- d->m_sample = sampleCache()->requestSample(url);
- QObject::connect(d->m_sample, &QSample::error, d, &QSoundEffectPrivate::decoderError);
- QObject::connect(d->m_sample, &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
+ d->m_sample.reset(sampleCache()->requestSample(url));
+ connect(d->m_sample.get(), &QSample::error, d, &QSoundEffectPrivate::decoderError);
+ connect(d->m_sample.get(), &QSample::ready, d, &QSoundEffectPrivate::sampleReady);
switch (d->m_sample->state()) {
case QSample::Ready:
@@ -486,6 +511,11 @@ void QSoundEffect::setLoopCount(int loopCount)
emit loopCountChanged();
}
+/*!
+ \property QSoundEffect::audioDevice
+
+ Returns the QAudioDevice instance.
+*/
QAudioDevice QSoundEffect::audioDevice()
{
return d->m_audioDevice;
@@ -530,7 +560,7 @@ int QSoundEffect::loopsRemaining() const
UI volume controls should usually be scaled non-linearly. For example, using a logarithmic scale
will produce linear changes in perceived loudness, which is what a user would normally expect
- from a volume control. See \l {QAudio::convertVolume()}{convertVolume()}
+ from a volume control. See \l {QtAudio::convertVolume()}{convertVolume()}
for more details.
*/
/*!
@@ -544,8 +574,8 @@ int QSoundEffect::loopsRemaining() const
*/
float QSoundEffect::volume() const
{
- if (d->m_audioOutput && !d->m_muted)
- return d->m_audioOutput->volume();
+ if (d->m_audioSink && !d->m_muted)
+ return d->m_audioSink->volume();
return d->m_volume;
}
@@ -560,7 +590,7 @@ float QSoundEffect::volume() const
UI volume controls should usually be scaled non-linearly. For example, using a logarithmic scale
will produce linear changes in perceived loudness, which is what a user would normally expect
- from a volume control. See QAudio::convertVolume() for more details.
+ from a volume control. See QtAudio::convertVolume() for more details.
*/
void QSoundEffect::setVolume(float volume)
{
@@ -570,8 +600,8 @@ void QSoundEffect::setVolume(float volume)
d->m_volume = volume;
- if (d->m_audioOutput && !d->m_muted)
- d->m_audioOutput->setVolume(volume);
+ if (d->m_audioSink && !d->m_muted)
+ d->m_audioSink->setVolume(volume);
emit volumeChanged();
}
@@ -606,10 +636,10 @@ void QSoundEffect::setMuted(bool muted)
if (d->m_muted == muted)
return;
- if (muted && d->m_audioOutput)
- d->m_audioOutput->setVolume(0);
- else if (!muted && d->m_audioOutput && d->m_muted)
- d->m_audioOutput->setVolume(d->m_volume);
+ if (muted && d->m_audioSink)
+ d->m_audioSink->setVolume(0);
+ else if (!muted && d->m_audioSink && d->m_muted)
+ d->m_audioSink->setVolume(d->m_volume);
d->m_muted = muted;
emit mutedChanged();
diff --git a/src/multimedia/audio/qsoundeffect.h b/src/multimedia/audio/qsoundeffect.h
index 5b806b382..eeafc4c9f 100644
--- a/src/multimedia/audio/qsoundeffect.h
+++ b/src/multimedia/audio/qsoundeffect.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSOUNDEFFECT_H
#define QSOUNDEFFECT_H
diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qtaudio.cpp
index 2898868a9..fb14e5093 100644
--- a/src/multimedia/audio/qaudio.cpp
+++ b/src/multimedia/audio/qtaudio.cpp
@@ -1,44 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <qaudio.h>
+#include <qtaudio.h>
#include <qmath.h>
#include <QDebug>
@@ -47,16 +11,16 @@ QT_BEGIN_NAMESPACE
#define LOG100 4.60517018599
/*!
- \namespace QAudio
+ \namespace QtAudio
\ingroup multimedia-namespaces
- \brief The QAudio namespace contains enums used by the audio classes.
+ \brief The QtAudio namespace contains enums used by the audio classes.
\inmodule QtMultimedia
\ingroup multimedia
\ingroup multimedia_audio
*/
/*!
- \enum QAudio::Error
+ \enum QtAudio::Error
\value NoError No errors have occurred
\value OpenError An error occurred opening the audio device
@@ -66,7 +30,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \enum QAudio::State
+ \enum QtAudio::State
\value ActiveState Audio data is being processed, this state is set after start() is called
and while audio data is available to be processed.
@@ -80,7 +44,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \enum QAudio::VolumeScale
+ \enum QtAudio::VolumeScale
This enum defines the different audio volume scales.
@@ -95,11 +59,14 @@ QT_BEGIN_NAMESPACE
\value DecibelVolumeScale Decibel (dB, amplitude) logarithmic scale. \c -200 is silence
and \c 0 is full volume.
- \since 5.8
- \sa QAudio::convertVolume()
+ \sa QtAudio::convertVolume()
*/
+#if defined(Q_QDOC)
+namespace QtAudio
+#else
namespace QAudio
+#endif
{
/*!
@@ -120,7 +87,6 @@ namespace QAudio
\snippet multimedia-snippets/audio.cpp Volume conversion
- \since 5.8
\sa VolumeScale, QAudioSink::setVolume(), QAudioSource::setVolume(),
QSoundEffect::setVolume()
*/
diff --git a/src/multimedia/audio/qtaudio.h b/src/multimedia/audio/qtaudio.h
new file mode 100644
index 000000000..6f6e0e668
--- /dev/null
+++ b/src/multimedia/audio/qtaudio.h
@@ -0,0 +1,18 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#ifndef QTAUDIO_H
+#define QTAUDIO_H
+
+#if 0
+#pragma qt_class(QtAudio)
+#endif
+
+#include <QtMultimedia/qaudio.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
+
+#endif // QTAUDIO_H
diff --git a/src/multimedia/audio/qwavedecoder.cpp b/src/multimedia/audio/qwavedecoder.cpp
index 8d6173443..452363ddc 100644
--- a/src/multimedia/audio/qwavedecoder.cpp
+++ b/src/multimedia/audio/qwavedecoder.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwavedecoder.h"
@@ -92,7 +56,7 @@ bool QWaveDecoder::open(QIODevice::OpenMode mode)
if (canOpen && enoughDataAvailable())
handleData();
else
- connect(device, SIGNAL(readyRead()), SLOT(handleData()));
+ connect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
return canOpen;
}
@@ -127,6 +91,10 @@ qint64 QWaveDecoder::pos() const
return device->pos();
}
+void QWaveDecoder::setIODevice(QIODevice * /* device */)
+{
+}
+
QAudioFormat QWaveDecoder::audioFormat() const
{
return format;
@@ -175,7 +143,8 @@ qint64 QWaveDecoder::headerLength()
qint64 QWaveDecoder::readData(char *data, qint64 maxlen)
{
- if (!haveFormat || format.bytesPerSample() == 0)
+ const int bytesPerSample = format.bytesPerSample();
+ if (!haveFormat || bytesPerSample == 0)
return 0;
if (bps == 24) {
@@ -199,15 +168,15 @@ qint64 QWaveDecoder::readData(char *data, qint64 maxlen)
return l;
}
- qint64 nSamples = maxlen / format.bytesPerSample();
- maxlen = nSamples * format.bytesPerSample();
+ qint64 nSamples = maxlen / bytesPerSample;
+ maxlen = nSamples * bytesPerSample;
int read = device->read(data, maxlen);
if (!byteSwap || format.bytesPerFrame() == 1)
return read;
- nSamples = read / format.bytesPerSample();
- switch (format.bytesPerSample()) {
+ nSamples = read / bytesPerSample;
+ switch (bytesPerSample) {
case 2:
bswap2(data, nSamples);
break;
@@ -305,7 +274,7 @@ bool QWaveDecoder::writeDataLength()
void QWaveDecoder::parsingFailed()
{
Q_ASSERT(device);
- device->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
+ disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
emit parsingError();
}
@@ -408,8 +377,8 @@ void QWaveDecoder::handleData()
}
format.setSampleFormat(fmt);
- format.setSampleRate(/*qFromBigEndian<quint32>*/(wave.sampleRate));
- format.setChannelCount(/*qFromBigEndian<quint16>*/(wave.numChannels));
+ format.setSampleRate(rate);
+ format.setChannelCount(channels);
state = QWaveDecoder::WaitingForDataState;
}
@@ -417,7 +386,7 @@ void QWaveDecoder::handleData()
if (state == QWaveDecoder::WaitingForDataState) {
if (findChunk("data")) {
- device->disconnect(SIGNAL(readyRead()), this, SLOT(handleData()));
+ disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData);
chunk descriptor;
device->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk));
@@ -431,7 +400,7 @@ void QWaveDecoder::handleData()
dataSize = device->size() - headerLength();
haveFormat = true;
- connect(device, SIGNAL(readyRead()), SIGNAL(readyRead()));
+ connect(device, &QIODevice::readyRead, this, &QIODevice::readyRead);
emit formatKnown();
return;
@@ -474,9 +443,15 @@ bool QWaveDecoder::findChunk(const char *chunkId)
if (qstrncmp(descriptor.id, chunkId, 4) == 0)
return true;
+ // A program reading a RIFF file can skip over any chunk whose chunk
+ // ID it doesn't recognize; it simply skips the number of bytes specified
+ // by ckSize plus the pad byte, if present. See Multimedia Programming
+ // Interface and Data Specifications 1.0. IBM / Microsoft. August 1991. pp. 10-11.
+ const quint32 sizeWithPad = descriptor.size + (descriptor.size & 1);
+
// It's possible that bytes->available() is less than the chunk size
// if it's corrupt.
- junkToSkip = qint64(sizeof(chunk) + descriptor.size);
+ junkToSkip = qint64(sizeof(chunk) + sizeWithPad);
// Skip the current amount
if (junkToSkip > 0)
diff --git a/src/multimedia/audio/qwavedecoder.h b/src/multimedia/audio/qwavedecoder.h
index ab6e49ab8..a96e731db 100644
--- a/src/multimedia/audio/qwavedecoder.h
+++ b/src/multimedia/audio/qwavedecoder.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef WAVEDECODER_H
#define WAVEDECODER_H
@@ -98,9 +62,11 @@ private:
struct chunk
{
- char id[4];
- quint32 size;
+ char id[4]; // A four-character code that identifies the representation of the chunk data
+ // padded on the right with blank characters (ASCII 32)
+ quint32 size; // Does not include the size of the id or size fields or the pad byte at the end of payload
};
+
bool peekChunk(chunk* pChunk, bool handleEndianness = true);
struct RIFFHeader
diff --git a/src/multimedia/camera/qcamera.cpp b/src/multimedia/camera/qcamera.cpp
index 60b2d99ea..a625fb96f 100644
--- a/src/multimedia/camera/qcamera.cpp
+++ b/src/multimedia/camera/qcamera.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcamera_p.h"
@@ -193,29 +157,26 @@ void QCameraPrivate::_q_error(int error, const QString &errorString)
{
Q_Q(QCamera);
- this->error = QCamera::Error(error);
- this->errorString = errorString;
-
- emit q->errorChanged();
- emit q->errorOccurred(this->error, errorString);
+ this->error.setAndNotify(QCamera::Error(error), errorString, *q);
}
void QCameraPrivate::init(const QCameraDevice &device)
{
Q_Q(QCamera);
- control = QPlatformMediaIntegration::instance()->createCamera(q);
- if (!control) {
- _q_error(QCamera::CameraError, QString::fromUtf8("Camera not supported"));
+ auto maybeControl = QPlatformMediaIntegration::instance()->createCamera(q);
+ if (!maybeControl) {
+ qWarning() << "Failed to initialize QCamera" << maybeControl.error();
+ error = { QCamera::CameraError, maybeControl.error() };
return;
}
-
+ control = maybeControl.value();
cameraDevice = !device.isNull() ? device : QMediaDevices::defaultVideoInput();
if (cameraDevice.isNull())
- _q_error(QCamera::CameraError, QString::fromUtf8("No camera detected"));
+ _q_error(QCamera::CameraError, QStringLiteral("No camera detected"));
control->setCamera(cameraDevice);
- q->connect(control, SIGNAL(activeChanged(bool)), q, SIGNAL(activeChanged(bool)));
- q->connect(control, SIGNAL(error(int,QString)), q, SLOT(_q_error(int,QString)));
+ q->connect(control, &QPlatformVideoSource::activeChanged, q, &QCamera::activeChanged);
+ q->connect(control, SIGNAL(error(int, QString)), q, SLOT(_q_error(int, QString)));
}
/*!
@@ -279,7 +240,6 @@ QCamera::~QCamera()
Q_D(QCamera);
if (d->captureSession)
d->captureSession->setCamera(nullptr);
- Q_ASSERT(!d->captureSession);
}
/*!
@@ -329,12 +289,14 @@ void QCamera::setActive(bool active)
*/
/*!
+ \property QCamera::error
+
Returns the error state of the camera.
*/
QCamera::Error QCamera::error() const
{
- return d_func()->error;
+ return d_func()->error.code();
}
/*!
@@ -344,11 +306,13 @@ QCamera::Error QCamera::error() const
*/
/*!
+ \property QCamera::errorString
+
Returns a human readable string describing a camera's error state.
*/
QString QCamera::errorString() const
{
- return d_func()->errorString;
+ return d_func()->error.description();
}
/*! \enum QCamera::Feature
@@ -378,6 +342,8 @@ QString QCamera::errorString() const
*/
/*!
+ \property QCamera::supportedFeatures
+
Returns the features supported by this camera.
\sa QCamera::Feature
@@ -449,12 +415,14 @@ QPlatformCamera *QCamera::platformCamera()
return d->control;
}
-/*! \qmlproperty CameraDevice QtMultimedia::Camera::cameraDevice
+/*! \qmlproperty cameraDevice QtMultimedia::Camera::cameraDevice
Gets or sets the currently active camera device.
*/
/*!
+ \property QCamera::cameraDevice
+
Returns the QCameraDevice object associated with this camera.
*/
QCameraDevice QCamera::cameraDevice() const
@@ -483,16 +451,28 @@ void QCamera::setCameraDevice(const QCameraDevice &cameraDevice)
setCameraFormat({});
}
-/*! \qmlproperty CameraDevice QtMultimedia::Camera::cameraFormat
+/*! \qmlproperty cameraFormat QtMultimedia::Camera::cameraFormat
Gets or sets the currently active camera format.
- \sa CameraDevice::videoFormats
+ \note When using the FFMPEG backend on an Android target device if you request
+ \b YUV420P format, you will receive either a fully planar 4:2:0 YUV420P or a
+ semi-planar NV12/NV21. This depends on the codec implemented by the device
+ OEM.
+
+ \sa cameraDevice::videoFormats
*/
/*!
+ \property QCamera::cameraFormat
+
Returns the camera format currently used by the camera.
+ \note When using the FFMPEG backend on an Android target device if you request
+ \b YUV420P format, you will receive either a fully planar 4:2:0 YUV420P or a
+ semi-planar NV12/NV21. This depends on the codec implemented by the device
+ OEM.
+
\sa QCameraDevice::videoFormats
*/
QCameraFormat QCamera::cameraFormat() const
@@ -502,8 +482,13 @@ QCameraFormat QCamera::cameraFormat() const
}
/*!
- Tells the camera to use the format desribed by \a format. This can be used to define
- as specific resolution and frame rate to be used for recording and image capture.
+ Tells the camera to use the format described by \a format. This can be used to define
+ a specific resolution and frame rate to be used for recording and image capture.
+
+ \note When using the FFMPEG backend on an Android target device if you request
+ \b YUV420P format, you will receive either a fully planar 4:2:0 YUV420P or a
+ semi-planar NV12/NV21. This depends on the codec implemented by the device
+ OEM.
*/
void QCamera::setCameraFormat(const QCameraFormat &format)
{
@@ -578,6 +563,11 @@ QCamera::FocusMode QCamera::focusMode() const
return d->control ? d->control->focusMode() : QCamera::FocusModeAuto;
}
+/*!
+ \fn void QCamera::focusModeChanged()
+
+ Signals when the focusMode changes.
+*/
void QCamera::setFocusMode(QCamera::FocusMode mode)
{
Q_D(QCamera);
@@ -608,6 +598,8 @@ bool QCamera::isFocusModeSupported(FocusMode mode) const
*/
/*!
+ \property QCamera::focusPoint
+
Returns the point currently used by the auto focus system to focus onto.
*/
QPointF QCamera::focusPoint() const
@@ -650,20 +642,19 @@ QPointF QCamera::customFocusPoint() const
void QCamera::setCustomFocusPoint(const QPointF &point)
{
Q_D(QCamera);
- if (!d->control)
- return;
- d->control->setCustomFocusPoint(point);
+ if (d->control)
+ d->control->setCustomFocusPoint(point);
}
/*!
- \qmlproperty float QCamera::focusDistance
+ \qmlproperty float QtMultimedia::Camera::focusDistance
This property return an approximate focus distance of the camera. The value reported
is between 0 and 1, 0 being the closest possible focus distance, 1 being as far away
as possible. Note that 1 is often, but not always infinity.
Setting the focus distance will be ignored unless the focus mode is set to
- \l FocusModeManual.
+ \l {focusMode}{FocusModeManual}.
*/
/*!
@@ -700,6 +691,8 @@ float QCamera::focusDistance() const
/*!
+ \property QCamera::maximumZoomFactor
+
Returns the maximum zoom factor.
This will be \c 1.0 on cameras that do not support zooming.
@@ -708,7 +701,7 @@ float QCamera::focusDistance() const
float QCamera::maximumZoomFactor() const
{
Q_D(const QCamera);
- return d->control ? d->control->maxZoomFactor() : 1.;
+ return d->control ? d->control->maxZoomFactor() : 1.f;
}
/*!
@@ -720,6 +713,8 @@ float QCamera::maximumZoomFactor() const
*/
/*!
+ \property QCamera::minimumZoomFactor
+
Returns the minimum zoom factor.
This will be \c 1.0 on cameras that do not support zooming.
@@ -728,7 +723,7 @@ float QCamera::maximumZoomFactor() const
float QCamera::minimumZoomFactor() const
{
Q_D(const QCamera);
- return d->control ? d->control->minZoomFactor() : 1.;
+ return d->control ? d->control->minZoomFactor() : 1.f;
}
/*!
@@ -748,14 +743,14 @@ float QCamera::minimumZoomFactor() const
float QCamera::zoomFactor() const
{
Q_D(const QCamera);
- return d->control ? d->control->zoomFactor() : 1.;
+ return d->control ? d->control->zoomFactor() : 1.f;
}
/*!
Zooms to a zoom factor \a factor at a rate of 1 factor per second.
*/
void QCamera::setZoomFactor(float factor)
{
- zoomTo(factor, 0.);
+ zoomTo(factor, 0.f);
}
/*!
@@ -781,9 +776,9 @@ void QCamera::setZoomFactor(float factor)
*/
void QCamera::zoomTo(float factor, float rate)
{
- Q_ASSERT(rate >= 0.);
- if (rate < 0.)
- rate = 0.;
+ Q_ASSERT(rate >= 0.f);
+ if (rate < 0.f)
+ rate = 0.f;
Q_D(QCamera);
if (!d->control)
@@ -806,11 +801,15 @@ void QCamera::zoomTo(float factor, float rate)
*/
/*!
- \qmlproperty Camera::FlashMode QtMultimedia::Camera::flashMode
+ \qmlproperty enumeration QtMultimedia::Camera::flashMode
Gets or sets a certain flash mode if the camera has a flash.
- \sa QCamera::FlashMode, Camera::isFlashModeSupported, Camera::isFlashReady
+ \value Camera.FlashOff Flash is Off.
+ \value Camera.FlashOn Flash is On.
+ \value Camera.FlashAuto Automatic flash.
+
+ \sa isFlashModeSupported, isFlashReady
*/
/*!
@@ -952,14 +951,11 @@ void QCamera::setExposureMode(QCamera::ExposureMode mode)
bool QCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
{
Q_D(const QCamera);
- if (!d->control)
- return false;
-
- return d->control->isExposureModeSupported(mode);
+ return d->control && d->control->isExposureModeSupported(mode);
}
/*!
- \qmlproperty real QCamera::exposureCompensation
+ \qmlproperty real QtMultimedia::Camera::exposureCompensation
Gets or sets the exposure compensation in EV units.
@@ -977,7 +973,7 @@ bool QCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
float QCamera::exposureCompensation() const
{
Q_D(const QCamera);
- return d->control ? d->control->exposureCompensation() : 0.;
+ return d->control ? d->control->exposureCompensation() : 0.f;
}
void QCamera::setExposureCompensation(float ev)
@@ -1075,7 +1071,7 @@ int QCamera::maximumIsoSensitivity() const
float QCamera::minimumExposureTime() const
{
Q_D(const QCamera);
- return d->control ? d->control->minExposureTime() : -1.;
+ return d->control ? d->control->minExposureTime() : -1.f;
}
/*!
@@ -1084,7 +1080,7 @@ float QCamera::minimumExposureTime() const
float QCamera::maximumExposureTime() const
{
Q_D(const QCamera);
- return d->control ? d->control->maxExposureTime() : -1.;
+ return d->control ? d->control->maxExposureTime() : -1.f;
}
/*!
@@ -1127,6 +1123,8 @@ float QCamera::exposureTime() const
*/
/*!
+ \property QCamera::manualExposureTime
+
Set the manual exposure time to \a seconds
*/
@@ -1235,6 +1233,8 @@ void QCamera::setAutoExposureTime()
*/
/*!
+ \property QCamera::whiteBalanceMode
+
Returns the white balance mode being used.
*/
QCamera::WhiteBalanceMode QCamera::whiteBalanceMode() const
@@ -1270,9 +1270,7 @@ void QCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
bool QCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
{
Q_D(const QCamera);
- if (!d->control)
- return false;
- return d->control->isWhiteBalanceModeSupported(mode);
+ return d->control && d->control->isWhiteBalanceModeSupported(mode);
}
/*!
@@ -1287,6 +1285,8 @@ bool QCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
*/
/*!
+ \property QCamera::colorTemperature
+
Returns the current color temperature if the
current white balance mode is \c WhiteBalanceManual. For other modes the
return value is undefined.
@@ -1339,6 +1339,22 @@ void QCamera::setColorTemperature(int colorTemperature)
\value WhiteBalanceSunset Sunset white balance mode.
*/
+/*!
+ \fn void QCamera::brightnessChanged()
+ \internal
+*/
+/*!
+ \fn void QCamera::contrastChanged()
+ \internal
+*/
+/*!
+ \fn void QCamera::hueChanged()
+ \internal
+*/
+/*!
+ \fn void QCamera::saturationChanged()
+ \internal
+*/
QT_END_NAMESPACE
#include "moc_qcamera.cpp"
diff --git a/src/multimedia/camera/qcamera.h b/src/multimedia/camera/qcamera.h
index 735bdc77a..09d9521ff 100644
--- a/src/multimedia/camera/qcamera.h
+++ b/src/multimedia/camera/qcamera.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCAMERA_H
#define QCAMERA_H
@@ -70,7 +34,7 @@ class Q_MULTIMEDIA_EXPORT QCamera : public QObject
Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged)
Q_PROPERTY(QCameraFormat cameraFormat READ cameraFormat WRITE setCameraFormat NOTIFY cameraFormatChanged)
- Q_PROPERTY(FocusMode focusMode READ focusMode WRITE setFocusMode)
+ Q_PROPERTY(FocusMode focusMode READ focusMode WRITE setFocusMode NOTIFY focusModeChanged)
Q_PROPERTY(QPointF focusPoint READ focusPoint NOTIFY focusPointChanged)
Q_PROPERTY(QPointF customFocusPoint READ customFocusPoint WRITE setCustomFocusPoint NOTIFY customFocusPointChanged)
Q_PROPERTY(float focusDistance READ focusDistance WRITE setFocusDistance NOTIFY focusDistanceChanged)
@@ -284,9 +248,8 @@ Q_SIGNALS:
void exposureCompensationChanged(float);
void exposureModeChanged();
- void whiteBalanceModeChanged() const;
- void colorTemperatureChanged() const;
-
+ void whiteBalanceModeChanged() QT6_ONLY(const);
+ void colorTemperatureChanged() QT6_ONLY(const);
void brightnessChanged();
void contrastChanged();
void saturationChanged();
diff --git a/src/multimedia/camera/qcamera_p.h b/src/multimedia/camera/qcamera_p.h
index b9e4e377a..c0477c242 100644
--- a/src/multimedia/camera/qcamera_p.h
+++ b/src/multimedia/camera/qcamera_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCAMERA_P_H
#define QCAMERA_P_H
@@ -52,6 +16,7 @@
//
#include "private/qobject_p.h"
+#include "private/qerrorinfo_p.h"
#include "qcamera.h"
#include "qcameradevice.h"
@@ -64,25 +29,18 @@ class QCameraPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QCamera)
public:
- QCameraPrivate()
- : QObjectPrivate(),
- error(QCamera::NoError)
- {
- }
-
void init(const QCameraDevice &device);
QMediaCaptureSession *captureSession = nullptr;
QPlatformCamera *control = nullptr;
- QCamera::Error error;
- QString errorString;
+ QErrorInfo<QCamera::Error> error;
QCameraDevice cameraDevice;
QCameraFormat cameraFormat;
void _q_error(int error, const QString &errorString);
- void unsetError() { error = QCamera::NoError; errorString.clear(); }
+ void unsetError() { error = {}; }
};
QT_END_NAMESPACE
diff --git a/src/multimedia/camera/qcameradevice.cpp b/src/multimedia/camera/qcameradevice.cpp
index e727301e3..50727d49c 100644
--- a/src/multimedia/camera/qcameradevice.cpp
+++ b/src/multimedia/camera/qcameradevice.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcameradevice_p.h"
@@ -114,7 +78,7 @@ QCameraFormat::~QCameraFormat() = default;
*/
/*!
- \qmlproperty enumeration QtMultimedia::CameraFormat::pixelFormat
+ \qmlproperty enumeration QtMultimedia::cameraFormat::pixelFormat
Holds the pixel format.
@@ -125,6 +89,8 @@ QCameraFormat::~QCameraFormat() = default;
*/
/*!
+ \property QCameraFormat::pixelFormat
+
Returns the pixel format.
Most commonly this is either QVideoFrameFormat::Format_Jpeg or QVideoFrameFormat::Format_YUVY
@@ -138,12 +104,14 @@ QVideoFrameFormat::PixelFormat QCameraFormat::pixelFormat() const noexcept
}
/*!
- \qmlproperty size QtMultimedia::CameraFormat::resolution
+ \qmlproperty size QtMultimedia::cameraFormat::resolution
Returns the resolution.
*/
/*!
+ \property QCameraFormat::resolution
+
Returns the resolution.
*/
QSize QCameraFormat::resolution() const noexcept
@@ -152,12 +120,14 @@ QSize QCameraFormat::resolution() const noexcept
}
/*!
- \qmlproperty real QtMultimedia::CameraFormat::minFrameRate
+ \qmlproperty real QtMultimedia::cameraFormat::minFrameRate
Returns the lowest frame rate defined by this format.
*/
/*!
+ \property QCameraFormat::minFrameRate
+
Returns the lowest frame rate defined by this format.
*/
float QCameraFormat::minFrameRate() const noexcept
@@ -166,18 +136,20 @@ float QCameraFormat::minFrameRate() const noexcept
}
/*!
- \qmlproperty real QtMultimedia::CameraFormat::maxFrameRate
+ \qmlproperty real QtMultimedia::cameraFormat::maxFrameRate
Returns the highest frame rate defined by this format.
- In 6.2, the camera will always try to use the maximum frame rate supported by a
+ The camera will always try to use the maximum frame rate supported by a
certain video format.
*/
/*!
+ \property QCameraFormat::maxFrameRate
+
Returns the highest frame rate defined by this format.
- In 6.2, the camera will always try to use the highest frame rate supported by a
+ The camera will always try to use the highest frame rate supported by a
certain video format.
*/
float QCameraFormat::maxFrameRate() const noexcept
@@ -237,8 +209,7 @@ bool QCameraFormat::operator==(const QCameraFormat &other) const
\snippet multimedia-snippets/camera.cpp Camera selection
You can also use QCameraDevice to get general information about a camera
- device such as description, physical position on the system, or camera sensor
- orientation.
+ device such as description and physical position on the system.
\snippet multimedia-snippets/camera.cpp Camera info
@@ -322,6 +293,8 @@ bool QCameraDevice::isNull() const
*/
/*!
+ \property QCameraDevice::id
+
Returns the device id of the camera
This is a unique ID to identify the camera and may not be human-readable.
@@ -338,6 +311,8 @@ QByteArray QCameraDevice::id() const
*/
/*!
+ \property QCameraDevice::isDefault
+
Returns true if this is the default camera device.
*/
bool QCameraDevice::isDefault() const
@@ -346,6 +321,38 @@ bool QCameraDevice::isDefault() const
}
/*!
+ \since 6.7
+ \qmlproperty QtVideo::Rotation QtMultimedia::cameraDevice::correctionAngle
+
+ Returns the rotation angle needed to compensate for the physical camera rotation of the camera
+ compared to its native orientation. In other words, the property represents the clockwise angle
+ through which the output image needs to be rotated to be upright on the device screen in its
+ native orientation. Since \a correctionAngle is relative to the native orientation, this value
+ does not change with altering the device orientation (portrait/landscape). The correction angle
+ may be non-zero mostly on Android, where native and camera orientations are defined by the manufacturer.
+
+ \image camera_correctionAngle_90.png Example with 90 degrees \a correctionAngle
+*/
+
+/*!
+ \since 6.7
+ \property QCameraDevice::correctionAngle
+
+ Returns the rotation angle needed to compensate for the physical camera rotation of the camera
+ compared to its native orientation. In other words, the property represents the clockwise angle
+ through which the output image needs to be rotated to be upright on the device screen in its
+ native orientation. Since \a correctionAngle is relative to the native orientation, this value
+ does not change with altering the device orientation (portrait/landscape). The correction angle
+ may be non-zero mostly on Android, where native and camera orientations are defined by the manufacturer.
+
+ \image camera_correctionAngle_90.png Example with 90 degrees \a correctionAngle
+*/
+QtVideo::Rotation QCameraDevice::correctionAngle() const
+{
+ return d ? QtVideo::Rotation(d->orientation) : QtVideo::Rotation::None;
+}
+
+/*!
\qmlproperty string QtMultimedia::cameraDevice::description
Holds a human readable name of the camera.
@@ -354,6 +361,8 @@ bool QCameraDevice::isDefault() const
*/
/*!
+ \property QCameraDevice::description
+
Returns the human-readable description of the camera.
Use this string to present the device to the user.
@@ -392,6 +401,8 @@ QString QCameraDevice::description() const
*/
/*!
+ \property QCameraDevice::position
+
Returns the physical position of the camera on the hardware system.
*/
QCameraDevice::Position QCameraDevice::position() const
@@ -411,12 +422,14 @@ QList<QSize> QCameraDevice::photoResolutions() const
}
/*!
- \qmlproperty CameraFormat QtMultiMedia::CameraDevice::videoFormats
+ \qmlproperty CameraFormat QtMultimedia::cameraDevice::videoFormats
Holds the video formats supported by the camera.
*/
/*!
+ \property QCameraDevice::videoFormats
+
Returns the video formats supported by the camera.
*/
QList<QCameraFormat> QCameraDevice::videoFormats() const
@@ -451,3 +464,5 @@ QDebug operator<<(QDebug d, const QCameraDevice &camera)
#endif
QT_END_NAMESPACE
+
+#include "moc_qcameradevice.cpp"
diff --git a/src/multimedia/camera/qcameradevice.h b/src/multimedia/camera/qcameradevice.h
index b58a49687..2a9fac659 100644
--- a/src/multimedia/camera/qcameradevice.h
+++ b/src/multimedia/camera/qcameradevice.h
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCAMERAINFO_H
#define QCAMERAINFO_H
+#include <QtMultimedia/qtvideo.h>
#include <QtMultimedia/qvideoframe.h>
#include <QtCore/qsharedpointer.h>
@@ -85,6 +50,7 @@ class Q_MULTIMEDIA_EXPORT QCameraDevice
Q_PROPERTY(bool isDefault READ isDefault CONSTANT)
Q_PROPERTY(Position position READ position CONSTANT)
Q_PROPERTY(QList<QCameraFormat> videoFormats READ videoFormats CONSTANT)
+ Q_PROPERTY(QtVideo::Rotation correctionAngle READ correctionAngle CONSTANT)
public:
QCameraDevice();
QCameraDevice(const QCameraDevice& other);
@@ -118,6 +84,7 @@ public:
QList<QSize> photoResolutions() const;
QList<QCameraFormat> videoFormats() const;
+ QtVideo::Rotation correctionAngle() const;
// ### Add zoom and other camera information
private:
diff --git a/src/multimedia/camera/qcameradevice_p.h b/src/multimedia/camera/qcameradevice_p.h
index 6967e09fa..638cc0cae 100644
--- a/src/multimedia/camera/qcameradevice_p.h
+++ b/src/multimedia/camera/qcameradevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCAMERAINFO_P_H
#define QCAMERAINFO_P_H
@@ -60,10 +24,22 @@ QT_BEGIN_NAMESPACE
class QCameraFormatPrivate : public QSharedData
{
public:
- QVideoFrameFormat::PixelFormat pixelFormat;
+ QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid;
QSize resolution;
float minFrameRate = 0;
float maxFrameRate = 0;
+ QVideoFrameFormat::ColorRange colorRange = QVideoFrameFormat::ColorRange_Unknown;
+
+ static QVideoFrameFormat::ColorRange getColorRange(const QCameraFormat &format)
+ {
+ auto d = handle(format);
+ return d ? d->colorRange : QVideoFrameFormat::ColorRange_Unknown;
+ }
+
+ static const QCameraFormatPrivate *handle(const QCameraFormat &format)
+ {
+ return format.d.get();
+ }
QCameraFormat create() { return QCameraFormat(this); }
};
@@ -79,6 +55,18 @@ public:
QList<QSize> photoResolutions;
QList<QCameraFormat> videoFormats;
+ static const QCameraDevicePrivate *handle(const QCameraDevice &device)
+ {
+ return device.d.data();
+ }
+
+ bool operator==(const QCameraDevicePrivate &other) const
+ {
+ return id == other.id && description == other.description && isDefault == other.isDefault
+ && position == other.position && orientation == other.orientation
+ && photoResolutions == other.photoResolutions && videoFormats == other.videoFormats;
+ }
+
QCameraDevice create() { return QCameraDevice(this); }
};
diff --git a/src/multimedia/camera/qimagecapture.cpp b/src/multimedia/camera/qimagecapture.cpp
index 41c3721f2..df3ddae3f 100644
--- a/src/multimedia/camera/qimagecapture.cpp
+++ b/src/multimedia/camera/qimagecapture.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qimagecapture.h>
#include <private/qplatformimagecapture_p.h>
#include <qmediametadata.h>
@@ -118,25 +82,36 @@ QImageCapture::QImageCapture(QObject *parent)
{
Q_D(QImageCapture);
d->q_ptr = this;
- d->control = QPlatformMediaIntegration::instance()->createImageCapture(this);
-
- connect(d->control, SIGNAL(imageExposed(int)),
- this, SIGNAL(imageExposed(int)));
- connect(d->control, SIGNAL(imageCaptured(int,QImage)),
- this, SIGNAL(imageCaptured(int,QImage)));
- connect(d->control, SIGNAL(imageMetadataAvailable(int,const QMediaMetaData&)),
- this, SIGNAL(imageMetadataAvailable(int,const QMediaMetaData&)));
- connect(d->control, SIGNAL(imageAvailable(int,QVideoFrame)),
- this, SIGNAL(imageAvailable(int,QVideoFrame)));
- connect(d->control, SIGNAL(imageSaved(int,QString)),
- this, SIGNAL(imageSaved(int,QString)));
- connect(d->control, SIGNAL(readyForCaptureChanged(bool)),
- this, SIGNAL(readyForCaptureChanged(bool)));
+
+ auto maybeControl = QPlatformMediaIntegration::instance()->createImageCapture(this);
+ if (!maybeControl) {
+ qWarning() << "Failed to initialize QImageCapture" << maybeControl.error();
+ d->errorString = maybeControl.error();
+ d->error = NotReadyError;
+ return;
+ }
+
+ d->control = maybeControl.value();
+ connect(d->control, &QPlatformImageCapture::imageExposed, this, &QImageCapture::imageExposed);
+ connect(d->control, &QPlatformImageCapture::imageCaptured, this, &QImageCapture::imageCaptured);
+ connect(d->control, &QPlatformImageCapture::imageMetadataAvailable, this,
+ &QImageCapture::imageMetadataAvailable);
+ connect(d->control, &QPlatformImageCapture::imageAvailable, this,
+ &QImageCapture::imageAvailable);
+ connect(d->control, &QPlatformImageCapture::imageSaved, this, &QImageCapture::imageSaved);
+ connect(d->control, &QPlatformImageCapture::readyForCaptureChanged, this,
+ &QImageCapture::readyForCaptureChanged);
connect(d->control, SIGNAL(error(int,int,QString)),
this, SLOT(_q_error(int,int,QString)));
}
/*!
+ \fn void QImageCapture::imageMetadataAvailable(int id, const QMediaMetaData &metaData)
+
+ Signals that an image identified by \a id has \a metaData.
+*/
+
+/*!
\internal
*/
void QImageCapture::setCaptureSession(QMediaCaptureSession *session)
@@ -161,7 +136,7 @@ QImageCapture::~QImageCapture()
*/
bool QImageCapture::isAvailable() const
{
- return d_func()->captureSession && d_func()->captureSession->camera();
+ return d_func()->control && d_func()->captureSession && d_func()->captureSession->camera();
}
/*!
@@ -177,6 +152,8 @@ QMediaCaptureSession *QImageCapture::captureSession() const
}
/*!
+ \property QImageCapture::error
+
Returns the current error state.
\sa errorString()
@@ -188,6 +165,8 @@ QImageCapture::Error QImageCapture::error() const
}
/*!
+ \property QImageCapture::errorString
+
Returns a string describing the current error state.
\sa error()
@@ -219,7 +198,8 @@ void QImageCapture::setMetaData(const QMediaMetaData &metaData)
{
Q_D(QImageCapture);
d->metaData = metaData;
- d->control->setMetaData(d->metaData);
+ if (d->control)
+ d->control->setMetaData(d->metaData);
emit metaDataChanged();
}
@@ -285,14 +265,13 @@ bool QImageCapture::isReadyForCapture() const
int QImageCapture::captureToFile(const QString &file)
{
Q_D(QImageCapture);
-
- d->unsetError();
-
if (!d->control) {
- d->_q_error(-1, NotSupportedFeatureError, QPlatformImageCapture::msgCameraNotReady());
+ d->_q_error(-1, d->error, d->errorString);
return -1;
}
+ d->unsetError();
+
if (!isReadyForCapture()) {
d->_q_error(-1, NotReadyError, tr("Could not capture in stopped state"));
return -1;
@@ -316,18 +295,13 @@ int QImageCapture::captureToFile(const QString &file)
int QImageCapture::capture()
{
Q_D(QImageCapture);
-
- d->unsetError();
-
- if (d->control)
+ if (!d->control) {
+ d->_q_error(-1, d->error, d->errorString);
+ return -1;
+ } else {
+ d->unsetError();
return d->control->captureToBuffer();
-
- d->error = NotSupportedFeatureError;
- d->errorString = tr("Device does not support images capture.");
-
- d->_q_error(-1, d->error, d->errorString);
-
- return -1;
+ }
}
/*!
@@ -375,6 +349,20 @@ int QImageCapture::capture()
*/
/*!
+ \enum QImageCapture::FileFormat
+
+ Choose one of the following image formats:
+
+ \value UnspecifiedFormat No format specified
+ \value JPEG \c .jpg or \c .jpeg format
+ \value PNG \c .png format
+ \value WebP \c .webp format
+ \value Tiff \c .tiff format
+ \omitvalue LastFileFormat
+*/
+
+
+/*!
\property QImageCapture::fileFormat
\brief The image format.
*/
@@ -382,9 +370,7 @@ int QImageCapture::capture()
QImageCapture::FileFormat QImageCapture::fileFormat() const
{
Q_D(const QImageCapture);
- if (!d->control)
- return UnspecifiedFormat;
- return d->control->imageSettings().format();
+ return d->control ? d->control->imageSettings().format() : UnspecifiedFormat;
}
/*!
@@ -403,11 +389,19 @@ void QImageCapture::setFileFormat(QImageCapture::FileFormat format)
emit fileFormatChanged();
}
+/*!
+ Returns a list of supported file formats.
+
+ \sa {QImageCapture::}{FileFormat}
+*/
QList<QImageCapture::FileFormat> QImageCapture::supportedFormats()
{
return QPlatformMediaIntegration::instance()->formatInfo()->imageFormats;
}
+/*!
+ Returns the name of the given format, \a f.
+*/
QString QImageCapture::fileFormatName(QImageCapture::FileFormat f)
{
const char *name = nullptr;
@@ -431,6 +425,9 @@ QString QImageCapture::fileFormatName(QImageCapture::FileFormat f)
return QString::fromUtf8(name);
}
+/*!
+ Returns the description of the given file format, \a f.
+*/
QString QImageCapture::fileFormatDescription(QImageCapture::FileFormat f)
{
const char *name = nullptr;
@@ -461,12 +458,16 @@ QString QImageCapture::fileFormatDescription(QImageCapture::FileFormat f)
QSize QImageCapture::resolution() const
{
Q_D(const QImageCapture);
- if (!d->control)
- return QSize();
- return d->control->imageSettings().resolution();
+ return d->control ? d->control->imageSettings().resolution() : QSize{};
}
/*!
+ \fn void QImageCapture::resolutionChanged()
+
+ Signals when the image resolution changes.
+*/
+
+/*!
Sets the \a resolution of the encoded image.
An empty QSize indicates the encoder should make an optimal choice based on
@@ -514,9 +515,7 @@ void QImageCapture::setResolution(int width, int height)
QImageCapture::Quality QImageCapture::quality() const
{
Q_D(const QImageCapture);
- if (!d->control)
- return NormalQuality;
- return d->control->imageSettings().quality();
+ return d->control ? d->control->imageSettings().quality() : NormalQuality;
}
/*!
diff --git a/src/multimedia/camera/qimagecapture.h b/src/multimedia/camera/qimagecapture.h
index 5d49c51d3..48608792a 100644
--- a/src/multimedia/camera/qimagecapture.h
+++ b/src/multimedia/camera/qimagecapture.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCAMERAIMAGECAPTURE_H
#define QCAMERAIMAGECAPTURE_H
diff --git a/src/multimedia/compat/removed_api.cpp b/src/multimedia/compat/removed_api.cpp
new file mode 100644
index 000000000..567024c8e
--- /dev/null
+++ b/src/multimedia/compat/removed_api.cpp
@@ -0,0 +1,16 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#define QT_MULTIMEDIA_BUILD_REMOVED_API
+
+#include <QtMultimedia/qtmultimediaglobal.h>
+
+QT_USE_NAMESPACE
+
+#if QT_MULTIMEDIA_REMOVED_SINCE(6, 7)
+
+// #include "qotherheader.h"
+// // implement removed functions from qotherheader.h
+// order sections alphabetically
+
+#endif // QT_MULTIMEDIA_REMOVED_SINCE(6, 7)
diff --git a/src/multimedia/configure.cmake b/src/multimedia/configure.cmake
index efcadfc5c..5fe25f172 100644
--- a/src/multimedia/configure.cmake
+++ b/src/multimedia/configure.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#### Inputs
@@ -19,10 +22,14 @@ qt_find_package(MMRendererCore PROVIDED_TARGETS MMRendererCore::MMRendererCore M
qt_find_package(MMRenderer PROVIDED_TARGETS MMRenderer::MMRenderer MODULE_NAME multimedia QMAKE_LIB mmrndclient)
qt_find_package(WrapPulseAudio PROVIDED_TARGETS WrapPulseAudio::WrapPulseAudio MODULE_NAME multimedia QMAKE_LIB pulseaudio)
qt_find_package(WMF PROVIDED_TARGETS WMF::WMF MODULE_NAME multimedia QMAKE_LIB wmf)
-qt_find_package(EGL)
+if(TARGET EGL::EGL)
+ qt_internal_disable_find_package_global_promotion(EGL::EGL)
+endif()
+qt_find_package(EGL PROVIDED_TARGETS EGL::EGL)
+
-qt_find_package(FFmpeg OPTIONAL_COMPONENTS AVCODEC AVFORMAT AVUTIL AVDEVICE SWRESAMPLE SWSCALE PROVIDED_TARGETS FFmpeg::avcodec FFmpeg::avformat FFmpeg::avutil FFmpeg::avdevice FFmpeg::swresample FFmpeg::swscale MODULE_NAME multimedia QMAKE_LIB ffmpeg)
-qt_find_package(VAAPI COMPONENTS VA DRM PROVIDED_TARGETS VAAPI::VA VAAPI::DRM MODULE_NAME multimedia QMAKE_LIB vaapi)
+qt_find_package(FFmpeg OPTIONAL_COMPONENTS AVCODEC AVFORMAT AVUTIL SWRESAMPLE SWSCALE PROVIDED_TARGETS FFmpeg::avcodec FFmpeg::avformat FFmpeg::avutil FFmpeg::swresample FFmpeg::swscale MODULE_NAME multimedia QMAKE_LIB ffmpeg)
+qt_find_package(VAAPI COMPONENTS VA DRM PROVIDED_TARGETS VAAPI::VAAPI MODULE_NAME multimedia QMAKE_LIB vaapi)
#### Tests
@@ -99,11 +106,6 @@ qt_feature("evr" PUBLIC PRIVATE
qt_feature("gstreamer_1_0" PRIVATE
LABEL "GStreamer 1.0"
CONDITION GStreamer_FOUND
- ENABLE INPUT_gstreamer STREQUAL 'yes'
- DISABLE INPUT_gstreamer STREQUAL 'no'
-)
-qt_feature("gstreamer" PRIVATE
- CONDITION QT_FEATURE_gstreamer_1_0
)
qt_feature("gstreamer_app" PRIVATE
LABEL "GStreamer App"
@@ -115,7 +117,13 @@ qt_feature("gstreamer_photography" PRIVATE
)
qt_feature("gstreamer_gl" PRIVATE
LABEL "GStreamer OpenGL"
- CONDITION QT_FEATURE_opengl AND QT_FEATURE_gstreamer_1_0 AND GStreamer_Gl_FOUND
+ CONDITION QT_FEATURE_opengl AND QT_FEATURE_gstreamer_1_0 AND GStreamer_Gl_FOUND AND EGL_FOUND
+)
+qt_feature("gstreamer" PRIVATE
+ LABEL "QtMM GStreamer plugin"
+ CONDITION (QT_FEATURE_gstreamer_1_0 AND QT_FEATURE_gstreamer_app)
+ ENABLE INPUT_gstreamer STREQUAL 'yes'
+ DISABLE INPUT_gstreamer STREQUAL 'no'
)
qt_feature("gpu_vivante" PRIVATE
diff --git a/src/multimedia/darwin/qcoreaudiosessionmanager.mm b/src/multimedia/darwin/qcoreaudiosessionmanager.mm
index 264935d74..cee955905 100644
--- a/src/multimedia/darwin/qcoreaudiosessionmanager.mm
+++ b/src/multimedia/darwin/qcoreaudiosessionmanager.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcoreaudiosessionmanager_p.h"
#import <AVFoundation/AVAudioSession.h>
diff --git a/src/multimedia/darwin/qcoreaudiosessionmanager_p.h b/src/multimedia/darwin/qcoreaudiosessionmanager_p.h
index 7b2a5294f..a6dfe88f4 100644
--- a/src/multimedia/darwin/qcoreaudiosessionmanager_p.h
+++ b/src/multimedia/darwin/qcoreaudiosessionmanager_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef IOSAUDIOSESSIONMANAGER_H
#define IOSAUDIOSESSIONMANAGER_H
diff --git a/src/multimedia/darwin/qcoreaudioutils.mm b/src/multimedia/darwin/qcoreaudioutils.mm
index c8ee541cf..46f98dcc9 100644
--- a/src/multimedia/darwin/qcoreaudioutils.mm
+++ b/src/multimedia/darwin/qcoreaudioutils.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcoreaudioutils_p.h"
#include <qdebug.h>
@@ -260,7 +224,9 @@ QAudioFormat::ChannelConfig CoreAudioUtils::fromAudioChannelLayout(const AudioCh
quint32 channels = 0;
if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
// special case 1 and 2 channel configs, as they are often reported without proper descriptions
- if (layout->mNumberChannelDescriptions == 1 && layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Unknown)
+ if (layout->mNumberChannelDescriptions == 1
+ && (layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Unknown
+ || layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Mono))
return QAudioFormat::ChannelConfigMono;
if (layout->mNumberChannelDescriptions == 2 &&
layout->mChannelDescriptions[0].mChannelLabel == kAudioChannelLabel_Unknown &&
@@ -268,19 +234,27 @@ QAudioFormat::ChannelConfig CoreAudioUtils::fromAudioChannelLayout(const AudioCh
return QAudioFormat::ChannelConfigStereo;
for (uint i = 0; i < layout->mNumberChannelDescriptions; ++i) {
- bool found = false;
- for (const auto &m : channelMap) {
- if (layout->mChannelDescriptions[i].mChannelLabel == m.label) {
- channels |= QAudioFormat::channelConfig(m.pos);
- found = true;
- break;
- }
+ const auto channelLabel = layout->mChannelDescriptions[i].mChannelLabel;
+ if (channelLabel == kAudioChannelLabel_Unknown) {
+ // Any number of unknown channel labels occurs for loopback audio devices.
+ // E.g. the case is reproduced with installed software Soundflower.
+ continue;
}
- if (!found)
- qWarning() << "audio device has unknown channel" << layout->mChannelDescriptions[i].mChannelLabel;
+
+ const auto found = std::find_if(channelMap, std::end(channelMap),
+ [channelLabel](const auto &labelWithPos) {
+ return labelWithPos.label == channelLabel;
+ });
+
+ if (found == std::end(channelMap))
+ qWarning() << "audio device has unrecognized channel, index:" << i
+ << "label:" << channelLabel;
+ else
+ channels |= QAudioFormat::channelConfig(found->pos);
}
} else {
- qWarning() << "Channel layout uses unimplemented format";
+ qWarning() << "Channel layout uses unimplemented format, channelLayoutTag:"
+ << layout->mChannelLayoutTag;
}
return QAudioFormat::ChannelConfig(channels);
}
diff --git a/src/multimedia/darwin/qcoreaudioutils_p.h b/src/multimedia/darwin/qcoreaudioutils_p.h
index b6730c978..94b6298f3 100644
--- a/src/multimedia/darwin/qcoreaudioutils_p.h
+++ b/src/multimedia/darwin/qcoreaudioutils_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef IOSAUDIOUTILS_H
#define IOSAUDIOUTILS_H
@@ -67,7 +31,7 @@ public:
static AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat);
// ownership is transferred to caller, free with ::free()
- static std::unique_ptr<AudioChannelLayout> toAudioChannelLayout(const QAudioFormat &format, UInt32 *size);
+ static Q_MULTIMEDIA_EXPORT std::unique_ptr<AudioChannelLayout> toAudioChannelLayout(const QAudioFormat &format, UInt32 *size);
static QAudioFormat::ChannelConfig fromAudioChannelLayout(const AudioChannelLayout *layout);
private:
diff --git a/src/multimedia/darwin/qdarwinaudiodevice.mm b/src/multimedia/darwin/qdarwinaudiodevice.mm
index e23c087f8..8374f4cea 100644
--- a/src/multimedia/darwin/qdarwinaudiodevice.mm
+++ b/src/multimedia/darwin/qdarwinaudiodevice.mm
@@ -1,48 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdarwinaudiodevice_p.h"
#include "qcoreaudioutils_p.h"
#include <private/qcore_mac_p.h>
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
-# include "qcoreaudiosessionmanager_p.h"
+#if defined(Q_OS_IOS)
+#include "qcoreaudiosessionmanager_p.h"
+#else
+#include "qmacosaudiodatautils_p.h"
#endif
#include <QtCore/QDataStream>
@@ -72,50 +38,29 @@ QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(const QByteArray &device, QAudioDevic
}
-
QAudioFormat QCoreAudioDeviceInfo::determinePreferredFormat() const
{
QAudioFormat format;
#if defined(Q_OS_MACOS)
- UInt32 propSize = 0;
- AudioObjectPropertyScope audioDevicePropertyScope = mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
- AudioObjectPropertyAddress audioDevicePropertyStreamsAddress = { kAudioDevicePropertyStreams,
- audioDevicePropertyScope,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyDataSize(m_deviceId, &audioDevicePropertyStreamsAddress, 0, NULL, &propSize) == noErr) {
-
- const int sc = propSize / sizeof(AudioStreamID);
-
- if (sc > 0) {
- AudioStreamID* streams = new AudioStreamID[sc];
-
- if (AudioObjectGetPropertyData(m_deviceId, &audioDevicePropertyStreamsAddress, 0, NULL, &propSize, streams) == noErr) {
-
- AudioObjectPropertyAddress audioDevicePhysicalFormatPropertyAddress = { kAudioStreamPropertyPhysicalFormat,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- for (int i = 0; i < sc; ++i) {
- if (AudioObjectGetPropertyDataSize(streams[i], &audioDevicePhysicalFormatPropertyAddress, 0, NULL, &propSize) == noErr) {
- AudioStreamBasicDescription sf;
-
- if (AudioObjectGetPropertyData(streams[i], &audioDevicePhysicalFormatPropertyAddress, 0, NULL, &propSize, &sf) == noErr) {
- format = CoreAudioUtils::toQAudioFormat(sf);
- break;
- } else {
- qWarning() << "QAudioDevice: Unable to find perferedFormat for stream";
- }
- } else {
- qWarning() << "QAudioDevice: Unable to find size of perferedFormat for stream";
- }
- }
+ const auto audioDevicePropertyStreamsAddress =
+ makePropertyAddress(kAudioDevicePropertyStreams, mode);
+
+ if (auto streamIDs = getAudioData<AudioStreamID>(m_deviceId, audioDevicePropertyStreamsAddress,
+ "propertyStreams")) {
+ const auto audioDevicePhysicalFormatPropertyAddress =
+ makePropertyAddress(kAudioStreamPropertyPhysicalFormat, mode);
+
+ for (auto streamID : *streamIDs) {
+ if (auto streamDescription = getAudioObject<AudioStreamBasicDescription>(
+ streamID, audioDevicePhysicalFormatPropertyAddress,
+ "prefferedPhysicalFormat")) {
+ format = CoreAudioUtils::toQAudioFormat(*streamDescription);
+ break;
}
-
- delete[] streams;
}
}
+
if (!format.isValid())
#endif
{
@@ -132,22 +77,14 @@ QAudioFormat QCoreAudioDeviceInfo::determinePreferredFormat() const
QString QCoreAudioDeviceInfo::getDescription() const
{
#ifdef Q_OS_MACOS
- CFStringRef name;
- UInt32 size = sizeof(CFStringRef);
- AudioObjectPropertyScope audioPropertyScope = mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
-
- AudioObjectPropertyAddress audioDeviceNamePropertyAddress = { kAudioObjectPropertyName,
- audioPropertyScope,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyData(m_deviceId, &audioDeviceNamePropertyAddress, 0, NULL, &size, &name) != noErr) {
- qWarning() << "QAudioDevice: Unable to find device description";
- return QString();
+ const auto propertyAddress = makePropertyAddress(kAudioObjectPropertyName, mode);
+ if (auto name =
+ getAudioObject<CFStringRef>(m_deviceId, propertyAddress, "Device Description")) {
+ auto deleter = qScopeGuard([&name]() { CFRelease(*name); });
+ return QString::fromCFString(*name);
}
- QString s = QString::fromCFString(name);
- CFRelease(name);
- return s;
+ return {};
#else
return QString::fromUtf8(id);
#endif
@@ -156,16 +93,12 @@ QString QCoreAudioDeviceInfo::getDescription() const
void QCoreAudioDeviceInfo::getChannelLayout()
{
#ifdef Q_OS_MACOS
- AudioObjectPropertyAddress audioDeviceChannelLayoutPropertyAddress = { kAudioDevicePropertyPreferredChannelLayout,
- (mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
- kAudioObjectPropertyElementMaster };
- UInt32 propSize;
- if (AudioObjectGetPropertyDataSize(m_deviceId, &audioDeviceChannelLayoutPropertyAddress, 0, nullptr, &propSize) == noErr) {
- AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propSize));
- if (AudioObjectGetPropertyData(m_deviceId, &audioDeviceChannelLayoutPropertyAddress, 0, nullptr, &propSize, layout) == noErr) {
- channelConfiguration = CoreAudioUtils::fromAudioChannelLayout(layout);
- }
- free(layout);
+ const auto propertyAddress =
+ makePropertyAddress(kAudioDevicePropertyPreferredChannelLayout, mode);
+ if (auto data = getAudioData<char>(m_deviceId, propertyAddress, "prefferedChannelLayout",
+ sizeof(AudioChannelLayout))) {
+ const auto *layout = reinterpret_cast<const AudioChannelLayout *>(data->data());
+ channelConfiguration = CoreAudioUtils::fromAudioChannelLayout(layout);
}
#else
channelConfiguration = (mode == QAudioDevice::Input) ? QAudioFormat::ChannelConfigMono : QAudioFormat::ChannelConfigStereo;
diff --git a/src/multimedia/darwin/qdarwinaudiodevice_p.h b/src/multimedia/darwin/qdarwinaudiodevice_p.h
index ab6758118..637c5d197 100644
--- a/src/multimedia/darwin/qdarwinaudiodevice_p.h
+++ b/src/multimedia/darwin/qdarwinaudiodevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef IOSAUDIODEVICEINFO_H
#define IOSAUDIODEVICEINFO_H
diff --git a/src/multimedia/darwin/qdarwinaudiosink.mm b/src/multimedia/darwin/qdarwinaudiosink.mm
index 49443d234..9242bb2f0 100644
--- a/src/multimedia/darwin/qdarwinaudiosink.mm
+++ b/src/multimedia/darwin/qdarwinaudiosink.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdarwinaudiosink_p.h"
#include "qcoreaudiosessionmanager_p.h"
#include "qdarwinaudiodevice_p.h"
@@ -49,7 +13,7 @@
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
# include <AudioUnit/AudioComponent.h>
#endif
@@ -59,24 +23,29 @@
QT_BEGIN_NAMESPACE
-QDarwinAudioSinkBuffer::QDarwinAudioSinkBuffer(int bufferSize, int maxPeriodSize, const QAudioFormat &audioFormat)
- : m_deviceError(false)
- , m_maxPeriodSize(maxPeriodSize)
- , m_device(0)
+static int audioRingBufferSize(int bufferSize, int maxPeriodSize)
{
- m_buffer = new CoreAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
- m_bytesPerFrame = audioFormat.bytesPerFrame();
- m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.sampleRate();
-
- m_fillTimer = new QTimer(this);
- connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer()));
+ // TODO: review this code
+ return bufferSize
+ + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize));
}
-QDarwinAudioSinkBuffer::~QDarwinAudioSinkBuffer()
+QDarwinAudioSinkBuffer::QDarwinAudioSinkBuffer(int bufferSize, int maxPeriodSize,
+ const QAudioFormat &audioFormat)
+ : m_maxPeriodSize(maxPeriodSize),
+ m_bytesPerFrame(audioFormat.bytesPerFrame()),
+ m_periodTime(maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.sampleRate()),
+ m_buffer(
+ std::make_unique<CoreAudioRingBuffer>(audioRingBufferSize(bufferSize, maxPeriodSize)))
{
- delete m_buffer;
+ m_fillTimer = new QTimer(this);
+ m_fillTimer->setTimerType(Qt::PreciseTimer);
+ m_fillTimer->setInterval(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime);
+ connect(m_fillTimer, &QTimer::timeout, this, &QDarwinAudioSinkBuffer::fillBuffer);
}
+QDarwinAudioSinkBuffer::~QDarwinAudioSinkBuffer() = default;
+
qint64 QDarwinAudioSinkBuffer::readFrames(char *data, qint64 maxFrames)
{
bool wecan = true;
@@ -137,31 +106,46 @@ int QDarwinAudioSinkBuffer::available() const
return m_buffer->free();
}
+bool QDarwinAudioSinkBuffer::deviceAtEnd() const
+{
+ return m_deviceAtEnd;
+}
+
void QDarwinAudioSinkBuffer::reset()
{
m_buffer->reset();
- m_device = 0;
- m_deviceError = false;
+ setFillingEnabled(false);
+ setPrefetchDevice(nullptr);
}
void QDarwinAudioSinkBuffer::setPrefetchDevice(QIODevice *device)
{
- if (m_device != device) {
- m_device = device;
- if (m_device != 0)
- fillBuffer();
- }
+ if (std::exchange(m_device, device) == device)
+ return;
+
+ m_deviceError = false;
+ m_deviceAtEnd = device && device->atEnd();
+ const auto wasFillingEnabled = m_fillingEnabled;
+ setFillingEnabled(false);
+ setFillingEnabled(wasFillingEnabled);
}
-void QDarwinAudioSinkBuffer::startFillTimer()
+QIODevice *QDarwinAudioSinkBuffer::prefetchDevice() const
{
- if (m_device != 0)
- m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime);
+ return m_device;
}
-void QDarwinAudioSinkBuffer::stopFillTimer()
+void QDarwinAudioSinkBuffer::setFillingEnabled(bool enabled)
{
- m_fillTimer->stop();
+ if (std::exchange(m_fillingEnabled, enabled) == enabled)
+ return;
+
+ if (!enabled)
+ m_fillTimer->stop();
+ else if (m_device) {
+ fillBuffer();
+ m_fillTimer->start();
+ }
}
void QDarwinAudioSinkBuffer::fillBuffer()
@@ -178,12 +162,13 @@ void QDarwinAudioSinkBuffer::fillBuffer()
if (region.second > 0) {
region.second = m_device->read(region.first, region.second);
+ m_deviceAtEnd = m_device->atEnd();
if (region.second > 0)
filled += region.second;
else if (region.second == 0)
wecan = false;
else if (region.second < 0) {
- m_fillTimer->stop();
+ setFillingEnabled(false);
region.second = 0;
m_deviceError = true;
}
@@ -219,8 +204,8 @@ qint64 QDarwinAudioSinkDevice::writeData(const char *data, qint64 len)
return m_audioBuffer->writeBytes(data, len);
}
-QDarwinAudioSink::QDarwinAudioSink(const QAudioDevice &device)
- : m_audioDeviceInfo(device)
+QDarwinAudioSink::QDarwinAudioSink(const QAudioDevice &device, QObject *parent)
+ : QPlatformAudioSink(parent), m_audioDeviceInfo(device), m_stateMachine(*this)
{
QAudioDevice di = device;
if (di.isNull())
@@ -233,7 +218,8 @@ QDarwinAudioSink::QDarwinAudioSink(const QAudioDevice &device)
m_device = di.id();
m_clockFrequency = CoreAudioUtils::frequency() / 1000;
- m_audioThreadState.storeRelaxed(Stopped);
+
+ connect(this, &QDarwinAudioSink::stateChanged, this, &QDarwinAudioSink::updateAudioDevice);
}
QDarwinAudioSink::~QDarwinAudioSink()
@@ -243,106 +229,77 @@ QDarwinAudioSink::~QDarwinAudioSink()
void QDarwinAudioSink::start(QIODevice *device)
{
- QIODevice* op = device;
+ reset();
if (!m_audioDeviceInfo.isFormatSupported(m_audioFormat) || !open()) {
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::OpenError;
+ m_stateMachine.setError(QAudio::OpenError);
return;
}
- reset();
- m_audioBuffer->reset();
- m_audioBuffer->setPrefetchDevice(op);
-
- if (op == 0) {
- op = m_audioIO;
- m_stateCode = QAudio::IdleState;
+ if (!device) {
+ m_stateMachine.setError(QAudio::IOError);
+ return;
}
- else
- m_stateCode = QAudio::ActiveState;
- // Start
+ m_audioBuffer->setPrefetchDevice(device);
+
m_pullMode = true;
- m_errorCode = QAudio::NoError;
m_totalFrames = 0;
- if (m_stateCode == QAudio::ActiveState)
- audioThreadStart();
-
- emit stateChanged(m_stateCode);
+ m_stateMachine.start();
}
QIODevice *QDarwinAudioSink::start()
{
+ reset();
+
if (!m_audioDeviceInfo.isFormatSupported(m_audioFormat) || !open()) {
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::OpenError;
+ m_stateMachine.setError(QAudio::OpenError);
return m_audioIO;
}
- reset();
- m_audioBuffer->reset();
- m_audioBuffer->setPrefetchDevice(0);
-
- m_stateCode = QAudio::IdleState;
-
- // Start
m_pullMode = false;
- m_errorCode = QAudio::NoError;
m_totalFrames = 0;
- emit stateChanged(m_stateCode);
+ m_stateMachine.start(false);
return m_audioIO;
}
void QDarwinAudioSink::stop()
{
- if (m_stateCode == QAudio::StoppedState)
- return;
+ if (m_audioBuffer)
+ m_audioBuffer->setFillingEnabled(false);
+
+ if (auto notifier = m_stateMachine.stop(QAudio::NoError, true)) {
+ Q_ASSERT((notifier.prevAudioState() == QAudio::ActiveState) == notifier.isDraining());
+ if (notifier.isDraining() && !m_drainSemaphore.tryAcquire(1, 500)) {
+ const bool wasDraining = m_stateMachine.onDrained();
- audioThreadDrain();
+ qWarning() << "Failed wait for getting sink drained; was draining:" << wasDraining;
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
+ // handle a rare corner case when the audio thread managed to release the semaphore in
+ // the time window between tryAcquire and onDrained
+ if (!wasDraining)
+ m_drainSemaphore.acquire();
+ }
+ }
}
void QDarwinAudioSink::reset()
{
- if (m_stateCode == QAudio::StoppedState)
- return;
-
- audioThreadStop();
-
- m_stateCode = QAudio::StoppedState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
+ onAudioDeviceDrained();
+ m_stateMachine.stopOrUpdateError();
}
void QDarwinAudioSink::suspend()
{
- if (m_stateCode != QAudio::ActiveState && m_stateCode != QAudio::IdleState)
- return;
-
- audioThreadStop();
-
- m_stateCode = QAudio::SuspendedState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
+ m_stateMachine.suspend();
}
void QDarwinAudioSink::resume()
{
- if (m_stateCode != QAudio::SuspendedState)
- return;
-
- audioThreadStart();
-
- m_stateCode = m_pullMode ? QAudio::ActiveState : QAudio::IdleState;
- m_errorCode = QAudio::NoError;
- emit stateChanged(m_stateCode);
+ m_stateMachine.resume();
}
qsizetype QDarwinAudioSink::bytesFree() const
@@ -352,7 +309,7 @@ qsizetype QDarwinAudioSink::bytesFree() const
void QDarwinAudioSink::setBufferSize(qsizetype value)
{
- if (m_stateCode == QAudio::StoppedState)
+ if (state() == QAudio::StoppedState)
m_internalBufferSize = value;
}
@@ -368,17 +325,17 @@ qint64 QDarwinAudioSink::processedUSecs() const
QAudio::Error QDarwinAudioSink::error() const
{
- return m_errorCode;
+ return m_stateMachine.error();
}
QAudio::State QDarwinAudioSink::state() const
{
- return m_stateCode;
+ return m_stateMachine.state();
}
void QDarwinAudioSink::setFormat(const QAudioFormat &format)
{
- if (m_stateCode == QAudio::StoppedState)
+ if (state() == QAudio::StoppedState)
m_audioFormat = format;
}
@@ -393,7 +350,7 @@ void QDarwinAudioSink::setVolume(qreal volume)
if (!m_isOpen)
return;
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
//on OS X the volume can be set directly on the AudioUnit
if (AudioUnitSetParameter(m_audioUnit,
kHALOutputParam_Volume,
@@ -410,22 +367,9 @@ qreal QDarwinAudioSink::volume() const
return m_cachedVolume;
}
-void QDarwinAudioSink::deviceStopped()
-{
- emit stateChanged(m_stateCode);
-}
-
void QDarwinAudioSink::inputReady()
{
- if (m_stateCode != QAudio::IdleState)
- return;
-
- audioThreadStart();
-
- m_stateCode = QAudio::ActiveState;
- m_errorCode = QAudio::NoError;
-
- emit stateChanged(m_stateCode);
+ m_stateMachine.activateFromIdle();
}
OSStatus QDarwinAudioSink::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
@@ -437,15 +381,15 @@ OSStatus QDarwinAudioSink::renderCallback(void *inRefCon, AudioUnitRenderActionF
QDarwinAudioSink* d = static_cast<QDarwinAudioSink*>(inRefCon);
- const int threadState = d->m_audioThreadState.fetchAndAddAcquire(0);
- if (threadState == Stopped) {
+ const auto [drained, stopped] = d->m_stateMachine.getDrainedAndStopped();
+
+ if (drained && stopped) {
ioData->mBuffers[0].mDataByteSize = 0;
- d->audioDeviceStop();
- }
- else {
+ } else {
const UInt32 bytesPerFrame = d->m_streamFormat.mBytesPerFrame;
qint64 framesRead;
+ Q_ASSERT(ioData->mBuffers[0].mDataByteSize / bytesPerFrame == inNumberFrames);
framesRead = d->m_audioBuffer->readFrames((char*)ioData->mBuffers[0].mData,
ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
@@ -454,8 +398,7 @@ OSStatus QDarwinAudioSink::renderCallback(void *inRefCon, AudioUnitRenderActionF
d->m_totalFrames += framesRead;
#if defined(Q_OS_MACOS)
- // If playback is already stopped.
- if (threadState != Running) {
+ if (stopped) {
qreal oldVolume = d->m_cachedVolume;
// Decrease volume smoothly.
d->setVolume(d->m_volume / 2);
@@ -475,14 +418,10 @@ OSStatus QDarwinAudioSink::renderCallback(void *inRefCon, AudioUnitRenderActionF
}
else {
ioData->mBuffers[0].mDataByteSize = 0;
- if (framesRead == 0) {
- if (threadState == Draining)
- d->audioDeviceStop();
- else
- d->audioDeviceIdle();
- }
+ if (framesRead == 0)
+ d->onAudioDeviceIdle();
else
- d->audioDeviceError();
+ d->onAudioDeviceError();
}
}
@@ -498,7 +437,7 @@ bool QDarwinAudioSink::open()
CoreAudioSessionManager::instance().setActive(true);
#endif
- if (m_errorCode != QAudio::NoError)
+ if (error() != QAudio::NoError)
return false;
if (m_isOpen) {
@@ -508,7 +447,7 @@ bool QDarwinAudioSink::open()
AudioComponentDescription componentDescription;
componentDescription.componentType = kAudioUnitType_Output;
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
#else
componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
@@ -543,7 +482,7 @@ bool QDarwinAudioSink::open()
return false;
}
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
//Set Audio Device
if (AudioUnitSetProperty(m_audioUnit,
kAudioOutputUnitProperty_CurrentDevice,
@@ -574,7 +513,7 @@ bool QDarwinAudioSink::open()
// Allocate buffer
UInt32 numberOfFrames = 0;
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
size = sizeof(UInt32);
if (AudioUnitGetProperty(m_audioUnit,
kAudioDevicePropertyBufferFrameSize,
@@ -597,10 +536,12 @@ bool QDarwinAudioSink::open()
else
m_internalBufferSize -= m_internalBufferSize % m_streamFormat.mBytesPerFrame;
- m_audioBuffer = new QDarwinAudioSinkBuffer(m_internalBufferSize, m_periodSizeBytes, m_audioFormat);
- connect(m_audioBuffer, SIGNAL(readyRead()), SLOT(inputReady())); //Pull
+ m_audioBuffer = std::make_unique<QDarwinAudioSinkBuffer>(m_internalBufferSize,
+ m_periodSizeBytes, m_audioFormat);
+ connect(m_audioBuffer.get(), &QDarwinAudioSinkBuffer::readyRead, this,
+ &QDarwinAudioSink::inputReady); // Pull
- m_audioIO = new QDarwinAudioSinkDevice(m_audioBuffer, this);
+ m_audioIO = new QDarwinAudioSinkDevice(m_audioBuffer.get(), this);
//Init
if (AudioUnitInitialize(m_audioUnit)) {
@@ -618,77 +559,49 @@ bool QDarwinAudioSink::open()
void QDarwinAudioSink::close()
{
if (m_audioUnit != 0) {
- AudioOutputUnitStop(m_audioUnit);
+ m_stateMachine.stop();
+
AudioUnitUninitialize(m_audioUnit);
AudioComponentInstanceDispose(m_audioUnit);
+ m_audioUnit = 0;
}
- delete m_audioBuffer;
-}
-
-void QDarwinAudioSink::audioThreadStart()
-{
- QMutexLocker lock(&m_mutex);
- startTimers();
- m_audioThreadState.storeRelaxed(Running);
- AudioOutputUnitStart(m_audioUnit);
+ m_audioBuffer.reset();
}
-void QDarwinAudioSink::audioThreadStop()
+void QDarwinAudioSink::onAudioDeviceIdle()
{
- QMutexLocker lock(&m_mutex);
- stopTimers();
- if (m_audioThreadState.testAndSetAcquire(Running, Stopped))
- m_threadFinished.wait(&m_mutex, 500);
+ const bool atEnd = m_audioBuffer->deviceAtEnd();
+ if (!m_stateMachine.updateActiveOrIdle(false, atEnd ? QAudio::NoError : QAudio::UnderrunError))
+ onAudioDeviceDrained();
}
-void QDarwinAudioSink::audioThreadDrain()
+void QDarwinAudioSink::onAudioDeviceDrained()
{
- QMutexLocker lock(&m_mutex);
- stopTimers();
- if (m_audioThreadState.testAndSetAcquire(Running, Draining))
- m_threadFinished.wait(&m_mutex, 500);
+ if (m_stateMachine.onDrained())
+ m_drainSemaphore.release();
}
-void QDarwinAudioSink::audioDeviceStop()
+void QDarwinAudioSink::onAudioDeviceError()
{
- AudioOutputUnitStop(m_audioUnit);
- m_audioThreadState.storeRelaxed(Stopped);
- m_threadFinished.wakeOne();
-}
-
-void QDarwinAudioSink::audioDeviceIdle()
-{
- if (m_stateCode != QAudio::ActiveState)
- return;
-
- audioDeviceStop();
-
- m_errorCode = QAudio::UnderrunError;
- m_stateCode = QAudio::IdleState;
- QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
+ m_stateMachine.stop(QAudio::IOError);
}
-void QDarwinAudioSink::audioDeviceError()
+void QDarwinAudioSink::updateAudioDevice()
{
- if (m_stateCode != QAudio::ActiveState)
- return;
+ const auto state = m_stateMachine.state();
- audioDeviceStop();
-
- m_errorCode = QAudio::IOError;
- m_stateCode = QAudio::StoppedState;
- QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
-}
+ Q_ASSERT(m_audioBuffer);
+ Q_ASSERT(m_audioUnit);
-void QDarwinAudioSink::startTimers()
-{
- m_audioBuffer->startFillTimer();
-}
+ if (state == QAudio::StoppedState)
+ m_audioBuffer->reset();
+ else
+ m_audioBuffer->setFillingEnabled(state != QAudio::SuspendedState);
-void QDarwinAudioSink::stopTimers()
-{
- m_audioBuffer->stopFillTimer();
+ const bool unitStarted = state == QAudio::ActiveState;
+ if (std::exchange(m_audioUnitStarted, unitStarted) != unitStarted)
+ (unitStarted ? AudioOutputUnitStart : AudioOutputUnitStop)(m_audioUnit);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/darwin/qdarwinaudiosink_p.h b/src/multimedia/darwin/qdarwinaudiosink_p.h
index bc72c50a1..1fddcb205 100644
--- a/src/multimedia/darwin/qdarwinaudiosink_p.h
+++ b/src/multimedia/darwin/qdarwinaudiosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef IOSAUDIOOUTPUT_H
#define IOSAUDIOOUTPUT_H
@@ -51,17 +15,17 @@
//
#include <private/qaudiosystem_p.h>
+#include <private/qaudiostatemachine_p.h>
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
# include <CoreAudio/CoreAudio.h>
#endif
#include <AudioUnit/AudioUnit.h>
#include <CoreAudio/CoreAudioTypes.h>
#include <QtCore/QIODevice>
-#include <QtCore/QWaitCondition>
-#include <QtCore/QMutex>
#include <qdarwinaudiodevice_p.h>
+#include <qsemaphore.h>
QT_BEGIN_NAMESPACE
@@ -82,12 +46,16 @@ public:
qint64 writeBytes(const char *data, qint64 maxSize);
int available() const;
+
+ bool deviceAtEnd() const;
+
void reset();
void setPrefetchDevice(QIODevice *device);
- void startFillTimer();
- void stopFillTimer();
+ QIODevice *prefetchDevice() const;
+
+ void setFillingEnabled(bool enabled);
signals:
void readyRead();
@@ -96,13 +64,15 @@ private slots:
void fillBuffer();
private:
- bool m_deviceError;
- int m_maxPeriodSize;
- int m_bytesPerFrame;
- int m_periodTime;
- QIODevice *m_device;
- QTimer *m_fillTimer;
- CoreAudioRingBuffer *m_buffer;
+ bool m_deviceError = false;
+ bool m_fillingEnabled = false;
+ bool m_deviceAtEnd = false;
+ const int m_maxPeriodSize = 0;
+ const int m_bytesPerFrame = 0;
+ const int m_periodTime = 0;
+ QIODevice *m_device = nullptr;
+ QTimer *m_fillTimer = nullptr;
+ std::unique_ptr<CoreAudioRingBuffer> m_buffer;
};
class QDarwinAudioSinkDevice : public QIODevice
@@ -125,7 +95,7 @@ class QDarwinAudioSink : public QPlatformAudioSink
Q_OBJECT
public:
- QDarwinAudioSink(const QAudioDevice &device);
+ QDarwinAudioSink(const QAudioDevice &device, QObject *parent);
~QDarwinAudioSink();
void start(QIODevice *device);
@@ -147,15 +117,11 @@ public:
qreal volume() const;
private slots:
- void deviceStopped();
void inputReady();
+ void updateAudioDevice();
private:
- enum {
- Running,
- Draining,
- Stopped
- };
+ enum ThreadState { Running, Draining, Stopped };
static OSStatus renderCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
@@ -166,15 +132,9 @@ private:
bool open();
void close();
- void audioThreadStart();
- void audioThreadStop();
- void audioThreadDrain();
- void audioDeviceStop();
- void audioDeviceIdle();
- void audioDeviceError();
-
- void startTimers();
- void stopTimers();
+ void onAudioDeviceIdle();
+ void onAudioDeviceError();
+ void onAudioDeviceDrained();
QAudioDevice m_audioDeviceInfo;
QByteArray m_device;
@@ -191,20 +151,18 @@ private:
AudioDeviceID m_audioDeviceId;
#endif
AudioUnit m_audioUnit = 0;
+ bool m_audioUnitStarted = false;
Float64 m_clockFrequency = 0;
AudioStreamBasicDescription m_streamFormat;
- QDarwinAudioSinkBuffer *m_audioBuffer = nullptr;
- QAtomicInt m_audioThreadState;
- QWaitCondition m_threadFinished;
- QMutex m_mutex;
+ std::unique_ptr<QDarwinAudioSinkBuffer> m_audioBuffer;
qreal m_cachedVolume = 1.;
#if defined(Q_OS_MACOS)
qreal m_volume = 1.;
#endif
bool m_pullMode = false;
- QAudio::Error m_errorCode = QAudio::NoError;
- QAudio::State m_stateCode = QAudio::StoppedState;
+ QAudioStateMachine m_stateMachine;
+ QSemaphore m_drainSemaphore;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/darwin/qdarwinaudiosource.mm b/src/multimedia/darwin/qdarwinaudiosource.mm
index 7f9b9bc95..4c1345fb8 100644
--- a/src/multimedia/darwin/qdarwinaudiosource.mm
+++ b/src/multimedia/darwin/qdarwinaudiosource.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdarwinaudiosource_p.h"
#include "qcoreaudiosessionmanager_p.h"
#include "qdarwinaudiodevice_p.h"
@@ -43,7 +7,7 @@
#include "qdarwinmediadevices_p.h"
#include <qmediadevices.h>
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
# include <AudioUnit/AudioComponent.h>
#endif
@@ -205,7 +169,7 @@ QDarwinAudioSourceBuffer::QDarwinAudioSourceBuffer(int bufferSize, int maxPeriod
m_inputBufferList = new QCoreAudioBufferList(m_inputFormat);
m_flushTimer = new QTimer(this);
- connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer()));
+ connect(m_flushTimer, SIGNAL(timeout()), this, SLOT(flushBuffer()));
if (CoreAudioUtils::toQAudioFormat(inputFormat) != CoreAudioUtils::toQAudioFormat(outputFormat)) {
if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) {
@@ -432,7 +396,7 @@ QDarwinAudioSourceDevice::QDarwinAudioSourceDevice(QDarwinAudioSourceBuffer *aud
, m_audioBuffer(audioBuffer)
{
open(QIODevice::ReadOnly | QIODevice::Unbuffered);
- connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead()));
+ connect(m_audioBuffer, SIGNAL(readyRead()), this, SIGNAL(readyRead()));
}
qint64 QDarwinAudioSourceDevice::readData(char *data, qint64 len)
@@ -448,8 +412,9 @@ qint64 QDarwinAudioSourceDevice::writeData(const char *data, qint64 len)
return 0;
}
-QDarwinAudioSource::QDarwinAudioSource(const QAudioDevice &device)
- : m_audioDeviceInfo(device)
+QDarwinAudioSource::QDarwinAudioSource(const QAudioDevice &device, QObject *parent)
+ : QPlatformAudioSource(parent)
+ , m_audioDeviceInfo(device)
, m_isOpen(false)
, m_internalBufferSize(DEFAULT_BUFFER_SIZE)
, m_totalFrames(0)
@@ -489,7 +454,7 @@ bool QDarwinAudioSource::open()
AudioComponentDescription componentDescription;
componentDescription.componentType = kAudioUnitType_Output;
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
componentDescription.componentSubType = kAudioUnitSubType_HALOutput;
#else
componentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
@@ -548,7 +513,7 @@ bool QDarwinAudioSource::open()
return false;
}
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
//Set Audio Device
if (AudioUnitSetProperty(m_audioUnit,
kAudioOutputUnitProperty_CurrentDevice,
@@ -564,7 +529,7 @@ bool QDarwinAudioSource::open()
//set format
m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat);
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
UInt32 size = 0;
if (m_audioFormat == m_audioDeviceInfo.preferredFormat()) {
@@ -577,7 +542,7 @@ bool QDarwinAudioSource::open()
1,
&m_deviceFormat,
sizeof(m_deviceFormat));
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
} else {
size = sizeof(m_deviceFormat);
if (AudioUnitGetProperty(m_audioUnit,
@@ -604,7 +569,7 @@ bool QDarwinAudioSource::open()
//setup buffers
UInt32 numberOfFrames;
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
size = sizeof(UInt32);
if (AudioUnitGetProperty(m_audioUnit,
kAudioDevicePropertyBufferFrameSize,
diff --git a/src/multimedia/darwin/qdarwinaudiosource_p.h b/src/multimedia/darwin/qdarwinaudiosource_p.h
index 681230994..87c3c1070 100644
--- a/src/multimedia/darwin/qdarwinaudiosource_p.h
+++ b/src/multimedia/darwin/qdarwinaudiosource_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef IOSAUDIOINPUT_H
#define IOSAUDIOINPUT_H
@@ -201,7 +165,7 @@ class QDarwinAudioSource : public QPlatformAudioSource
Q_OBJECT
public:
- QDarwinAudioSource(const QAudioDevice &device);
+ QDarwinAudioSource(const QAudioDevice &device, QObject *parent);
~QDarwinAudioSource();
void start(QIODevice *device);
@@ -262,7 +226,7 @@ private:
QAudioFormat m_audioFormat;
QIODevice *m_audioIO;
AudioUnit m_audioUnit;
-#if defined(Q_OS_OSX)
+#if defined(Q_OS_MACOS)
AudioDeviceID m_audioDeviceId;
#endif
Float64 m_clockFrequency;
diff --git a/src/multimedia/darwin/qdarwinmediadevices.mm b/src/multimedia/darwin/qdarwinmediadevices.mm
index 1770e3707..b0a108935 100644
--- a/src/multimedia/darwin/qdarwinmediadevices.mm
+++ b/src/multimedia/darwin/qdarwinmediadevices.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdarwinmediadevices_p.h"
#include "qmediadevices.h"
@@ -44,206 +8,262 @@
#include "qdarwinaudiosource_p.h"
#include "qdarwinaudiosink_p.h"
+#include <qloggingcategory.h>
+
#include <qdebug.h>
-#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
+#if defined(Q_OS_IOS)
#include "qcoreaudiosessionmanager_p.h"
#import <AVFoundation/AVFoundation.h>
+#else
+#include "qmacosaudiodatautils_p.h"
+#endif
+
+#if defined(Q_OS_MACOS)
+static Q_LOGGING_CATEGORY(qLcDarwinMediaDevices, "qt.multimedia.darwin.mediaDevices")
#endif
QT_BEGIN_NAMESPACE
+template<typename... Args>
+QAudioDevice createAudioDevice(bool isDefault, Args &&...args)
+{
+ auto *dev = new QCoreAudioDeviceInfo(std::forward<Args>(args)...);
+ dev->isDefault = isDefault;
+ return dev->create();
+}
+
#if defined(Q_OS_MACOS)
-AudioDeviceID defaultAudioDevice(QAudioDevice::Mode mode)
+
+static AudioDeviceID defaultAudioDevice(QAudioDevice::Mode mode)
{
- AudioDeviceID audioDevice;
- UInt32 size = sizeof(audioDevice);
const AudioObjectPropertySelector selector = (mode == QAudioDevice::Output) ? kAudioHardwarePropertyDefaultOutputDevice
: kAudioHardwarePropertyDefaultInputDevice;
- AudioObjectPropertyAddress defaultDevicePropertyAddress = { selector,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
- &defaultDevicePropertyAddress,
- 0, NULL, &size, &audioDevice) != noErr) {
- qWarning("QAudioDevice: Unable to find default %s device", (mode == QAudioDevice::Output) ? "output" : "input");
- return 0;
+ const AudioObjectPropertyAddress propertyAddress = {
+ selector,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMain,
+ };
+
+ if (auto audioDevice = getAudioObject<AudioDeviceID>(kAudioObjectSystemObject, propertyAddress,
+ "Default Device")) {
+ return *audioDevice;
}
- return audioDevice;
+ return 0;
}
static QByteArray uniqueId(AudioDeviceID device, QAudioDevice::Mode mode)
{
- CFStringRef name;
- UInt32 size = sizeof(CFStringRef);
-
- AudioObjectPropertyScope audioPropertyScope = mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
-
- AudioObjectPropertyAddress audioDeviceNamePropertyAddress = { kAudioDevicePropertyDeviceUID,
- audioPropertyScope,
- kAudioObjectPropertyElementMaster };
+ const AudioObjectPropertyAddress propertyAddress =
+ makePropertyAddress(kAudioDevicePropertyDeviceUID, mode);
- if (AudioObjectGetPropertyData(device, &audioDeviceNamePropertyAddress, 0, NULL, &size, &name) != noErr) {
- qWarning() << "QAudioDevice: Unable to get device UID";
- return QByteArray();
+ if (auto name = getAudioObject<CFStringRef>(device, propertyAddress, "Device UID")) {
+ QString s = QString::fromCFString(*name);
+ CFRelease(*name);
+ return s.toUtf8();
}
- QString s = QString::fromCFString(name);
- CFRelease(name);
- return s.toUtf8();
+ return QByteArray();
}
-QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
+static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
{
-
QList<QAudioDevice> devices;
AudioDeviceID defaultDevice = defaultAudioDevice(mode);
- if (defaultDevice != 0) {
- auto *dev = new QCoreAudioDeviceInfo(defaultDevice, uniqueId(defaultDevice, mode), mode);
- dev->isDefault = true;
- devices << dev->create();
+ if (defaultDevice != 0)
+ devices << createAudioDevice(true, defaultDevice, uniqueId(defaultDevice, mode), mode);
+
+ const AudioObjectPropertyAddress audioDevicesPropertyAddress = {
+ kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMain
+ };
+
+ if (auto audioDevices = getAudioData<AudioDeviceID>(
+ kAudioObjectSystemObject, audioDevicesPropertyAddress, "Audio Devices")) {
+ const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress =
+ makePropertyAddress(kAudioDevicePropertyStreamFormat, mode);
+
+ for (const auto &device : *audioDevices) {
+ if (device == defaultDevice)
+ continue;
+
+ if (getAudioObject<AudioStreamBasicDescription>(device,
+ audioDeviceStreamFormatPropertyAddress,
+ nullptr /*don't print logs*/)) {
+ devices << createAudioDevice(false, device, uniqueId(device, mode), mode);
+ }
+ }
}
- UInt32 propSize = 0;
- AudioObjectPropertyAddress audioDevicesPropertyAddress = { kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
+ return devices;
+}
- if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
- &audioDevicesPropertyAddress,
- 0, NULL, &propSize) == noErr) {
+static OSStatus audioDeviceChangeListener(AudioObjectID id, UInt32,
+ const AudioObjectPropertyAddress *address, void *ptr)
+{
+ Q_ASSERT(address);
+ Q_ASSERT(ptr);
+
+ QDarwinMediaDevices *instance = static_cast<QDarwinMediaDevices *>(ptr);
+
+ qCDebug(qLcDarwinMediaDevices)
+ << "audioDeviceChangeListener: id:" << id << "address: " << address->mSelector
+ << address->mScope << address->mElement;
+
+ switch (address->mSelector) {
+ case kAudioHardwarePropertyDefaultInputDevice:
+ instance->onInputsUpdated();
+ break;
+ case kAudioHardwarePropertyDefaultOutputDevice:
+ instance->onOutputsUpdated();
+ break;
+ default:
+ instance->onInputsUpdated();
+ instance->onOutputsUpdated();
+ break;
+ }
+
+ return 0;
+}
- const int dc = propSize / sizeof(AudioDeviceID);
+static constexpr AudioObjectPropertyAddress listenerAddresses[] = {
+ { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMain },
+ { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMain },
+ { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMain }
+};
- if (dc > 0) {
- AudioDeviceID* audioDevices = new AudioDeviceID[dc];
+static void setAudioListeners(QDarwinMediaDevices &instance)
+{
+ for (const auto &address : listenerAddresses) {
+ const auto err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &address,
+ audioDeviceChangeListener, &instance);
+
+ if (err)
+ qWarning() << "Fail to add listener. mSelector:" << address.mSelector
+ << "mScope:" << address.mScope << "mElement:" << address.mElement
+ << "err:" << err;
+ }
+}
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesPropertyAddress, 0, NULL, &propSize, audioDevices) == noErr) {
- for (int i = 0; i < dc; ++i) {
- if (audioDevices[i] == defaultDevice)
- continue;
+static void removeAudioListeners(QDarwinMediaDevices &instance)
+{
+ for (const auto &address : listenerAddresses) {
+ const auto err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &address,
+ audioDeviceChangeListener, &instance);
+
+ if (err)
+ qWarning() << "Fail to remove listener. mSelector:" << address.mSelector
+ << "mScope:" << address.mScope << "mElement:" << address.mElement
+ << "err:" << err;
+ }
+}
- AudioStreamBasicDescription sf;
- UInt32 size = sizeof(AudioStreamBasicDescription);
- AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress = { kAudioDevicePropertyStreamFormat,
- (mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput),
- kAudioObjectPropertyElementMaster };
+#elif defined(Q_OS_IOS)
- if (AudioObjectGetPropertyData(audioDevices[i], &audioDeviceStreamFormatPropertyAddress, 0, NULL, &size, &sf) == noErr)
- devices << (new QCoreAudioDeviceInfo(audioDevices[i], uniqueId(audioDevices[i], mode), mode))->create();
- }
- }
+static QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode)
+{
+ QList<QAudioDevice> devices;
- delete[] audioDevices;
+ if (mode == QAudioDevice::Output) {
+ devices.append(createAudioDevice(true, "default", QAudioDevice::Output));
+ } else {
+ AVCaptureDevice *defaultDevice =
+ [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
+
+ // TODO: Support Bluetooth and USB devices
+ AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession =
+ [AVCaptureDeviceDiscoverySession
+ discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInMicrophone ]
+ mediaType:AVMediaTypeAudio
+ position:AVCaptureDevicePositionUnspecified];
+
+ NSArray *captureDevices = [captureDeviceDiscoverySession devices];
+ for (AVCaptureDevice *device in captureDevices) {
+ const bool isDefault =
+ defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID];
+ devices.append(createAudioDevice(isDefault,
+ QString::fromNSString(device.uniqueID).toUtf8(),
+ QAudioDevice::Input));
}
}
return devices;
}
-static OSStatus
-audioDeviceChangeListener(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void* ptr)
+static void setAudioListeners(QDarwinMediaDevices &)
{
- QDarwinMediaDevices *m = static_cast<QDarwinMediaDevices *>(ptr);
- m->updateAudioDevices();
- return 0;
+ // ### This should use the audio session manager
+}
+
+static void removeAudioListeners(QDarwinMediaDevices &)
+{
+ // ### This should use the audio session manager
}
+
#endif
QDarwinMediaDevices::QDarwinMediaDevices()
: QPlatformMediaDevices()
{
-
-#ifdef Q_OS_MACOS
- OSStatus err = noErr;
- AudioObjectPropertyAddress *audioDevicesAddress = new AudioObjectPropertyAddress{ kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
- m_audioDevicesProperty = audioDevicesAddress;
- err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, audioDevicesAddress, audioDeviceChangeListener, this);
- if (err)
- qDebug("error on AudioObjectAddPropertyListener");
-#else
- // ### This should use the audio session manager
+#ifdef Q_OS_MACOS // TODO: implement setAudioListeners, removeAudioListeners for Q_OS_IOS, after
+ // that - remove or modify the define
+ m_cachedAudioInputs = availableAudioDevices(QAudioDevice::Input);
+ m_cachedAudioOutputs = availableAudioDevices(QAudioDevice::Output);
#endif
- updateAudioDevices();
+
+ setAudioListeners(*this);
}
QDarwinMediaDevices::~QDarwinMediaDevices()
{
-
-#ifdef Q_OS_MACOS
- AudioObjectRemovePropertyListener(kAudioObjectSystemObject, (AudioObjectPropertyAddress *)m_audioDevicesProperty, audioDeviceChangeListener, this);
-#endif
+ removeAudioListeners(*this);
}
QList<QAudioDevice> QDarwinMediaDevices::audioInputs() const
{
-#ifdef Q_OS_IOS
- AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
-
- // TODO: Support Bluetooth and USB devices
- QList<QAudioDevice> devices;
- AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession
- discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone]
- mediaType:AVMediaTypeAudio
- position:AVCaptureDevicePositionUnspecified];
-
- NSArray *captureDevices = [captureDeviceDiscoverySession devices];
- for (AVCaptureDevice *device in captureDevices) {
- auto *dev = new QCoreAudioDeviceInfo(QString::fromNSString(device.uniqueID).toUtf8(), QAudioDevice::Input);
- if (defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID])
- dev->isDefault = true;
- devices << dev->create();
- }
- return devices;
-#else
return availableAudioDevices(QAudioDevice::Input);
-#endif
}
QList<QAudioDevice> QDarwinMediaDevices::audioOutputs() const
{
-#ifdef Q_OS_IOS
- QList<QAudioDevice> devices;
- auto *dev = new QCoreAudioDeviceInfo("default", QAudioDevice::Output);
- dev->isDefault = true;
- devices.append(dev->create());
- return devices;
-#else
return availableAudioDevices(QAudioDevice::Output);
-#endif
}
-void QDarwinMediaDevices::updateAudioDevices()
+void QDarwinMediaDevices::onInputsUpdated()
{
-#ifdef Q_OS_MACOS
- QList<QAudioDevice> inputs = availableAudioDevices(QAudioDevice::Input);
- if (m_audioInputs != inputs) {
- m_audioInputs = inputs;
- audioInputsChanged();
+ auto inputs = availableAudioDevices(QAudioDevice::Input);
+ if (m_cachedAudioInputs != inputs) {
+ m_cachedAudioInputs = inputs;
+ emit audioInputsChanged();
}
+}
- QList<QAudioDevice> outputs = availableAudioDevices(QAudioDevice::Output);
- if (m_audioOutputs!= outputs) {
- m_audioOutputs = outputs;
- audioOutputsChanged();
+void QDarwinMediaDevices::onOutputsUpdated()
+{
+ auto outputs = availableAudioDevices(QAudioDevice::Output);
+ if (m_cachedAudioOutputs != outputs) {
+ m_cachedAudioOutputs = outputs;
+ emit audioOutputsChanged();
}
-#endif
}
-QPlatformAudioSource *QDarwinMediaDevices::createAudioSource(const QAudioDevice &info)
+QPlatformAudioSource *QDarwinMediaDevices::createAudioSource(const QAudioDevice &info,
+ QObject *parent)
{
- return new QDarwinAudioSource(info);
+ return new QDarwinAudioSource(info, parent);
}
-QPlatformAudioSink *QDarwinMediaDevices::createAudioSink(const QAudioDevice &info)
+QPlatformAudioSink *QDarwinMediaDevices::createAudioSink(const QAudioDevice &info,
+ QObject *parent)
{
- return new QDarwinAudioSink(info);
+ return new QDarwinAudioSink(info, parent);
}
-
QT_END_NAMESPACE
diff --git a/src/multimedia/darwin/qdarwinmediadevices_p.h b/src/multimedia/darwin/qdarwinmediadevices_p.h
index 95fda5ee0..0c7a45433 100644
--- a/src/multimedia/darwin/qdarwinmediadevices_p.h
+++ b/src/multimedia/darwin/qdarwinmediadevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDARWINMEDIADEVICES_H
#define QDARWINMEDIADEVICES_H
@@ -63,22 +27,21 @@ class QDarwinMediaDevices : public QPlatformMediaDevices
{
public:
QDarwinMediaDevices();
- ~QDarwinMediaDevices();
+ ~QDarwinMediaDevices() override;
QList<QAudioDevice> audioInputs() const override;
QList<QAudioDevice> audioOutputs() const override;
- QPlatformAudioSource *createAudioSource(const QAudioDevice &info) override;
- QPlatformAudioSink *createAudioSink(const QAudioDevice &info) override;
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &info,
+ QObject *parent) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &info,
+ QObject *parent) override;
- void updateAudioDevices();
+ void onInputsUpdated();
+ void onOutputsUpdated();
private:
- QList<QAudioDevice> m_audioInputs;
- QList<QAudioDevice> m_audioOutputs;
-
-#ifdef Q_OS_MACOS
- void *m_audioDevicesProperty;
-#endif
+ QList<QAudioDevice> m_cachedAudioInputs;
+ QList<QAudioDevice> m_cachedAudioOutputs;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/darwin/qmacosaudiodatautils_p.h b/src/multimedia/darwin/qmacosaudiodatautils_p.h
new file mode 100644
index 000000000..8cc2f8440
--- /dev/null
+++ b/src/multimedia/darwin/qmacosaudiodatautils_p.h
@@ -0,0 +1,112 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMACOSAUDIODATAUTILS_P_H
+#define QMACOSAUDIODATAUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <CoreAudio/AudioHardware.h>
+
+#include "qaudiodevice.h"
+#include "qdebug.h"
+
+#include <optional>
+#include <vector>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+template<typename... Args>
+void printUnableToReadWarning(const char *logName, AudioObjectID objectID, const AudioObjectPropertyAddress &address, Args &&...args)
+{
+ if (!logName)
+ return;
+
+ char scope[5] = {0};
+ memcpy(&scope, &address.mScope, 4);
+ std::reverse(scope, scope + 4);
+
+ auto warn = qWarning();
+ warn << "Unable to read property" << logName << "for object" << objectID << ", scope" << scope << ";";
+ (warn << ... << args);
+ warn << "\n If the warning is unexpected use test_audio_config to get comprehensive audio info and report a bug";
+}
+
+inline static AudioObjectPropertyAddress
+makePropertyAddress(AudioObjectPropertySelector selector, QAudioDevice::Mode mode,
+ AudioObjectPropertyElement element = kAudioObjectPropertyElementMain)
+{
+ return { selector,
+ mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput
+ : kAudioDevicePropertyScopeOutput,
+ element };
+}
+
+inline static bool getAudioData(AudioObjectID objectID, const AudioObjectPropertyAddress &address,
+ void *dst, UInt32 dstSize, const char *logName)
+{
+ UInt32 readBytes = dstSize;
+ const auto res = AudioObjectGetPropertyData(objectID, &address, 0, nullptr, &readBytes, dst);
+
+ if (res != noErr)
+ printUnableToReadWarning(logName, objectID, address, "Err:", res);
+ else if (readBytes != dstSize)
+ printUnableToReadWarning(logName, objectID, address, "Data size", readBytes, "VS", dstSize,
+ "expected");
+ else
+ return true;
+
+ return false;
+}
+
+template<typename T>
+std::optional<std::vector<T>> getAudioData(AudioObjectID objectID,
+ const AudioObjectPropertyAddress &address,
+ const char *logName, size_t minDataSize = 0)
+{
+ static_assert(std::is_trivial_v<T>, "A trivial type is expected");
+
+ UInt32 size = 0;
+ const auto res = AudioObjectGetPropertyDataSize(objectID, &address, 0, nullptr, &size);
+
+ if (res != noErr) {
+ printUnableToReadWarning(logName, objectID, address,
+ "AudioObjectGetPropertyDataSize failed, Err:", res);
+ } else if (size / sizeof(T) < minDataSize) {
+ printUnableToReadWarning(logName, objectID, address, "Data size is too small:", size, "VS",
+ minDataSize * sizeof(T), "bytes");
+ } else {
+ std::vector<T> data(size / sizeof(T));
+ if (getAudioData(objectID, address, data.data(), data.size() * sizeof(T), logName))
+ return { std::move(data) };
+ }
+
+ return {};
+}
+
+template<typename T>
+std::optional<T> getAudioObject(AudioObjectID objectID, const AudioObjectPropertyAddress &address,
+ const char *logName)
+{
+ static_assert(std::is_trivial_v<T>, "A trivial type is expected");
+
+ T object{};
+ if (getAudioData(objectID, address, &object, sizeof(T), logName))
+ return { object };
+
+ return {};
+}
+
+QT_END_NAMESPACE
+
+#endif // QMACOSAUDIODATAUTILS_P_H
diff --git a/src/multimedia/doc/QtMultimediaDoc b/src/multimedia/doc/QtMultimediaDoc
index 13b8e7229..e57c3595b 100644
--- a/src/multimedia/doc/QtMultimediaDoc
+++ b/src/multimedia/doc/QtMultimediaDoc
@@ -1,9 +1,2 @@
#include <QtMultimedia/QtMultimedia>
#include <QtMultimediaWidgets/QtMultimediaWidgets>
-
-#include "../audio/qaudiosystem_p.h"
-#include "../platform/qplatformaudiodecoder_p.h"
-#include "../platform/qplatformcamera_p.h"
-#include "../platform/qplatformmediarecorder_p.h"
-#include "../platform/qplatformimagecapture_p.h"
-#include "../platform/qplatformvideosink_p.h"
diff --git a/src/multimedia/doc/qtmultimedia.qdocconf b/src/multimedia/doc/qtmultimedia.qdocconf
index f93091179..97e6dc696 100644
--- a/src/multimedia/doc/qtmultimedia.qdocconf
+++ b/src/multimedia/doc/qtmultimedia.qdocconf
@@ -1,6 +1,5 @@
include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
include($QT_INSTALL_DOCS/config/exampleurl-qtmultimedia.qdocconf)
-include(../../../examples/multimedia/video/mediaplayer/doc/qmlmediaplayer.qdocconf)
project = QtMultimedia
description = Qt Multimedia Documentation
@@ -9,6 +8,8 @@ version = $QT_VERSION
moduleheader = QtMultimediaDoc
includepaths += .
+examplesinstallpath = multimedia
+
# The following parameters are for creating a qhp file, the qhelpgenerator
# program can convert the qhp file into a qch file which can be opened in
# Qt Assistant and/or Qt Creator.
@@ -37,7 +38,7 @@ qhp.QtMultimedia.subprojects.widgetclasses.sortPages = true
qhp.QtMultimedia.subprojects.qmltypes.title = QML Types
qhp.QtMultimedia.subprojects.qmltypes.indexTitle = Qt Multimedia QML Types
-qhp.QtMultimedia.subprojects.qmltypes.selectors = qmlclass
+qhp.QtMultimedia.subprojects.qmltypes.selectors = qmltype
qhp.QtMultimedia.subprojects.qmltypes.sortPages = true
qhp.QtMultimedia.subprojects.examples.title = Examples
@@ -45,24 +46,22 @@ qhp.QtMultimedia.subprojects.examples.indexTitle = Qt Multimedia Examples
qhp.QtMultimedia.subprojects.examples.selectors = doc:example
qhp.QtMultimedia.subprojects.examples.sortPages = true
-exampledirs += ../../../examples \
+exampledirs += ../../../examples/multimedia \
+ ../../../examples/multimediawidgets \
snippets \
../../multimediawidgets/doc/snippets
-manifestmeta.highlighted.names = "QtMultimedia/QML Media Player Example" \
- "QtMultimedia/Media Player Example" \
- "QtMultimedia/Camera Example" \
- "QtMultimedia/QML Recorder Example" \
-
-headerdirs += ../..
+headerdirs += .. \
+ ../../multimediawidgets \
+ ../../multimediaquick
imagedirs += src/images \
-sourcedirs += ../..
-
-excludedirs += ../../gsttools
+sourcedirs += .. \
+ ../../multimediawidgets \
+ ../../multimediaquick
-depends += qtcore qtdoc qtgui qtquick qtqml qtwidgets qtnetwork qmake qtcmake qtquickcontrols
+depends += qtcore qtdoc qtgui qtquick qtqml qtwidgets qtnetwork qmake qtcmake qtquickcontrols qtspatialaudio
# Ignore \since commands for versions earlier than 6.3
ignoresince = 6.3
@@ -71,5 +70,13 @@ navigation.landingpage = "Qt Multimedia"
navigation.cppclassespage = "Qt Multimedia C++ Classes"
navigation.qmltypespage = "Qt Multimedia QML Types"
+# Highlighted examples in Graphics & Multimedia category
+manifestmeta.highlighted.names = \
+ "QtMultimedia/Screen Capture Example" \
+ "QtMultimedia/QML Video Recorder"
+
+# Highlighted examples in Mobile category
+manifestmeta.highlighted.names += "QtMultimedia/QML Camera Application"
+
# Fail the documentation build if there are more warnings than the limit
warninglimit = 0
diff --git a/src/multimedia/doc/snippets/CMakeLists.txt b/src/multimedia/doc/snippets/CMakeLists.txt
index e178b6c10..c3937477b 100644
--- a/src/multimedia/doc/snippets/CMakeLists.txt
+++ b/src/multimedia/doc/snippets/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(multimedia_cppsnippets)
diff --git a/src/multimedia/doc/snippets/doc_src_qtmultimedia.cpp b/src/multimedia/doc/snippets/doc_src_qtmultimedia.cpp
deleted file mode 100644
index a56283474..000000000
--- a/src/multimedia/doc/snippets/doc_src_qtmultimedia.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//! [0]
-#include <QtMultimedia>
-//! [0]
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/audio.cpp b/src/multimedia/doc/snippets/multimedia-snippets/audio.cpp
index 24045adab..565f7b29b 100644
--- a/src/multimedia/doc/snippets/multimedia-snippets/audio.cpp
+++ b/src/multimedia/doc/snippets/multimedia-snippets/audio.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/* Audio related snippets */
#include <QFile>
@@ -87,9 +51,9 @@ void AudioInputExample::setup()
}
audio = new QAudioSource(format, this);
- connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
+ connect(audio, &QAudioSource::stateChanged, this, &AudioInputExample::handleStateChanged);
- QTimer::singleShot(3000, this, SLOT(stopRecording()));
+ QTimer::singleShot(3000, this, &AudioInputExample::stopRecording);
audio->start(&destinationFile);
// Records audio for 3000ms
}
@@ -135,6 +99,7 @@ public:
public Q_SLOTS:
void handleStateChanged(QAudio::State newState);
+ void stopAudioOutput();
private:
//! [Audio output class members]
@@ -156,27 +121,34 @@ void AudioOutputExample::setup()
format.setChannelCount(1);
format.setSampleFormat(QAudioFormat::UInt8);
- QAudioDevice info(QAudioDevice::defaultOutputDevice());
+ QAudioDevice info(QMediaDevices::defaultAudioOutput());
if (!info.isFormatSupported(format)) {
qWarning() << "Raw audio format not supported by backend, cannot play audio.";
return;
}
audio = new QAudioSink(format, this);
- connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
+ connect(audio, QAudioSink::stateChanged, this, &AudioInputExample::handleStateChanged);
audio->start(&sourceFile);
}
//! [Audio output setup]
+//! [Audio output stop]
+void AudioOutputExample::stopAudioOutput()
+{
+ audio->stop();
+ sourceFile.close();
+ delete audio;
+}
+//! [Audio output stop]
+
//! [Audio output state changed]
void AudioOutputExample::handleStateChanged(QAudio::State newState)
{
switch (newState) {
case QAudio::IdleState:
// Finished playing (no more data)
- audio->stop();
- sourceFile.close();
- delete audio;
+ AudioOutputExample::stopAudioOutput();
break;
case QAudio::StoppedState:
@@ -203,9 +175,9 @@ void AudioDeviceInfo()
//! [Setting audio format]
//! [Dumping audio formats]
- const auto deviceInfos = QMediaDevices::availableDevices(QAudioDevice::Output);
- for (const QAudioDevice &deviceInfo : deviceInfos)
- qDebug() << "Device: " << deviceInfo.description();
+ const auto devices = QMediaDevices::audioOutputs();
+ for (const QAudioDevice &device : devices)
+ qDebug() << "Device: " << device.description();
//! [Dumping audio formats]
}
@@ -231,7 +203,7 @@ void AudioDecodingExample::decode()
decoder->setAudioFormat(desiredFormat);
decoder->setSource("level1.mp3");
- connect(decoder, SIGNAL(bufferReady()), this, SLOT(readBuffer()));
+ connect(decoder, &QAudioDecoder::bufferReady, this, &AudioDecodingExample::readBuffer);
decoder->start();
// Now wait for bufferReady() signal and call decoder->read()
@@ -245,9 +217,9 @@ void applyVolume(int volumeSliderValue)
{
// volumeSliderValue is in the range [0..100]
- qreal linearVolume = QAudio::convertVolume(volumeSliderValue / qreal(100.0),
- QAudio::LogarithmicVolumeScale,
- QAudio::LinearVolumeScale);
+ qreal linearVolume = QtAudio::convertVolume(volumeSliderValue / qreal(100.0),
+ QtAudio::LogarithmicVolumeScale,
+ QtAudio::LinearVolumeScale);
player.setVolume(qRound(linearVolume * 100));
}
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/camera.cpp b/src/multimedia/doc/snippets/multimedia-snippets/camera.cpp
index 7c7b5e5a0..053af088f 100644
--- a/src/multimedia/doc/snippets/multimedia-snippets/camera.cpp
+++ b/src/multimedia/doc/snippets/multimedia-snippets/camera.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/* Camera snippets */
@@ -201,8 +165,6 @@ void camera_info()
qDebug() << "The camera is on the front face of the hardware system.";
else if (cameraDevice.position() == QCameraDevice::BackFace)
qDebug() << "The camera is on the back face of the hardware system.";
-
- qDebug() << "The camera sensor orientation is " << cameraDevice.orientation() << " degrees.";
//! [Camera info]
}
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/devices.cpp b/src/multimedia/doc/snippets/multimedia-snippets/devices.cpp
new file mode 100644
index 000000000..652400364
--- /dev/null
+++ b/src/multimedia/doc/snippets/multimedia-snippets/devices.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QAudioDevice>
+#include <QCameraDevice>
+#include <QMediaDevices>
+#include <QString>
+#include <QTextStream>
+
+int main(int argc, char *argv[])
+{
+ Q_UNUSED(argc);
+ Q_UNUSED(argv);
+
+ QTextStream out(stdout);
+
+ //! [Media Audio Input Device Enumeration]
+ const QList<QAudioDevice> audioDevices = QMediaDevices::audioInputs();
+ for (const QAudioDevice &device : audioDevices)
+ {
+ out << "ID: " << device.id() << Qt::endl;
+ out << "Description: " << device.description() << Qt::endl;
+ out << "Is default: " << (device.isDefault() ? "Yes" : "No") << Qt::endl;
+ }
+ //! [Media Audio Input Device Enumeration]
+
+ //! [Media Video Input Device Enumeration]
+ const QList<QCameraDevice> videoDevices = QMediaDevices::videoInputs();
+ for (const QCameraDevice &device : videoDevices)
+ {
+ out << "ID: " << device.id() << Qt::endl;
+ out << "Description: " << device.description() << Qt::endl;
+ out << "Is default: " << (device.isDefault() ? "Yes" : "No") << Qt::endl;
+ }
+ //! [Media Video Input Device Enumeration]
+
+ return 0;
+}
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/media.cpp b/src/multimedia/doc/snippets/multimedia-snippets/media.cpp
index 634c5dd2b..a4b9a9fb5 100644
--- a/src/multimedia/doc/snippets/multimedia-snippets/media.cpp
+++ b/src/multimedia/doc/snippets/multimedia-snippets/media.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/* Media related snippets */
#include <QFile>
@@ -112,7 +76,7 @@ void MediaExample::MediaPlayer()
player = new QMediaPlayer;
audioOutput = new QAudioOutput;
player->setAudioOutput(audioOutput);
- connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
+ connect(player, &QMediaPlayer::positionChanged, this, &MediaExample::positionChanged);
player->setSource(QUrl::fromLocalFile("/Users/me/Music/coolsong.mp3"));
audioOutput->setVolume(50);
player->play();
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/multiple-videooutputs.qml b/src/multimedia/doc/snippets/multimedia-snippets/multiple-videooutputs.qml
index f50c9f169..0c673cc42 100644
--- a/src/multimedia/doc/snippets/multimedia-snippets/multiple-videooutputs.qml
+++ b/src/multimedia/doc/snippets/multimedia-snippets/multiple-videooutputs.qml
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/qsound.cpp b/src/multimedia/doc/snippets/multimedia-snippets/qsound.cpp
index 246589767..644dcb228 100644
--- a/src/multimedia/doc/snippets/multimedia-snippets/qsound.cpp
+++ b/src/multimedia/doc/snippets/multimedia-snippets/qsound.cpp
@@ -1,52 +1,5 @@
- /****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "qpushbutton.h"
#include "qsoundeffect.h"
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/qtvideosink.qml b/src/multimedia/doc/snippets/multimedia-snippets/qtvideosink.qml
index 842b3138c..eeac9c28e 100644
--- a/src/multimedia/doc/snippets/multimedia-snippets/qtvideosink.qml
+++ b/src/multimedia/doc/snippets/multimedia-snippets/qtvideosink.qml
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtMultimedia
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/soundeffect.qml b/src/multimedia/doc/snippets/multimedia-snippets/soundeffect.qml
index 501443d25..e91e54e3c 100644
--- a/src/multimedia/doc/snippets/multimedia-snippets/soundeffect.qml
+++ b/src/multimedia/doc/snippets/multimedia-snippets/soundeffect.qml
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtMultimedia
diff --git a/src/multimedia/doc/snippets/multimedia-snippets/video.cpp b/src/multimedia/doc/snippets/multimedia-snippets/video.cpp
index ce35d6e74..8cc3b41b3 100644
--- a/src/multimedia/doc/snippets/multimedia-snippets/video.cpp
+++ b/src/multimedia/doc/snippets/multimedia-snippets/video.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/* Video related snippets */
#include "qvideorenderercontrol.h"
diff --git a/src/multimedia/doc/src/audiooverview.qdoc b/src/multimedia/doc/src/audiooverview.qdoc
index d47df5566..40a6318a6 100644
--- a/src/multimedia/doc/src/audiooverview.qdoc
+++ b/src/multimedia/doc/src/audiooverview.qdoc
@@ -1,47 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt for Multimedia module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page audiooverview.html
\title Audio Overview
\inlineimage sound-wave-small.jpg
\brief Playback, recording and processing of Audio.
+\ingroup explanations-graphicsandmultimedia
\section1 Audio Features
@@ -156,13 +121,10 @@ Here's an example of decoding a local file:
\snippet multimedia-snippets/audio.cpp Local audio decoding
-\section1 Examples
+\section2 Spatial Audio
-There are both C++ and QML examples available.
-
-\section2 C++ Examples
-
-\annotatedlist audio_examples
+The \l {Qt Spatial Audio} module provides an API for implementation sound
+fields in 3D space.
\section1 Reference Documentation
@@ -174,5 +136,8 @@ There are both C++ and QML examples available.
\annotatedlist multimedia_audio_qml
+\section2 Examples
+
+\annotatedlist audio_examples
*/
diff --git a/src/multimedia/doc/src/cameraoverview.qdoc b/src/multimedia/doc/src/cameraoverview.qdoc
index 8753d86e0..b93954d6d 100644
--- a/src/multimedia/doc/src/cameraoverview.qdoc
+++ b/src/multimedia/doc/src/cameraoverview.qdoc
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt for Multimedia module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page cameraoverview.html
\title Camera Overview
\brief Camera viewfinder, still image capture, and video recording.
+\ingroup explanations-graphicsandmultimedia
The Qt Multimedia API provides a number of camera related classes, so you
can access images and videos from mobile device cameras or web cameras.
diff --git a/src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc b/src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc
index f531e613a..34f7df319 100644
--- a/src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc
+++ b/src/multimedia/doc/src/examples/video-qml-paint-rate.qdocinc
@@ -2,7 +2,7 @@ The QML painting rate is calculated by the FrequencyMonitor class, which
turns a stream of events (received via the notify() slot), into an
instantaneous and an averaged frequency:
-\quotefromfile multimedia/video/qmlvideo/frequencymonitor.h
+\quotefromfile video/qmlvideo/frequencymonitor.h
\skipto class FrequencyMonitor : public QObject
\printuntil Q_OBJECT
\skipto Q_PROPERTY(qreal instantaneousFrequency
@@ -19,13 +19,13 @@ instantaneous and an averaged frequency:
The FrequencyMonitor class is exposed to QML like this
-\quotefromfile multimedia/video/qmlvideo/frequencymonitordeclarative.cpp
+\quotefromfile video/qmlvideo/frequencymonitordeclarative.cpp
\skipto FrequencyMonitor::qmlRegisterType
\printuntil }
and its data is displayed by defining a QML item called FrequencyItem, like this:
-\quotefromfile multimedia/video/qmlvideo/qml/frequencymonitor/FrequencyItem.qml
+\quotefromfile video/qmlvideo/frequencymonitor/FrequencyItem.qml
\skipto import FrequencyMonitor
\printuntil id: root
\dots
diff --git a/src/multimedia/doc/src/images/camera_correctionAngle_90.png b/src/multimedia/doc/src/images/camera_correctionAngle_90.png
new file mode 100644
index 000000000..7a3c169a5
--- /dev/null
+++ b/src/multimedia/doc/src/images/camera_correctionAngle_90.png
Binary files differ
diff --git a/src/multimedia/doc/src/multimedia-overview.qdoc b/src/multimedia/doc/src/multimedia-overview.qdoc
index 2d6cebaec..6da54a7e2 100644
--- a/src/multimedia/doc/src/multimedia-overview.qdoc
+++ b/src/multimedia/doc/src/multimedia-overview.qdoc
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt for Multimedia module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
@@ -43,6 +7,7 @@
\page multimediaoverview.html
\title Multimedia Overview
\brief A set of APIs for working with audio, video and camera devices.
+\ingroup explanations-graphicsandmultimedia
Multimedia support in Qt is provided by the \l{Qt Multimedia} module. The Qt
Multimedia module provides a rich feature set that enables you to easily take
@@ -56,6 +21,7 @@ Here are some things you can do with the Qt Multimedia APIs:
\list
\li Access raw audio devices for input and output.
\li Play low latency sound effects.
+\li Play 3D spatial audio.
\li Play media files in playlists (such as compressed audio or video files).
\li Record audio and compress it.
\li Use a camera, including viewfinder, image capture, and movie recording
@@ -72,7 +38,7 @@ can also take a look at some \l{Multimedia Recipes}{recipes}.
\li \l {Audio Overview}
\li \l {Video Overview}
\li \l {Camera Overview}
-\li \l {Spatial Audio Overview}
+\li \l {Spatial Audio Overview} (Technology Preview)
\endlist
\section1 Multimedia Recipes
@@ -91,13 +57,18 @@ For some quick recipes, see this table:
\li \l{SoundEffect}
\li QSoundEffect
\row
+ \li Playing 3D sound
+ \li \l{Spatial Audio Panning Example}{audiopanning}
+ \li SpatialSound, AudioEngine
+ \li QSpatialSound, QAudioEngine
+ \row
\li Playing encoded audio (MP3, AAC etc)
\li \l{Media Player Example}{player}
\li \l{MediaPlayer}
\li QMediaPlayer
\row
\li Playing raw audio data with low latency
- \li \l{Audio Output Example}{audiooutput},
+ \li \l{Audio Output Example}{audiooutput}
\li
\li QAudioSink
\row
@@ -125,26 +96,21 @@ For some quick recipes, see this table:
\row
\li Capturing audio and video
\li \l {Camera Example}{camera},
- \l {QML Recorder Example}{recorder}
+ \l {QML Video Recorder}{recorder}
\li \l CaptureSession, \l Camera, \l AudioInput \l VideoOutput
\li QMediaCaptureSession, QCamera, QAudioInput, QVideoWidget
\row
\li Capturing photos
\li \l {Camera Example}{camera},
- \l {QML Recorder Example}{recorder}
+ \l {QML Video Recorder}{recorder}
\li \l CaptureSession, \l Camera, \l ImageCapture
\li QMediaCaptureSession, QCamera, QImageCapture
\row
\li Capturing movies
\li \l {Camera Example}{camera},
- \l {QML Recorder Example}{recorder}
+ \l {QML Video Recorder}{recorder}
\li \l CaptureSession, \l Camera, \l MediaRecorder
\li QMediaCaptureSession, QCamera, QMediaRecorder
- \row
- \li Spatial Audio
- \li \l {Spatial Audio example}{spatialaudio},
- \li \l SpatialAudioEngine, \l SpatialAudioListener, \l SpatialAudioSoundSource
- \li QSpatialAudioEngine, QSpatialAudioListener, QSpatialAudioSoundSource
\endtable
\section1 Limitations
@@ -154,6 +120,13 @@ platform. This can mean that support for various codecs, or containers will vary
between machines. This support depends on what the end user has installed.
See \l{Supported Media Formats} for more detail.
+\note Qt Multimedia APIs depend on functionality provided by QCoreApplication,
+and multimedia objects created using the Qt Multimedia APIs can only be used
+during the lifetime of this application object. It is therefore important to
+create a QCoreApplication, QGuiApplication, or QApplication before accessing
+any of the Qt Multimedia APIs. If the application object is recreated, make
+sure that any Qt Multimedia objects are also recreated.
+
\section1 Changes from Previous Versions
If you previously used Qt Multimedia in Qt 5, see
@@ -162,14 +135,8 @@ you might need to change when porting code to Qt 6.
\section1 Reference Documentation
-\section2 QML Types
-The QML types are accessed by using:
-\code
-import QtMultimedia
-\endcode
-
-\section2 Multimedia Classes
-
-\annotatedlist multimedia
-
+\list
+ \li \l{Qt Multimedia C++ Classes}{C++ Classes}
+ \li \l{Qt Multimedia QML Types}{QML Types}
+\endlist
*/
diff --git a/src/multimedia/doc/src/platform-notes-apple.qdoc b/src/multimedia/doc/src/platform-notes-apple.qdoc
index bd8636ee4..a9bca07c2 100644
--- a/src/multimedia/doc/src/platform-notes-apple.qdoc
+++ b/src/multimedia/doc/src/platform-notes-apple.qdoc
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the FOO module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtmultimedia-apple.html
diff --git a/src/multimedia/doc/src/platform-notes-wasm.qdoc b/src/multimedia/doc/src/platform-notes-wasm.qdoc
new file mode 100644
index 000000000..d1a942c9f
--- /dev/null
+++ b/src/multimedia/doc/src/platform-notes-wasm.qdoc
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtmultimedia-wasm.html
+\title Qt Multimedia on WebAssembly
+\brief Platform notes for WebAssembly
+
+This page covers the availability of Qt Multimedia features on WebAssembly.
+
+\section1 Limitations
+
+Due to the asynchronous nature of javascript, some features such as getting the list of
+QMediaDevices, will not be readily available and may take some time to request permissions and
+gather the list of devices. The audioInputsChanged, audioOutputsChanged and
+videoInputChanged signals from QMediaDevices class will be emitted when they are available.
+
+Playing video currently works by using a html 2d context, so all operations are on the CPU.
+
+Performance is acceptable, although there is a copy on every frame, so it may be
+less performant than desktop platforms when playing hi-def video.
+
+Using and selecting different Codecs/video formats have not yet been tested, but whatever
+video formats the browser supports will most likely work.
+
+Playing data from a stream (using \c {setSourceDevice(QIODevice*)}), instead
+of fetching a URL, isn't supported.
+
+Some advanced features may or may not work at this time.
+
+Files can be served from the/any web server, respective
+of \l{https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS}{CORS}. Because of
+the limited size of local file storage, playing local files is discouraged.
+
+ */
diff --git a/src/multimedia/doc/src/qm-external-pages.qdoc b/src/multimedia/doc/src/qm-external-pages.qdoc
index dcd5e9138..8a28780c0 100644
--- a/src/multimedia/doc/src/qm-external-pages.qdoc
+++ b/src/multimedia/doc/src/qm-external-pages.qdoc
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the FOO module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\externalpage https://gstreamer.freedesktop.org
@@ -228,7 +192,7 @@
/*!
\externalpage https://en.wikipedia.org/wiki/Motion_JPEG
- \title Motion JPEG
+ \title MotionJPEG
*/
/*!
diff --git a/src/multimedia/doc/src/qt6-changes.qdoc b/src/multimedia/doc/src/qt6-changes.qdoc
index dd37a1355..ad5ebab74 100644
--- a/src/multimedia/doc/src/qt6-changes.qdoc
+++ b/src/multimedia/doc/src/qt6-changes.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtmultimedia-changes-qt6.html
diff --git a/src/multimedia/doc/src/qtmultimedia-building-from-source.qdoc b/src/multimedia/doc/src/qtmultimedia-building-from-source.qdoc
new file mode 100644
index 000000000..df434c699
--- /dev/null
+++ b/src/multimedia/doc/src/qtmultimedia-building-from-source.qdoc
@@ -0,0 +1,94 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtmultimedia-building-from-source.html
+\title Building Qt Multimedia from sources
+\brief This document describes how to build Qt Multimedia with full
+feature support from source code.
+
+This page describes the process of configuring and building \l{Qt
+Multimedia}. This description assumes familiarity with \l{Building Qt
+Sources} which specifies build requirements for your platform, as well
+as an overview of \l{Qt Configure Options}. For platform-specific
+considerations related to the Qt Multimedia module, see \l{Platform
+Notes} below.
+
+\section1 Building from source
+
+Building Qt Multimedia with full feature support depends on \l
+{https://ffmpeg.org/}{FFmpeg} headers and libraries on most platforms.
+It is possible to build Qt Multimedia without the Qt Multimedia FFmpeg
+media backend, but this is only recommended when building for platforms
+where the FFmpeg backend is not supported.
+
+FFmpeg developer libraries required to build Qt Multimedia can be built
+from sources or downloaded as binary packages. Qt Multimedia can use
+either static linking or dynamic linking to FFmpeg libraries. We
+recommend using the same major version of FFmpeg that is listed in
+\l{FFmpeg as the default backend}.
+
+To build Qt Multimedia with FFmpeg support, specify the \c{-DFFMPEG_DIR}
+CMake variable on the configure command line when building Qt. Note the
+\c{--} separator which separates ordinary configure arguments from CMake
+parameters.
+
+\badcode
+qt-source/configure -- -DFFMPEG_DIR=<FFMPEG_DIR>
+\endcode
+
+Here, \c{<FFMPEG_DIR>} is the directory containing the FFmpeg include,
+lib, and bin directories. To build Qt Multimedia without FFmpeg, omit
+the \c{<FFMPEG_DIR>} variable or specify the \c{-no-feature-ffmpeg}
+configure option.
+
+If you prefer not to build all Qt's submodules, you can reduce configure
+and build times using the \c{-submodules} configure option. This will
+configure a build that only builds Qt Multimedia and its dependencies.
+
+\badcode
+qt-source/configure -submodules qtmultimedia -- -DFFMPEG_DIR=<FFMPEG_DIR>
+\endcode
+
+If you configure Qt Multimedia against FFmpeg built with shared
+libraries (dynamic linking), the FFmpeg shared libraries must be in the
+module loader's search path to run tests or use examples.
+
+\note Qt Multimedia requires the FFmpeg avformat, avcodec, swresample,
+swscale, and avutil libraries during runtime to be able to use the
+FFmpeg media backend. If one or more of these dynamic libraries are not
+found during application startup, the FFmpeg media backend will fail to
+load, and the system will attempt to load the native backend. Qt
+Multimedia doesn't support as many features on native backends.
+
+If you don't already have these libraries in the \c{path}, specify the
+\c{-DQT_DEPLOY_FFMPEG=ON} configure option. With this option enabled,
+the necessary FFmpeg binaries will be copied to Qt's install directory
+during the build and install steps:
+
+\badcode
+qt-source/configure -submodules qtmultimedia -- -DFFMPEG_DIR=<FFMPEG_DIR> -DQT_DEPLOY_FFMPEG=ON
+\endcode
+
+After configuring Qt Multimedia, carefully review the configure summary
+(found in the config.summary file). You can verify that FFmpeg is found
+under the "Plugin" section. Then follow the regular build and install
+steps described in \l{Building Qt Sources}.
+
+\section1 Platform Notes
+
+\section2 Linux
+
+\list
+ \li When configuring Qt Multimedia with FFmpeg enabled, the
+ pulseaudio development package is required. Without this
+ package, FFmpeg will not be recognized.
+ \li When using a version of FFmpeg that is built with VAAPI support,
+ we recommend building Qt Multimedia with VAAPI support as well
+ to make hardware texture conversion possible. To configure Qt
+ Multimedia with VAAPI support, VAAPI developer libraries must be
+ installed on your system. Review the config.summary file to
+ verify that VAAPI support is enabled under the "Hardware
+ acceleration and features" section.
+\endlist
+*/
diff --git a/src/multimedia/doc/src/qtmultimedia-cpp.qdoc b/src/multimedia/doc/src/qtmultimedia-cpp.qdoc
index 01d5579af..b0c4ea732 100644
--- a/src/multimedia/doc/src/qtmultimedia-cpp.qdoc
+++ b/src/multimedia/doc/src/qtmultimedia-cpp.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\module QtMultimedia
@@ -35,13 +11,11 @@
\brief The \l {Qt Multimedia} module provides audio, video and camera
functionality.
- To enable Qt Multimedia in a project, add this directive into your
- C++ files:
-
- \snippet doc_src_qtmultimedia.cpp 0
-
\include module-use.qdocinc using qt module
- \snippet ../src/multimedia/doc/snippets/CMakeLists.txt 0
+ \code
+ find_package(Qt6 REQUIRED COMPONENTS Multimedia)
+ target_link_libraries(mytarget PRIVATE Qt6::Multimedia)
+ \endcode
*/
/*!
@@ -65,4 +39,7 @@
\section2 Qt Multimedia Widgets Module
\generatelist {classesbymodule QtMultimediaWidgets}
+
+ \section2 Qt Spatial Audio Module
+ \generatelist {classesbymodule QtSpatialAudio}
*/
diff --git a/src/multimedia/doc/src/qtmultimedia-examples.qdoc b/src/multimedia/doc/src/qtmultimedia-examples.qdoc
index 40e69efbd..e53eed32f 100644
--- a/src/multimedia/doc/src/qtmultimedia-examples.qdoc
+++ b/src/multimedia/doc/src/qtmultimedia-examples.qdoc
@@ -1,33 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\group multimedia_examples
- \ingroup all-examples
\title Qt Multimedia Examples
\brief Demonstrates the multimedia functionality provided by Qt.
diff --git a/src/multimedia/doc/src/qtmultimedia-index.qdoc b/src/multimedia/doc/src/qtmultimedia-index.qdoc
index 74db2036e..67b6688be 100644
--- a/src/multimedia/doc/src/qtmultimedia-index.qdoc
+++ b/src/multimedia/doc/src/qtmultimedia-index.qdoc
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt for Multimedia module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtmultimedia-index.html
@@ -47,7 +11,7 @@
Qt Multimedia is an add-on module that provides a rich set of QML types
and C++ classes to handle multimedia content. It contains an easy to use
API for playing back audio and video files and rendering those on screen, as
- well as a comprehensive API for recording audio and video from the systems
+ well as a comprehensive API for recording audio and video from the system's
cameras and microphones.
The functionality of this module is divided into the following submodules:
@@ -55,10 +19,13 @@
\table
\row
\li \l{Multimedia Overview}{Qt Multimedia}
- \li Provides API for multimedia-specific use cases.
+ \li Provides an API for multimedia-specific use cases.
\row
\li \l{Qt Multimedia Widgets}
- \li Provides the widget-based multimedia API.
+ \li Provides a widget-based multimedia API.
+ \row
+ \li \l{Qt Spatial Audio}
+ \li Provides an API for implementing sound fields in 3D space.
\endtable
\section1 Getting started
@@ -72,17 +39,6 @@
import QtMultimedia
\endqml
- If you want to use the C++ classes in your application, include the C++
- definitions using the following directive:
-
- \code
- #include <QtMultimedia>
- #include <QtMultimediaWidgets>
- \endcode
-
- \note If you are using a few classes from this module, we recommend
- including those specific classes only instead of the module.
-
To link against the C++ libraries, add the following to your project's
\c CMakeLists.txt file. Substitute \c my_project with the name of your
project.
@@ -92,18 +48,22 @@
target_link_libraries(my_project PRIVATE Qt6::Multimedia)
\endcode
- \section1 Overviews and Important Topics
+ See \l {Building Qt Multimedia from sources} for guidance on building
+ Qt Multimedia from sources.
+
+ \section1 Overviews and important topics
\list
\li \l{Changes to Qt Multimedia}{Changes in Qt 6}
\li \l{Multimedia Overview}
\li \l{Audio Overview}
+ \li \l{Spatial Audio Overview}
\li \l{Video Overview}
\li \l{Camera Overview}
\li \l{Supported Media Formats}
\endlist
- \section1 QML Types
+ \section1 QML types
The following table outlines some important QML types.
@@ -139,11 +99,17 @@
\li \l {QtMultimedia::Video}{Video}
\li Add Video playback functionality to a scene. Uses MediaPlayer and
VideoOutput types to provide video playback functionality.
+ \row
+ \li \l {QtMultimedia::ScreenCapture}{ScreenCapture}
+ \li Captures a screen.
+ \row
+ \li \l {QtMultimedia::WindowCapture}{WindowCapture}
+ \li Captures a top-level window.
\endtable
- \section1 C++ Classes
+ \section1 C++ classes
- The following table outlines some important C++ Classes
+ The following table outlines some important C++ classes
\table
\header
@@ -179,13 +145,19 @@
\row
\li QAudioSink
\li Sends raw audio data to an audio output device.
+ \row
+ \li QScreenCapture
+ \li Captures a screen.
+ \row
+ \li QWindowCapture
+ \li Captures a top-level window.
\endtable
For playback QMediaPlayer, QAudioOutput and QVideoOutput contain all the required functionality.
The other classes are used for capturing audio and video content, where the QMediaCaptureSession is the central
class managing the whole capture/recording process.
- \section1 Licenses and Attributions
+ \section1 Licenses and attributions
The Qt Multimedia module is available under commercial licenses from
\l{The Qt Company}.
@@ -195,31 +167,117 @@
the \l{GNU General Public License, version 2}.
See \l{Qt Licensing} for further details.
- Furthermore, Qt Multimedia in Qt \QtVersion may contain third party
- modules under the following permissive licenses:
+ Furthermore, Qt Multimedia in Qt \QtVersion may contain third-party modules
+ under following permissive licenses:
\generatelist{groupsbymodule attributions-qtmultimedia}
- \section1 Platform Notes
+ Note that video compression standards, such as the H.264 media compression
+ standard, may be covered by patents and can incur royalty fees. This can
+ apply to any implementation, also if the implementation is provided as an
+ operating system service, through a third party library, or through any of
+ Qt Multimedia's backends. Such fees are not covered by the Qt licenses.
+
+ \section1 Target platform and backend notes
+ We aim to align the behavior on all the platforms but there are some issues
+ to consider.
+
+ \section2 Backends
+ On most platforms, there are two different backends that can be used for
+ Qt Multimedia.
+
+ \section3 FFmpeg as the default backend
+ In this release the \l {http://ffmpeg.org}{FFmpeg framework} is set as the
+ default backend on Windows, macOS, Android, and Linux except Yocto distribution.
- For most features, Qt Multimedia builds upon the multimedia framework of the
- underlying operating system. Therefore there are several multimedia back ends
- based on different technologies and APIs.
+ The version shipped with Qt binary packages is \b{FFmpeg 6.1.1} and is tested
+ by the maintainers.
- While we try to support our full API on all platforms, platform specific limitations do
- exist in a few places. This is due to the fact that the feature set supported by those
- frameworks varies, implying that some functionality
- might not be available on all platforms. This is especially true for the set of
- supported file formats and codecs, as well as advanced camera functionality.
+ \note On the Windows platform, Qt's FFmpeg media backend uses
+ dynamic linking to the FFmpeg libraries. Windows applications must
+ therefore bundle FFmpeg binaries in their installer, and make them
+ visible to the application according to Windows dll loading rules.
+ We recommend to store the FFmpeg dlls in the same directory as the
+ application's executable file, because this guarantees that the
+ correct build of FFmpeg is being used if multiple versions are
+ available on the system. All necessary FFmpeg dlls are shipped with
+ the Qt Online Installer and are automatically deployed if the
+ windeployqt tool is used to create the deployment. Applications can
+ also deploy their own build of FFmpeg, as long as the FFmpeg major
+ version matches the version used by Qt.
- Where limitations exist, we aim to document those in the respective classes and
- methods.
+ \note See \l{Licenses and Attributions} regarding what components are removed
+ in the package shipped by Qt.
+
+ \section3 Native backends
+ These are:
+ \list
+ \li gstreamer on Linux
+ \li AVFoundation on macOS and iOS
+ \li WMF Windows
+ \li MediaCodec framework on Android
+ \endlist
+
+ \note These are still available but with \b limited support. The gstreamer
+ backend is only available on Linux.
+
+ \section2 Backend support
+ Maintainers will strive to fix critical issues with the native backends but
+ don't guarantee fixing minor issues and won't implement new features for the
+ native backends. Furthermore, even some major issues with the gstreamer
+ backend (on Linux) won't be fixed since gstreamer is difficult to debug and
+ is further complicated as it is dependent on Linux distributions.
+
+ We aim to align the behavior on all the platforms, especially, with the
+ FFmpeg backend. Despite this fact we still have platform-dependent issues
+ with formats, codecs, advanced camera features, and screen capturing due to
+ the Qt Multimedia API relying on target platform APIs. For example, with FFmpeg,
+ there are specific problems with hardware acceleration on Linux targets with
+ ARM architectures.
+
+ Backend-dependent limitations will be documented and their status maintained
+ in the respective classes.
+
+ \section2 Changing backends
+
+ In the case of issues with the default FFmpeg backend, we suggest testing with a native backend.
+ You can switch to native backends by setting the \c{QT_MEDIA_BACKEND} environment variable
+ to \c windows, \c gstreamer (on Linux), \c darwin (on macOS and iOS), or \c android:
+
+ \code
+ export QT_MEDIA_BACKEND=darwin
+ \endcode
+
+ In order to force assign FFmpeg as the used backend, set the variable to \c ffmpeg:
+
+ \code
+ export QT_MEDIA_BACKEND=ffmpeg
+ \endcode
+
+ On the Qt Multimedia compilation stage the default media backend can be configured
+ via cmake variable \c{QT_DEFAULT_MEDIA_BACKEND}.
+
+ \section2 Target platform notes
+ The following pages list issues for specific target platforms that are not
+ related to the multimedia backed.
\list
\li \l{Qt Multimedia on macOS and iOS}{macOS and iOS}
+ \li \l{Qt Multimedia on WebAssembly}{WebAssembly}
\endlist
- \section1 Reference and Examples
+ \section1 Permissions
+
+ Starting from Qt 6.6, the Qt Multimedia module uses new \l QPermission API
+ to handle \l {QCameraPermission}{camera} and
+ \l {QMicrophonePermission}{microphone} permissions. This means that Qt
+ itself no longer queries for these permissions, so this needs to be done
+ directly from the client application.
+
+ Please refer to the \l {Application Permissions} page for an example of how
+ to integrate the new \l QPermission API into the application.
+
+ \section1 Reference and examples
\list
\li \l{Qt Multimedia QML Types}{QML Types}
\li \l{Qt Multimedia C++ Classes}{C++ Classes}
diff --git a/src/multimedia/doc/src/qtmultimedia-qml-types.qdoc b/src/multimedia/doc/src/qtmultimedia-qml-types.qdoc
index 16e0e581b..0d61a8917 100644
--- a/src/multimedia/doc/src/qtmultimedia-qml-types.qdoc
+++ b/src/multimedia/doc/src/qtmultimedia-qml-types.qdoc
@@ -1,32 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\qmlmodule QtMultimedia
+\noautolist
\title Qt Multimedia QML Types
\ingroup qmlmodules
\brief Provides QML types for multimedia support.
@@ -44,11 +21,25 @@ The QML types for \l{Qt Multimedia} support the basic use cases such as:
Qt Multimedia QML types can be imported into your application using the
following import statement in your .qml file:
-\qml \QtMinorVersion
+\qml
import QtMultimedia
\endqml
\generatelist qmltypesbymodule QtMultimedia
-\noautolist
+The \QtMultimedia import provides also the following
+\l [QtQml]{QML Value Types}{value types}:
+
+\generatelist qmlvaluetypesbymodule QtMultimedia
+
+\section2 Qt Spatial Audio Module
+
+Qt Spatial Audio QML types can be imported into your application using the
+following import statement in your .qml file:
+
+\qml
+import QtQuick3D.SpatialAudio
+\endqml
+
+\generatelist qmltypesbymodule QtQuick3D.SpatialAudio
*/
diff --git a/src/multimedia/doc/src/spatialaudiooverview.qdoc b/src/multimedia/doc/src/spatialaudiooverview.qdoc
deleted file mode 100644
index 1502829f2..000000000
--- a/src/multimedia/doc/src/spatialaudiooverview.qdoc
+++ /dev/null
@@ -1,96 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt for Multimedia module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
-\page spatialaudiooverview.html
-\title Spatial Audio Overview
-\brief Support for spatial audio.
-
-The Qt Multimedia API provides a number of classes that allow the creation of
-three dimensional sound scene. It is defined by objects located in 3D space
-that emit sound and surrounding geometry that can be modelled using
-one or several rooms. Finally a listener can be placed into this
-sound scene at a specified position and orientation.
-
-There are both C++ and QML APIs that can be used.
-
-\section1 Creating a sound scene
-
-To create the sound scene, one first instantiates a \l QSpatialAudioEngine. This engine
-processes input sound data and geometries to create a realistic
-representation of the sound scene as it would be experienced by a person placed
-at a specific location inside the scene.
-
-The \l QSpatialAudioEngine::OutputMode property can be used to optimize the output either
-for headphones using binaural (virtual 3D) rendering or for a stereo or surround speaker
-configuration.
-
-The output device can be selected using \l QSpatialAudioEngine::outputDevice property.
-
-Once the engine is set up, we can place various sound objects into the scene by creating
-\l QSpatialAudioSoundSource objects and specifying a url to a sound file using the \l
-QSpatialAudioSoundSource::source property.
-
-\l QSpatialAudioListener can be used to define the position and orientation of a person
-listening to the sound scene. At max one listener per engine can be used. If no listener
-is specified, the engine assumes that the listener is at the origin of the coordinate system
-facing into a positive z direction, with positive y pointing upwards.
-
-In addition to sound sources and a listener, you can define a geometry that influences how the
-sound is being experienced by the listener through a set of \l QSpatialAudioRoom objects. Rooms
-are rectangular and support a wide variety of materials for each wall giving a different experience
-with different sound reflections and reverb. Room effects will get applied if the listener is
-located inside one of the rooms. If he is inside multiple rooms, the room with the smallest
-geometrical volume will take precedence.
-
-If you need some stereo overlay that is independent of the position and orientation of
-the listener (such as background music or a voice-over), you can use
-\l QSpatialAudioStereoSource to create the sound overlay.
-
-\section1 Reference Documentation
-
-\section2 C++ Classes
-
-\annotatedlist multimedia_spatialaudio
-
-\section2 QML Types
-
-\annotatedlist quick3d_spatialaudio
-
-*/
diff --git a/src/multimedia/doc/src/videooverview.qdoc b/src/multimedia/doc/src/videooverview.qdoc
index 2988c6e7f..3185c9a02 100644
--- a/src/multimedia/doc/src/videooverview.qdoc
+++ b/src/multimedia/doc/src/videooverview.qdoc
@@ -1,35 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page videooverview.html
\title Video Overview
\brief Video playback
+\ingroup explanations-graphicsandmultimedia
\section1 Video Features
diff --git a/src/multimedia/platform/qplatformaudiodecoder.cpp b/src/multimedia/platform/qplatformaudiodecoder.cpp
index d12d084dd..0bf472410 100644
--- a/src/multimedia/platform/qplatformaudiodecoder.cpp
+++ b/src/multimedia/platform/qplatformaudiodecoder.cpp
@@ -1,122 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformaudiodecoder_p.h"
#include "qthread.h"
QT_BEGIN_NAMESPACE
+QPlatformAudioDecoder::QPlatformAudioDecoder(QAudioDecoder *parent) : q(parent) { }
-/*!
- \class QPlatformAudioDecoder
- \obsolete
- \inmodule QtMultimedia
-
-
- \ingroup multimedia_control
-
- \brief The QPlatformAudioDecoder class provides access to audio decoding
- functionality.
-
- \preliminary
-*/
-
-/*!
- Constructs a new audio decoder control with the given \a parent.
-*/
-QPlatformAudioDecoder::QPlatformAudioDecoder(QAudioDecoder *parent)
- : QObject(parent),
- q(parent)
-{
-}
-
-/*!
- \fn QPlatformAudioDecoder::source() const
-
- Returns the current media source filename, or a null QString if none (or a device)
-*/
-
-/*!
- \fn QPlatformAudioDecoder::setSource(const QUrl &fileName)
-
- Sets the current source to \a fileName. Changing the source will
- stop any current decoding and discard any buffers.
-
- Sources are exclusive, so only one can be set.
-*/
-
-/*!
- \fn QPlatformAudioDecoder::sourceDevice() const
-
- Returns the current media source QIODevice, or 0 if none (or a file).
-*/
-
-/*!
- \fn QPlatformAudioDecoder::setSourceDevice(QIODevice *device)
-
- Sets the current source to \a device. Changing the source will
- stop any current decoding and discard any buffers.
-
- Sources are exclusive, so only one can be set.
-*/
-
-/*!
- \fn QPlatformAudioDecoder::start()
-
- Starts decoding the current media.
-
- \sa read()
-*/
-
-/*!
- \fn QPlatformAudioDecoder::stop()
-
- Stops playback of the current media and discards any buffers.
-
- If successful, the player control will immediately stop decoding.
-*/
-
-/*!
- \fn QPlatformAudioDecoder::error(int error, const QString &errorString)
-
- Signals that an \a error has occurred. The \a errorString provides a more detailed explanation.
-*/
+QPlatformAudioDecoder::~QPlatformAudioDecoder() = default;
void QPlatformAudioDecoder::error(int error, const QString &errorString)
{
@@ -131,11 +23,6 @@ void QPlatformAudioDecoder::error(int error, const QString &errorString)
}
}
-/*!
- \fn QPlatformAudioDecoder::bufferAvailableChanged(bool available)
-
- Signals that the bufferAvailable property has changed to \a available.
-*/
void QPlatformAudioDecoder::bufferAvailableChanged(bool available)
{
if (m_bufferAvailable == available)
@@ -148,11 +35,6 @@ void QPlatformAudioDecoder::bufferAvailableChanged(bool available)
emit q->bufferAvailableChanged(available);
}
-/*!
- \fn QPlatformAudioDecoder::bufferReady()
-
- Signals that a new buffer is ready for reading.
-*/
void QPlatformAudioDecoder::bufferReady()
{
if (QThread::currentThread() != q->thread())
@@ -161,36 +43,16 @@ void QPlatformAudioDecoder::bufferReady()
emit q->bufferReady();
}
-/*!
- \fn QPlatformAudioDecoder::sourceChanged()
-
- Signals that the current source of the decoder has changed.
-
- \sa source(), sourceDevice()
-*/
void QPlatformAudioDecoder::sourceChanged()
{
emit q->sourceChanged();
}
-/*!
- \fn QPlatformAudioDecoder::formatChanged(const QAudioFormat &format)
-
- Signals that the current audio format of the decoder has changed to \a format.
-*/
void QPlatformAudioDecoder::formatChanged(const QAudioFormat &format)
{
emit q->formatChanged(format);
}
-/*!
- \fn void QPlatformAudioDecoder::finished()
-
- Signals that the decoding has finished successfully.
- If decoding fails, error signal is emitted instead.
-
- \sa start(), stop(), error()
-*/
void QPlatformAudioDecoder::finished()
{
durationChanged(-1);
@@ -198,52 +60,22 @@ void QPlatformAudioDecoder::finished()
emit q->finished();
}
-/*!
- \fn void QPlatformAudioDecoder::positionChanged(qint64 position)
-
- Signals that the current \a position of the decoder has changed.
-
- \sa durationChanged()
-*/
void QPlatformAudioDecoder::positionChanged(qint64 position)
{
if (m_position == position)
return;
m_position = position;
- q->positionChanged(position);
+ emit q->positionChanged(position);
}
-/*!
- \fn void QPlatformAudioDecoder::durationChanged(qint64 duration)
-
- Signals that the estimated \a duration of the decoded data has changed.
-
- \sa positionChanged()
-*/
void QPlatformAudioDecoder::durationChanged(qint64 duration)
{
if (m_duration == duration)
return;
m_duration = duration;
- q->durationChanged(duration);
+ emit q->durationChanged(duration);
}
-/*!
- \fn QPlatformAudioDecoder::read()
- Attempts to read a buffer from the decoder, without blocking. Returns invalid buffer if there are
- no decoded buffers available, or on error.
-*/
-
-/*!
- \fn QPlatformAudioDecoder::position() const
- Returns position (in milliseconds) of the last buffer read from
- the decoder or -1 if no buffers have been read.
-*/
-
-/*!
- \fn QPlatformAudioDecoder::duration() const
- Returns total duration (in milliseconds) of the audio stream
- or -1 if not available.
-*/
-
QT_END_NAMESPACE
+
+#include "moc_qplatformaudiodecoder_p.cpp"
diff --git a/src/multimedia/platform/qplatformaudiodecoder_p.h b/src/multimedia/platform/qplatformaudiodecoder_p.h
index 771c4e512..1159a37ca 100644
--- a/src/multimedia/platform/qplatformaudiodecoder_p.h
+++ b/src/multimedia/platform/qplatformaudiodecoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIODECODERCONTROL_H
#define QAUDIODECODERCONTROL_H
@@ -51,12 +15,10 @@
// We mean it.
//
-#include <QtMultimedia/qaudiodecoder.h>
-
-#include <QtCore/qpair.h>
-
#include <QtMultimedia/qaudiobuffer.h>
#include <QtMultimedia/qaudiodecoder.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qurl.h>
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -109,8 +71,11 @@ public:
QAudioDecoder::Error error() const { return m_error; }
QString errorString() const { return m_errorString; }
+ virtual ~QPlatformAudioDecoder();
+
protected:
explicit QPlatformAudioDecoder(QAudioDecoder *parent);
+
private:
QAudioDecoder *q = nullptr;
diff --git a/src/multimedia/platform/qplatformaudioinput_p.h b/src/multimedia/platform/qplatformaudioinput_p.h
index fd1df8d1b..d6497c06e 100644
--- a/src/multimedia/platform/qplatformaudioinput_p.h
+++ b/src/multimedia/platform/qplatformaudioinput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMAUDIOINPUT_H
#define QPLATFORMAUDIOINPUT_H
@@ -62,10 +26,10 @@ class QAudioInput;
class Q_MULTIMEDIA_EXPORT QPlatformAudioInput
{
public:
- QPlatformAudioInput(QAudioInput *qq) : q(qq) {}
- virtual ~QPlatformAudioInput() {}
+ explicit QPlatformAudioInput(QAudioInput *qq) : q(qq) { }
+ virtual ~QPlatformAudioInput() = default;
- virtual void setAudioDevice(const QAudioDevice &/*device*/) {}
+ virtual void setAudioDevice(const QAudioDevice & /*device*/) { }
virtual void setMuted(bool /*muted*/) {}
virtual void setVolume(float /*volume*/) {}
diff --git a/src/multimedia/platform/qplatformaudiooutput_p.h b/src/multimedia/platform/qplatformaudiooutput_p.h
index 300d20e46..b8f5af6f3 100644
--- a/src/multimedia/platform/qplatformaudiooutput_p.h
+++ b/src/multimedia/platform/qplatformaudiooutput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMAUDIOOUTPUT_H
#define QPLATFORMAUDIOOUTPUT_H
@@ -60,8 +24,8 @@ class QAudioOutput;
class Q_MULTIMEDIA_EXPORT QPlatformAudioOutput
{
public:
- QPlatformAudioOutput(QAudioOutput *qq) : q(qq) {}
- virtual ~QPlatformAudioOutput() {}
+ explicit QPlatformAudioOutput(QAudioOutput *qq) : q(qq) { }
+ virtual ~QPlatformAudioOutput() = default;
virtual void setAudioDevice(const QAudioDevice &/*device*/) {}
virtual void setMuted(bool /*muted*/) {}
diff --git a/src/multimedia/platform/qplatformaudioresampler_p.h b/src/multimedia/platform/qplatformaudioresampler_p.h
new file mode 100644
index 000000000..fd8edc2be
--- /dev/null
+++ b/src/multimedia/platform/qplatformaudioresampler_p.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPLATFORMAUDIORESAMPLER_P_H
+#define QPLATFORMAUDIORESAMPLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtmultimediaglobal_p.h>
+#include <qaudiobuffer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPlatformAudioResampler
+{
+public:
+ virtual ~QPlatformAudioResampler() = default;
+
+ virtual QAudioBuffer resample(const char *data, size_t size) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLATFORMAUDIORESAMPLER_P_H
diff --git a/src/multimedia/platform/qplatformcamera.cpp b/src/multimedia/platform/qplatformcamera.cpp
index 9b477a158..1d0e49cdd 100644
--- a/src/multimedia/platform/qplatformcamera.cpp
+++ b/src/multimedia/platform/qplatformcamera.cpp
@@ -1,96 +1,59 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformcamera_p.h"
+#include "private/qcameradevice_p.h"
QT_BEGIN_NAMESPACE
-/*!
- \class QPlatformCamera
- \obsolete
-
+QPlatformCamera::QPlatformCamera(QCamera *parent) : QPlatformVideoSource(parent), m_camera(parent)
+{
+ qRegisterMetaType<QVideoFrame>();
+}
+QCameraFormat QPlatformCamera::findBestCameraFormat(const QCameraDevice &camera) const
+{
+ // check if fmt is better. We try to find the highest resolution that offers
+ // at least 30 FPS
+ // we use 29 FPS to compare against as some cameras report 29.97 FPS...
- \brief The QPlatformCamera class is an abstract base class for
- classes that control still cameras or video cameras.
+ auto makeCriteria = [this](const QCameraFormat &fmt) {
+ constexpr float MinSufficientFrameRate = 29.f;
- \inmodule QtMultimedia
+ const auto isValid = fmt.pixelFormat() != QVideoFrameFormat::Format_Invalid;
+ const auto resolution = fmt.resolution();
+ const auto sufficientFrameRate = std::min(fmt.maxFrameRate(), MinSufficientFrameRate);
+ const auto pixelFormatScore =
+ cameraPixelFormatScore(fmt.pixelFormat(), QCameraFormatPrivate::getColorRange(fmt));
- \ingroup multimedia_control
-*/
+ return std::make_tuple(
+ isValid, // 1st: ensure valid formats
+ sufficientFrameRate, // 2nd: ensure the highest frame rate in the range [0; 29]*/
+ resolution.width() * resolution.height(), // 3rd: ensure the highest resolution
+ pixelFormatScore, // 4th: eshure the best pixel format
+ fmt.maxFrameRate()); // 5th: ensure the highest framerate in the whole range
+ };
-/*!
- Constructs a camera control object with \a parent.
-*/
+ const auto formats = camera.videoFormats();
+ const auto found =
+ std::max_element(formats.begin(), formats.end(),
+ [makeCriteria](const QCameraFormat &fmtA, const QCameraFormat &fmtB) {
+ return makeCriteria(fmtA) < makeCriteria(fmtB);
+ });
-QPlatformCamera::QPlatformCamera(QCamera *parent)
- : QObject(parent),
- m_camera(parent)
-{
+ return found == formats.end() ? QCameraFormat{} : *found;
}
-QCameraFormat QPlatformCamera::findBestCameraFormat(const QCameraDevice &camera)
+QVideoFrameFormat QPlatformCamera::frameFormat() const
{
- QCameraFormat f;
- const auto formats = camera.videoFormats();
- for (const auto &fmt : formats) {
- if (fmt.pixelFormat() == QVideoFrameFormat::Format_Invalid)
- continue;
- // check if fmt is better. We try to find the highest resolution that offers
- // at least 30 FPS
- // we use 29 FPS to compare against as some cameras report 29.97 FPS...
- if (f.maxFrameRate() < 29 && fmt.maxFrameRate() > f.maxFrameRate())
- f = fmt;
- else if (f.maxFrameRate() == fmt.maxFrameRate() &&
- f.resolution().width()*f.resolution().height() < fmt.resolution().width()*fmt.resolution().height())
- f = fmt;
- }
- return f;
+ QVideoFrameFormat result(m_cameraFormat.resolution(),
+ m_framePixelFormat == QVideoFrameFormat::Format_Invalid
+ ? m_cameraFormat.pixelFormat()
+ : m_framePixelFormat);
+ result.setStreamFrameRate(m_cameraFormat.maxFrameRate());
+ return result;
}
-/*!
- \fn void QPlatformCamera::error(int error, const QString &errorString)
-
- Signal emitted when an error occurs with error code \a error and
- a description of the error \a errorString.
-*/
-
void QPlatformCamera::supportedFeaturesChanged(QCamera::Features f)
{
if (m_supportedFeatures == f)
@@ -258,8 +221,6 @@ int QPlatformCamera::colorTemperatureForWhiteBalance(QCamera::WhiteBalanceMode m
return 0;
}
-
-
QT_END_NAMESPACE
#include "moc_qplatformcamera_p.cpp"
diff --git a/src/multimedia/platform/qplatformcamera_p.h b/src/multimedia/platform/qplatformcamera_p.h
index da910ce9e..85624c0ce 100644
--- a/src/multimedia/platform/qplatformcamera_p.h
+++ b/src/multimedia/platform/qplatformcamera_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMCAMERA_H
#define QPLATFORMCAMERA_H
@@ -51,28 +15,21 @@
// We mean it.
//
-#include <QtCore/qobject.h>
-#include <QtMultimedia/qtmultimediaglobal.h>
+#include "qplatformvideosource_p.h"
#include <QtMultimedia/qcamera.h>
-#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
-class Q_MULTIMEDIA_EXPORT QPlatformCamera : public QObject
+class Q_MULTIMEDIA_EXPORT QPlatformCamera : public QPlatformVideoSource
{
Q_OBJECT
public:
- virtual bool isActive() const = 0;
- virtual void setActive(bool active) = 0;
-
virtual void setCamera(const QCameraDevice &camera) = 0;
virtual bool setCameraFormat(const QCameraFormat &/*format*/) { return false; }
QCameraFormat cameraFormat() const { return m_cameraFormat; }
- virtual void setCaptureSession(QPlatformMediaCaptureSession *) {}
-
virtual bool isFocusModeSupported(QCamera::FocusMode mode) const { return mode == QCamera::FocusModeAuto; }
virtual void setFocusMode(QCamera::FocusMode /*mode*/) {}
@@ -102,6 +59,8 @@ public:
virtual void setWhiteBalanceMode(QCamera::WhiteBalanceMode /*mode*/) {}
virtual void setColorTemperature(int /*temperature*/) {}
+ QVideoFrameFormat frameFormat() const override;
+
QCamera::Features supportedFeatures() const { return m_supportedFeatures; }
QCamera::FocusMode focusMode() const { return m_focusMode; }
@@ -151,19 +110,22 @@ public:
static int colorTemperatureForWhiteBalance(QCamera::WhiteBalanceMode mode);
- // Can't use FFmpeg specific struct here, use void * for now.
- virtual const void *ffmpegHWAccel() const { return nullptr; }
-
Q_SIGNALS:
- void activeChanged(bool);
void error(int error, const QString &errorString);
- void newVideoFrame(const QVideoFrame &); // only used by FFmpeg
protected:
explicit QPlatformCamera(QCamera *parent);
- static QCameraFormat findBestCameraFormat(const QCameraDevice &camera);
+ virtual int cameraPixelFormatScore(QVideoFrameFormat::PixelFormat /*format*/,
+ QVideoFrameFormat::ColorRange /*colorRange*/) const
+ {
+ return 0;
+ }
+
+ QCameraFormat findBestCameraFormat(const QCameraDevice &camera) const;
QCameraFormat m_cameraFormat;
+ QVideoFrameFormat::PixelFormat m_framePixelFormat = QVideoFrameFormat::Format_Invalid;
+
private:
QCamera *m_camera = nullptr;
QCamera::Features m_supportedFeatures = {};
diff --git a/src/multimedia/platform/qplatformcapturablewindows_p.h b/src/multimedia/platform/qplatformcapturablewindows_p.h
new file mode 100644
index 000000000..41949aaac
--- /dev/null
+++ b/src/multimedia/platform/qplatformcapturablewindows_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPLATFORMCAPTURABLEWINDOWS_P_H
+#define QPLATFORMCAPTURABLEWINDOWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qtmultimediaglobal_p.h"
+#include "qcapturablewindow.h"
+
+#include <qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCapturableWindow;
+class QCapturableWindowPrivate;
+
+class QPlatformCapturableWindows
+{
+public:
+ QPlatformCapturableWindows() = default;
+
+ virtual ~QPlatformCapturableWindows() = default;
+
+ virtual QList<QCapturableWindow> windows() const { return {}; }
+
+ virtual bool isWindowValid(const QCapturableWindowPrivate &) const { return false; }
+
+ Q_DISABLE_COPY(QPlatformCapturableWindows);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLATFORMCAPTURABLEWINDOWS_P_H
diff --git a/src/multimedia/platform/qplatformimagecapture.cpp b/src/multimedia/platform/qplatformimagecapture.cpp
index 5cfc7e31e..5d8349256 100644
--- a/src/multimedia/platform/qplatformimagecapture.cpp
+++ b/src/multimedia/platform/qplatformimagecapture.cpp
@@ -1,58 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformimagecapture_p.h"
#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
-/*!
- \class QPlatformImageCapture
- \obsolete
-
- \brief The QPlatformImageCapture class provides a control interface
- for image capture services.
-
- \inmodule QtMultimedia
- \ingroup multimedia_control
-
-*/
+QPlatformImageCapture::QPlatformImageCapture(QImageCapture *parent)
+ : QObject(parent),
+ m_imageCapture(parent)
+{
+}
QString QPlatformImageCapture::msgCameraNotReady()
{
@@ -64,114 +22,6 @@ QString QPlatformImageCapture::msgImageCaptureNotSet()
return QImageCapture::tr("No instance of QImageCapture set on QMediaCaptureSession.");
}
-/*!
- Constructs a new image capture control object with the given \a parent
-*/
-QPlatformImageCapture::QPlatformImageCapture(QImageCapture *parent)
- : QObject(parent),
- m_imageCapture(parent)
-{
-}
-
-/*!
- \fn QPlatformImageCapture::isReadyForCapture() const
-
- Identifies if a capture control is ready to perform a capture
- immediately (all the resources necessary for image capture are allocated,
- hardware initialized, flash is charged, etc).
-
- Returns true if the camera is ready for capture; and false if it is not.
-
- It's permissible to call capture() while the camera status is QCamera::ActiveStatus
- regardless of isReadyForCapture property value.
- If camera is not ready to capture image immediately,
- the capture request is queued with all the related camera settings
- to be executed as soon as possible.
-*/
-
-/*!
- \fn QPlatformImageCapture::readyForCaptureChanged(bool ready)
-
- Signals that a capture control's \a ready state has changed.
-*/
-
-/*!
- \fn QPlatformImageCapture::capture(const QString &fileName)
-
- Initiates the capture of an image to \a fileName.
- The \a fileName can be relative or empty,
- in this case the service should use the system specific place
- and file naming scheme.
-
- The Camera service should save all the capture parameters
- like exposure settings or image processing parameters,
- so changes to camera parameters after capture() is called
- do not affect previous capture requests.
-
- Returns the capture request id number, which is used later
- with imageExposed(), imageCaptured() and imageSaved() signals.
-*/
-
-/*!
- \fn QPlatformImageCapture::imageExposed(int requestId)
-
- Signals that an image with it \a requestId
- has just been exposed.
- This signal can be used for the shutter sound or other indicaton.
-*/
-
-/*!
- \fn QPlatformImageCapture::imageCaptured(int requestId, const QImage &preview)
-
- Signals that an image with it \a requestId
- has been captured and a \a preview is available.
-*/
-
-/*!
- \fn QPlatformImageCapture::imageMetadataAvailable(int id, const QMediaMetaData &metaData)
-
- Signals that a metadata for an image with request \a id is available.
-
- This signal should be emitted between imageExposed and imageSaved signals.
-*/
-
-/*!
- \fn QPlatformImageCapture::imageAvailable(int requestId, const QVideoFrame &buffer)
-
- Signals that a captured \a buffer with a \a requestId is available.
-*/
-
-/*!
- \fn QPlatformImageCapture::imageSaved(int requestId, const QString &fileName)
-
- Signals that a captured image with a \a requestId has been saved
- to \a fileName.
-*/
-
-/*!
- \fn QPlatformImageCapture::imageSettings() const
-
- Returns the currently used image encoder settings.
-
- The returned value may be different than passed to setImageSettings()
- if the settings contains defaulted or undefined parameters.
-*/
-
-/*!
- \fn QPlatformImageCapture::setImageSettings(const QImageEncoderSettings &settings)
-
- Sets the selected image encoder \a settings.
-*/
-
-/*!
- \fn QPlatformImageCapture::error(int id, int error, const QString &errorString)
-
- Signals the capture request \a id failed with \a error code and message \a errorString.
-
- \sa QImageCapture::Error
-*/
-
-
QT_END_NAMESPACE
#include "moc_qplatformimagecapture_p.cpp"
diff --git a/src/multimedia/platform/qplatformimagecapture_p.h b/src/multimedia/platform/qplatformimagecapture_p.h
index 3d862fa7d..5bfb15ced 100644
--- a/src/multimedia/platform/qplatformimagecapture_p.h
+++ b/src/multimedia/platform/qplatformimagecapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCAMERAIMAGECAPTURECONTROL_H
#define QCAMERAIMAGECAPTURECONTROL_H
diff --git a/src/multimedia/platform/qplatformmediacapture.cpp b/src/multimedia/platform/qplatformmediacapture.cpp
index b041f69db..c8aded824 100644
--- a/src/multimedia/platform/qplatformmediacapture.cpp
+++ b/src/multimedia/platform/qplatformmediacapture.cpp
@@ -1,52 +1,44 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qtmultimediaglobal_p.h>
-#include "qplatformmediacapture_p.h"
-#include "qaudiodevice.h"
-#include "qaudioinput.h"
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtMultimedia/qaudiodevice.h>
+#include <QtMultimedia/qaudioinput.h>
+#include <QtMultimedia/qmediacapturesession.h>
+#include <QtMultimedia/private/qplatformcamera_p.h>
+#include <QtMultimedia/private/qplatformmediacapture_p.h>
+#include <QtMultimedia/private/qmediacapturesession_p.h>
+#include <QtMultimedia/private/qplatformsurfacecapture_p.h>
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
QT_BEGIN_NAMESPACE
-QPlatformMediaCaptureSession::~QPlatformMediaCaptureSession()
+QPlatformMediaCaptureSession::~QPlatformMediaCaptureSession() = default;
+
+std::vector<QPlatformVideoSource *> QPlatformMediaCaptureSession::activeVideoSources()
{
+ std::vector<QPlatformVideoSource *> result;
+
+ auto checkSource = [&result](QPlatformVideoSource *source) {
+ if (source && source->isActive())
+ result.push_back(source);
+ };
+
+ checkSource(camera());
+ checkSource(screenCapture());
+ checkSource(windowCapture());
+
+ return result;
+}
+
+void *QPlatformMediaCaptureSession::nativePipeline(QMediaCaptureSession *session)
+{
+ auto sessionPrivate = session->d_func();
+ if (!sessionPrivate || !sessionPrivate->captureSession)
+ return nullptr;
+
+ return sessionPrivate->captureSession->nativePipeline();
}
QT_END_NAMESPACE
+#include "moc_qplatformmediacapture_p.cpp"
diff --git a/src/multimedia/platform/qplatformmediacapture_p.h b/src/multimedia/platform/qplatformmediacapture_p.h
index dc6bdd230..981cf199b 100644
--- a/src/multimedia/platform/qplatformmediacapture_p.h
+++ b/src/multimedia/platform/qplatformmediacapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMMEDIACAPTURE_H
#define QPLATFORMMEDIACAPTURE_H
@@ -63,13 +27,15 @@ class QVideoSink;
class QPlatformAudioInput;
class QPlatformAudioOutput;
class QMediaCaptureSession;
+class QPlatformSurfaceCapture;
+class QPlatformVideoSource;
class Q_MULTIMEDIA_EXPORT QPlatformMediaCaptureSession : public QObject
{
Q_OBJECT
public:
QPlatformMediaCaptureSession() = default;
- virtual ~QPlatformMediaCaptureSession();
+ ~QPlatformMediaCaptureSession() override;
void setCaptureSession(QMediaCaptureSession *session) { m_session = session; }
QMediaCaptureSession *captureSession() const { return m_session; }
@@ -77,6 +43,12 @@ public:
virtual QPlatformCamera *camera() = 0;
virtual void setCamera(QPlatformCamera *) {}
+ virtual QPlatformSurfaceCapture *screenCapture() { return nullptr; }
+ virtual void setScreenCapture(QPlatformSurfaceCapture *) {}
+
+ virtual QPlatformSurfaceCapture *windowCapture() { return nullptr; }
+ virtual void setWindowCapture(QPlatformSurfaceCapture *) { }
+
virtual QPlatformImageCapture *imageCapture() = 0;
virtual void setImageCapture(QPlatformImageCapture *) {}
@@ -89,8 +61,18 @@ public:
virtual void setAudioOutput(QPlatformAudioOutput *) {}
+ // TBD: implement ordering of the sources basing on the order of adding
+ std::vector<QPlatformVideoSource *> activeVideoSources();
+
+ virtual void *nativePipeline() { return nullptr; }
+
+ // private API, the purpose is getting GstPipeline
+ static void *nativePipeline(QMediaCaptureSession *);
+
Q_SIGNALS:
void cameraChanged();
+ void screenCaptureChanged();
+ void windowCaptureChanged();
void imageCaptureChanged();
void encoderChanged();
diff --git a/src/multimedia/platform/qplatformmediadevices.cpp b/src/multimedia/platform/qplatformmediadevices.cpp
index 69f5e3054..a6029228d 100644
--- a/src/multimedia/platform/qplatformmediadevices.cpp
+++ b/src/multimedia/platform/qplatformmediadevices.cpp
@@ -1,56 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformmediadevices_p.h"
-#include "qmediadevices.h"
-#include "qaudiodevice.h"
+#include "qplatformmediaintegration_p.h"
#include "qcameradevice.h"
#include "qaudiosystem_p.h"
-
-#include <qmutex.h>
-#include <qloggingcategory.h>
+#include "qaudiodevice.h"
+#include "qplatformvideodevices_p.h"
#if defined(Q_OS_ANDROID)
#include <qandroidmediadevices_p.h>
#elif defined(Q_OS_DARWIN)
#include <qdarwinmediadevices_p.h>
-#elif defined(Q_OS_WINDOWS)
+#elif defined(Q_OS_WINDOWS) && QT_CONFIG(wmf)
#include <qwindowsmediadevices_p.h>
#elif QT_CONFIG(alsa)
#include <qalsamediadevices_p.h>
@@ -65,108 +27,88 @@
QT_BEGIN_NAMESPACE
-namespace {
-struct Holder {
- ~Holder()
- {
- QMutexLocker locker(&mutex);
- delete nativeInstance;
- nativeInstance = nullptr;
- instance = nullptr;
- }
- QBasicMutex mutex;
- QPlatformMediaDevices *instance = nullptr;
- QPlatformMediaDevices *nativeInstance = nullptr;
-} holder;
-
-}
-
-QPlatformMediaDevices *QPlatformMediaDevices::instance()
+std::unique_ptr<QPlatformMediaDevices> QPlatformMediaDevices::create()
{
- QMutexLocker locker(&holder.mutex);
- if (holder.instance)
- return holder.instance;
-
#ifdef Q_OS_DARWIN
- holder.nativeInstance = new QDarwinMediaDevices;
-#elif defined(Q_OS_WINDOWS)
- holder.nativeInstance = new QWindowsMediaDevices;
+ return std::make_unique<QDarwinMediaDevices>();
+#elif defined(Q_OS_WINDOWS) && QT_CONFIG(wmf)
+ return std::make_unique<QWindowsMediaDevices>();
#elif defined(Q_OS_ANDROID)
- holder.nativeInstance = new QAndroidMediaDevices;
+ return std::make_unique<QAndroidMediaDevices>();
#elif QT_CONFIG(alsa)
- holder.nativeInstance = new QAlsaMediaDevices;
+ return std::make_unique<QAlsaMediaDevices>();
#elif QT_CONFIG(pulseaudio)
- holder.nativeInstance = new QPulseAudioMediaDevices;
+ return std::make_unique<QPulseAudioMediaDevices>();
#elif defined(Q_OS_QNX)
- holder.nativeInstance = new QQnxMediaDevices;
+ return std::make_unique<QQnxMediaDevices>();
#elif defined(Q_OS_WASM)
- holder.nativeInstance = new QWasmMediaDevices;
+ return std::make_unique<QWasmMediaDevices>();
+#else
+ return std::make_unique<QPlatformMediaDevices>();
#endif
-
- holder.instance = holder.nativeInstance;
- return holder.instance;
}
+QPlatformMediaDevices::QPlatformMediaDevices() = default;
-QPlatformMediaDevices::QPlatformMediaDevices()
-{}
-
-void QPlatformMediaDevices::setDevices(QPlatformMediaDevices *devices)
+void QPlatformMediaDevices::initVideoDevicesConnection()
{
- holder.instance = devices;
+ // Make sure we are notified if video inputs changed
+ if (const auto videoDevices = QPlatformMediaIntegration::instance()->videoDevices())
+ connect(videoDevices, &QPlatformVideoDevices::videoInputsChanged, this,
+ &QPlatformMediaDevices::videoInputsChanged, Qt::UniqueConnection);
}
QPlatformMediaDevices::~QPlatformMediaDevices() = default;
-QList<QCameraDevice> QPlatformMediaDevices::videoInputs() const
+QList<QAudioDevice> QPlatformMediaDevices::audioInputs() const
{
return {};
}
-QPlatformAudioSource* QPlatformMediaDevices::audioInputDevice(const QAudioFormat &format, const QAudioDevice &deviceInfo)
+QList<QAudioDevice> QPlatformMediaDevices::audioOutputs() const
+{
+ return {};
+}
+
+QPlatformAudioSource *QPlatformMediaDevices::createAudioSource(const QAudioDevice &, QObject *)
+{
+ return nullptr;
+}
+QPlatformAudioSink *QPlatformMediaDevices::createAudioSink(const QAudioDevice &, QObject *)
+{
+ return nullptr;
+}
+
+QPlatformAudioSource *QPlatformMediaDevices::audioInputDevice(const QAudioFormat &format,
+ const QAudioDevice &deviceInfo,
+ QObject *parent)
{
QAudioDevice info = deviceInfo;
if (info.isNull())
info = audioInputs().value(0);
- QPlatformAudioSource* p = !info.isNull() ? createAudioSource(info) : nullptr;
+ QPlatformAudioSource* p = !info.isNull() ? createAudioSource(info, parent) : nullptr;
if (p)
p->setFormat(format);
return p;
}
-QPlatformAudioSink* QPlatformMediaDevices::audioOutputDevice(const QAudioFormat &format, const QAudioDevice &deviceInfo)
+QPlatformAudioSink *QPlatformMediaDevices::audioOutputDevice(const QAudioFormat &format,
+ const QAudioDevice &deviceInfo,
+ QObject *parent)
{
QAudioDevice info = deviceInfo;
if (info.isNull())
info = audioOutputs().value(0);
- QPlatformAudioSink* p = !info.isNull() ? createAudioSink(info) : nullptr;
+ QPlatformAudioSink* p = !info.isNull() ? createAudioSink(info, parent) : nullptr;
if (p)
p->setFormat(format);
return p;
}
-void QPlatformMediaDevices::audioInputsChanged() const
-{
- const auto devices = allMediaDevices();
- for (auto m : devices)
- emit m->audioInputsChanged();
-}
-
-void QPlatformMediaDevices::audioOutputsChanged() const
-{
- const auto devices = allMediaDevices();
- for (auto m : devices)
- emit m->audioOutputsChanged();
-}
-
-void QPlatformMediaDevices::videoInputsChanged() const
-{
- const auto devices = allMediaDevices();
- for (auto m : devices)
- emit m->videoInputsChanged();
-}
-
+void QPlatformMediaDevices::prepareAudio() { }
QT_END_NAMESPACE
+
+#include "moc_qplatformmediadevices_p.cpp"
diff --git a/src/multimedia/platform/qplatformmediadevices_p.h b/src/multimedia/platform/qplatformmediadevices_p.h
index d7048a6ce..0de41a973 100644
--- a/src/multimedia/platform/qplatformmediadevices_p.h
+++ b/src/multimedia/platform/qplatformmediadevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMMEDIADEVICES_H
#define QPLATFORMMEDIADEVICES_H
@@ -53,53 +17,44 @@
#include <private/qtmultimediaglobal_p.h>
#include <qlist.h>
+#include <qobject.h>
+#include <memory>
QT_BEGIN_NAMESPACE
-class QMediaDevices;
class QAudioDevice;
-class QCameraDevice;
class QPlatformAudioSource;
class QPlatformAudioSink;
class QAudioFormat;
-class QPlatformMediaIntegration;
-class Q_MULTIMEDIA_EXPORT QPlatformMediaDevices
+class Q_MULTIMEDIA_EXPORT QPlatformMediaDevices : public QObject
{
+ Q_OBJECT
public:
QPlatformMediaDevices();
- virtual ~QPlatformMediaDevices();
+ ~QPlatformMediaDevices() override;
- static void setDevices(QPlatformMediaDevices *);
- static QPlatformMediaDevices *instance();
+ static std::unique_ptr<QPlatformMediaDevices> create();
- virtual QList<QAudioDevice> audioInputs() const = 0;
- virtual QList<QAudioDevice> audioOutputs() const = 0;
- virtual QList<QCameraDevice> videoInputs() const;
- virtual QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) = 0;
- virtual QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) = 0;
+ virtual QList<QAudioDevice> audioInputs() const;
+ virtual QList<QAudioDevice> audioOutputs() const;
- QPlatformAudioSource *audioInputDevice(const QAudioFormat &format, const QAudioDevice &deviceInfo);
- QPlatformAudioSink *audioOutputDevice(const QAudioFormat &format, const QAudioDevice &deviceInfo);
+ virtual QPlatformAudioSource *createAudioSource(const QAudioDevice &, QObject *parent);
+ virtual QPlatformAudioSink *createAudioSink(const QAudioDevice &, QObject *parent);
- void addMediaDevices(QMediaDevices *m)
- {
- m_devices.append(m);
- }
- void removeMediaDevices(QMediaDevices *m)
- {
- m_devices.removeAll(m);
- }
+ QPlatformAudioSource *audioInputDevice(const QAudioFormat &format,
+ const QAudioDevice &deviceInfo, QObject *parent);
+ QPlatformAudioSink *audioOutputDevice(const QAudioFormat &format,
+ const QAudioDevice &deviceInfo, QObject *parent);
- QList<QMediaDevices *> allMediaDevices() const { return m_devices; }
+ virtual void prepareAudio();
- void videoInputsChanged() const;
+ void initVideoDevicesConnection();
-protected:
- void audioInputsChanged() const;
- void audioOutputsChanged() const;
-
- QList<QMediaDevices *> m_devices;
+Q_SIGNALS:
+ void audioInputsChanged();
+ void audioOutputsChanged();
+ void videoInputsChanged();
};
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qplatformmediaformatinfo.cpp b/src/multimedia/platform/qplatformmediaformatinfo.cpp
index d4bfecafc..e69b32ed3 100644
--- a/src/multimedia/platform/qplatformmediaformatinfo.cpp
+++ b/src/multimedia/platform/qplatformmediaformatinfo.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformmediaformatinfo_p.h"
#include <qset.h>
diff --git a/src/multimedia/platform/qplatformmediaformatinfo_p.h b/src/multimedia/platform/qplatformmediaformatinfo_p.h
index 8842304ca..4229a3c4c 100644
--- a/src/multimedia/platform/qplatformmediaformatinfo_p.h
+++ b/src/multimedia/platform/qplatformmediaformatinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMMEDIAFORMATINFO_H
#define QPLATFORMMEDIAFORMATINFO_H
diff --git a/src/multimedia/platform/qplatformmediaintegration.cpp b/src/multimedia/platform/qplatformmediaintegration.cpp
index fbf094290..4bacc488f 100644
--- a/src/multimedia/platform/qplatformmediaintegration.cpp
+++ b/src/multimedia/platform/qplatformmediaintegration.cpp
@@ -1,158 +1,222 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qtmultimediaglobal_p.h>
#include "qplatformmediaintegration_p.h"
-#include "qplatformmediadevices_p.h"
#include <qatomic.h>
#include <qmutex.h>
#include <qplatformaudioinput_p.h>
#include <qplatformaudiooutput_p.h>
+#include <qplatformaudioresampler_p.h>
#include <qplatformvideodevices_p.h>
#include <qmediadevices.h>
#include <qcameradevice.h>
#include <qloggingcategory.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qapplicationstatic.h>
-#include "QtCore/private/qfactoryloader_p.h"
+#include "qplatformcapturablewindows_p.h"
+#include "qplatformmediadevices_p.h"
+#include <QtCore/private/qfactoryloader_p.h>
+#include <QtCore/private/qcoreapplication_p.h>
+#include <private/qplatformmediaformatinfo_p.h>
#include "qplatformmediaplugin_p.h"
-class QDummyIntegration : public QPlatformMediaIntegration
+namespace {
+
+class QFallbackIntegration : public QPlatformMediaIntegration
{
public:
- QDummyIntegration() { qFatal("QtMultimedia is not currently supported on this platform or compiler."); }
- QPlatformMediaFormatInfo *formatInfo() override { return nullptr; }
+ QFallbackIntegration() : QPlatformMediaIntegration(QLatin1String("fallback"))
+ {
+ qWarning("No QtMultimedia backends found. Only QMediaDevices, QAudioDevice, QSoundEffect, QAudioSink, and QAudioSource are available.");
+ }
};
-Q_LOGGING_CATEGORY(qLcMediaPlugin, "qt.multimedia.plugin")
+static Q_LOGGING_CATEGORY(qLcMediaPlugin, "qt.multimedia.plugin")
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QPlatformMediaPlugin_iid,
QLatin1String("/multimedia")))
-static QStringList backends()
+static const auto FFmpegBackend = QStringLiteral("ffmpeg");
+
+static QString defaultBackend(const QStringList &backends)
{
- QStringList list;
+#ifdef QT_DEFAULT_MEDIA_BACKEND
+ auto backend = QString::fromUtf8(QT_DEFAULT_MEDIA_BACKEND);
+ if (backends.contains(backend))
+ return backend;
+#endif
+
+#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
+ // Return ffmpeg backend by default.
+ // Platform backends for the OS list are optionally available but have limited support.
+ if (backends.contains(FFmpegBackend))
+ return FFmpegBackend;
+#else
+ // Return platform backend (non-ffmpeg) by default.
+ if (backends.size() > 1 && backends[0] == FFmpegBackend)
+ return backends[1];
+#endif
+
+ return backends[0];
+}
- if (QFactoryLoader *fl = loader()) {
- const auto keyMap = fl->keyMap();
- for (auto it = keyMap.constBegin(); it != keyMap.constEnd(); ++it)
- if (!list.contains(it.value()))
- list << it.value();
+struct InstanceHolder
+{
+ InstanceHolder()
+ {
+ if (!QCoreApplication::instance())
+ qCCritical(qLcMediaPlugin()) << "Qt Multimedia requires a QCoreApplication instance";
+
+ const QStringList backends = QPlatformMediaIntegration::availableBackends();
+ QString backend = QString::fromUtf8(qgetenv("QT_MEDIA_BACKEND"));
+ if (backend.isEmpty() && !backends.isEmpty())
+ backend = defaultBackend(backends);
+
+ qCDebug(qLcMediaPlugin) << "Loading media backend" << backend;
+ instance.reset(
+ qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader(), backend));
+
+ if (!instance) {
+ // No backends found. Use fallback to support basic functionality
+ instance = std::make_unique<QFallbackIntegration>();
+ }
}
- qCDebug(qLcMediaPlugin) << "Available backends" << list;
- return list;
-}
+ ~InstanceHolder()
+ {
+ instance.reset();
+ qCDebug(qLcMediaPlugin) << "Released media backend";
+ }
+
+ std::unique_ptr<QPlatformMediaIntegration> instance;
+};
+
+Q_APPLICATION_STATIC(InstanceHolder, s_instanceHolder);
+
+} // namespace
QT_BEGIN_NAMESPACE
-namespace {
-struct Holder {
- ~Holder()
- {
- QMutexLocker locker(&mutex);
- instance = nullptr;
- }
- QBasicMutex mutex;
- QPlatformMediaIntegration *instance = nullptr;
- QPlatformMediaIntegration *nativeInstance = nullptr;
-} holder;
+QPlatformMediaIntegration *QPlatformMediaIntegration::instance()
+{
+ return s_instanceHolder->instance.get();
+}
+QList<QCameraDevice> QPlatformMediaIntegration::videoInputs()
+{
+ auto devices = videoDevices();
+ return devices ? devices->videoDevices() : QList<QCameraDevice>{};
}
-QPlatformMediaIntegration *QPlatformMediaIntegration::instance()
+QMaybe<std::unique_ptr<QPlatformAudioResampler>>
+QPlatformMediaIntegration::createAudioResampler(const QAudioFormat &, const QAudioFormat &)
{
- QMutexLocker locker(&holder.mutex);
- if (holder.instance)
- return holder.instance;
+ return notAvailable;
+}
- auto plugins = backends();
+QMaybe<QPlatformAudioInput *> QPlatformMediaIntegration::createAudioInput(QAudioInput *q)
+{
+ return new QPlatformAudioInput(q);
+}
- QString type = QString::fromUtf8(qgetenv("QT_MEDIA_BACKEND"));
- if (type.isEmpty() && !plugins.isEmpty()) {
- type = plugins.first();
- // FIXME: prefer platform specific backend if available over ffmpeg until it becomes mature
- if (type == QStringLiteral("ffmpeg") && plugins.size() > 1)
- type = plugins[1];
- }
+QMaybe<QPlatformAudioOutput *> QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q)
+{
+ return new QPlatformAudioOutput(q);
+}
- qCDebug(qLcMediaPlugin) << "loading backend" << type;
- holder.nativeInstance = qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader(), type);
+QList<QCapturableWindow> QPlatformMediaIntegration::capturableWindowsList()
+{
+ const auto capturableWindows = this->capturableWindows();
+ return capturableWindows ? capturableWindows->windows() : QList<QCapturableWindow>{};
+}
- if (!holder.nativeInstance) {
- qWarning() << "could not load multimedia backend" << type;
- holder.nativeInstance = new QDummyIntegration;
- }
+bool QPlatformMediaIntegration::isCapturableWindowValid(const QCapturableWindowPrivate &window)
+{
+ const auto capturableWindows = this->capturableWindows();
+ return capturableWindows && capturableWindows->isWindowValid(window);
+}
- holder.instance = holder.nativeInstance;
- return holder.instance;
+const QPlatformMediaFormatInfo *QPlatformMediaIntegration::formatInfo()
+{
+ std::call_once(m_formatInfoOnceFlg, [this]() {
+ m_formatInfo.reset(createFormatInfo());
+ Q_ASSERT(m_formatInfo);
+ });
+ return m_formatInfo.get();
}
-/*
- This API is there to be able to test with a mock backend.
-*/
-void QPlatformMediaIntegration::setIntegration(QPlatformMediaIntegration *integration)
+QPlatformMediaFormatInfo *QPlatformMediaIntegration::createFormatInfo()
{
- if (integration)
- holder.instance = integration;
- else
- holder.instance = holder.nativeInstance;
+ return new QPlatformMediaFormatInfo;
}
-QList<QCameraDevice> QPlatformMediaIntegration::videoInputs()
+std::unique_ptr<QPlatformMediaDevices> QPlatformMediaIntegration::createMediaDevices()
{
- return m_videoDevices ? m_videoDevices->videoDevices() : QList<QCameraDevice>{};
+ return QPlatformMediaDevices::create();
}
-QPlatformAudioInput *QPlatformMediaIntegration::createAudioInput(QAudioInput *q)
+// clang-format off
+QPlatformVideoDevices *QPlatformMediaIntegration::videoDevices()
{
- return new QPlatformAudioInput(q);
+ std::call_once(m_videoDevicesOnceFlag,
+ [this]() {
+ m_videoDevices.reset(createVideoDevices());
+ });
+ return m_videoDevices.get();
}
-QPlatformAudioOutput *QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q)
+QPlatformCapturableWindows *QPlatformMediaIntegration::capturableWindows()
{
- return new QPlatformAudioOutput(q);
+ std::call_once(m_capturableWindowsOnceFlag,
+ [this]() {
+ m_capturableWindows.reset(createCapturableWindows());
+ });
+ return m_capturableWindows.get();
}
-QPlatformMediaIntegration::~QPlatformMediaIntegration()
+QPlatformMediaDevices *QPlatformMediaIntegration::mediaDevices()
{
- delete m_videoDevices;
+ std::call_once(m_mediaDevicesOnceFlag, [this] {
+ m_mediaDevices = createMediaDevices();
+ });
+ return m_mediaDevices.get();
}
+// clang-format on
+
+QStringList QPlatformMediaIntegration::availableBackends()
+{
+ QStringList list;
+
+ if (QFactoryLoader *fl = loader()) {
+ const auto keyMap = fl->keyMap();
+ for (auto it = keyMap.constBegin(); it != keyMap.constEnd(); ++it)
+ if (!list.contains(it.value()))
+ list << it.value();
+ }
+
+ qCDebug(qLcMediaPlugin) << "Available backends" << list;
+ return list;
+}
+
+QLatin1String QPlatformMediaIntegration::name()
+{
+ return m_backendName;
+}
+
+QVideoFrame QPlatformMediaIntegration::convertVideoFrame(QVideoFrame &,
+ const QVideoFrameFormat &)
+{
+ return {};
+}
+
+QPlatformMediaIntegration::QPlatformMediaIntegration(QLatin1String name) : m_backendName(name) { }
+
+QPlatformMediaIntegration::~QPlatformMediaIntegration() = default;
+
QT_END_NAMESPACE
+
+#include "moc_qplatformmediaintegration_p.cpp"
diff --git a/src/multimedia/platform/qplatformmediaintegration_p.h b/src/multimedia/platform/qplatformmediaintegration_p.h
index 5a6d07344..b949af02a 100644
--- a/src/multimedia/platform/qplatformmediaintegration_p.h
+++ b/src/multimedia/platform/qplatformmediaintegration_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMMEDIAINTEGRATION_H
#define QPLATFORMMEDIAINTEGRATION_H
@@ -51,13 +15,21 @@
//
#include <private/qtmultimediaglobal_p.h>
+#include <private/qmultimediautils_p.h>
+#include <qcapturablewindow.h>
#include <qmediarecorder.h>
+#include <qstring.h>
+
+#include <memory>
+#include <mutex>
QT_BEGIN_NAMESPACE
class QMediaPlayer;
class QAudioDecoder;
class QCamera;
+class QScreenCapture;
+class QWindowCapture;
class QMediaRecorder;
class QImageCapture;
class QMediaDevices;
@@ -65,7 +37,9 @@ class QPlatformMediaDevices;
class QPlatformMediaCaptureSession;
class QPlatformMediaPlayer;
class QPlatformAudioDecoder;
+class QPlatformAudioResampler;
class QPlatformCamera;
+class QPlatformSurfaceCapture;
class QPlatformMediaRecorder;
class QPlatformImageCapture;
class QPlatformMediaFormatInfo;
@@ -77,34 +51,78 @@ class QAudioOutput;
class QPlatformAudioInput;
class QPlatformAudioOutput;
class QPlatformVideoDevices;
+class QCapturableWindow;
+class QPlatformCapturableWindows;
+class QVideoFrame;
-class Q_MULTIMEDIA_EXPORT QPlatformMediaIntegration
+class Q_MULTIMEDIA_EXPORT QPlatformMediaIntegration : public QObject
{
+ Q_OBJECT
+ inline static const QString notAvailable = QStringLiteral("Not available");
public:
static QPlatformMediaIntegration *instance();
- // API to be able to test with a mock backend
- static void setIntegration(QPlatformMediaIntegration *);
-
+ explicit QPlatformMediaIntegration(QLatin1String);
virtual ~QPlatformMediaIntegration();
- virtual QPlatformMediaFormatInfo *formatInfo() = 0;
+ const QPlatformMediaFormatInfo *formatInfo();
virtual QList<QCameraDevice> videoInputs();
- virtual QPlatformCamera *createCamera(QCamera *) { return nullptr; }
+ virtual QMaybe<QPlatformCamera *> createCamera(QCamera *) { return notAvailable; }
+ virtual QPlatformSurfaceCapture *createScreenCapture(QScreenCapture *) { return nullptr; }
+ virtual QPlatformSurfaceCapture *createWindowCapture(QWindowCapture *) { return nullptr; }
+
+ virtual QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *) { return notAvailable; }
+ virtual QMaybe<std::unique_ptr<QPlatformAudioResampler>>
+ createAudioResampler(const QAudioFormat & /*inputFormat*/,
+ const QAudioFormat & /*outputFormat*/);
+ virtual QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() { return notAvailable; }
+ virtual QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *) { return notAvailable; }
+ virtual QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *) { return notAvailable; }
+ virtual QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *) { return notAvailable; }
+
+ virtual QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *);
+ virtual QMaybe<QPlatformAudioOutput *> createAudioOutput(QAudioOutput *);
+
+ virtual QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *) { return notAvailable; }
+
+ QList<QCapturableWindow> capturableWindowsList();
+ bool isCapturableWindowValid(const QCapturableWindowPrivate &);
+
+ QPlatformVideoDevices *videoDevices();
+
+ QPlatformCapturableWindows *capturableWindows();
- virtual QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *) { return nullptr; }
- virtual QPlatformMediaCaptureSession *createCaptureSession() { return nullptr; }
- virtual QPlatformMediaPlayer *createPlayer(QMediaPlayer *) { return nullptr; }
- virtual QPlatformMediaRecorder *createRecorder(QMediaRecorder *) { return nullptr; }
- virtual QPlatformImageCapture *createImageCapture(QImageCapture *) { return nullptr; }
+ QPlatformMediaDevices *mediaDevices();
- virtual QPlatformAudioInput *createAudioInput(QAudioInput *);
- virtual QPlatformAudioOutput *createAudioOutput(QAudioOutput *);
+ static QStringList availableBackends();
+ QLatin1String name(); // for unit tests
- virtual QPlatformVideoSink *createVideoSink(QVideoSink *) { return nullptr; }
+ // Convert a QVideoFrame to the destination format
+ virtual QVideoFrame convertVideoFrame(QVideoFrame &, const QVideoFrameFormat &);
protected:
- QPlatformVideoDevices *m_videoDevices = nullptr;
+ virtual QPlatformMediaFormatInfo *createFormatInfo();
+
+ virtual QPlatformVideoDevices *createVideoDevices() { return nullptr; }
+
+ virtual QPlatformCapturableWindows *createCapturableWindows() { return nullptr; }
+
+ virtual std::unique_ptr<QPlatformMediaDevices> createMediaDevices();
+
+private:
+ std::unique_ptr<QPlatformVideoDevices> m_videoDevices;
+ std::once_flag m_videoDevicesOnceFlag;
+
+ std::unique_ptr<QPlatformCapturableWindows> m_capturableWindows;
+ std::once_flag m_capturableWindowsOnceFlag;
+
+ mutable std::unique_ptr<QPlatformMediaFormatInfo> m_formatInfo;
+ mutable std::once_flag m_formatInfoOnceFlg;
+
+ std::unique_ptr<QPlatformMediaDevices> m_mediaDevices;
+ std::once_flag m_mediaDevicesOnceFlag;
+
+ const QLatin1String m_backendName;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qplatformmediaplayer.cpp b/src/multimedia/platform/qplatformmediaplayer.cpp
index 21e25ec9b..ea22f94df 100644
--- a/src/multimedia/platform/qplatformmediaplayer.cpp
+++ b/src/multimedia/platform/qplatformmediaplayer.cpp
@@ -1,92 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformmediaplayer_p.h"
#include <private/qmediaplayer_p.h>
#include "qmediaplayer.h"
+#include "qplatformmediadevices_p.h"
+#include "qplatformmediaintegration_p.h"
QT_BEGIN_NAMESPACE
-
-/*!
- \class QPlatformMediaPlayer
- \internal
-
- \brief The QPlatformMediaPlayer class provides access to the media playing
- functionality.
-
- This control provides a means to set the \l {setMedia()}{media} to play,
- \l {play()}{start}, \l {pause()} {pause} and \l {stop()}{stop} playback,
- \l {setPosition()}{seek}, and control the \l {setVolume()}{volume}.
- It also provides feedback on the \l {duration()}{duration} of the media,
- the current \l {position()}{position}, and \l {bufferProgress()}{buffering}
- progress.
-
- The functionality provided by this control is exposed to application
- code through the QMediaPlayer class.
-
- \sa QMediaPlayer
-*/
+QPlatformMediaPlayer::QPlatformMediaPlayer(QMediaPlayer *parent) : player(parent)
+{
+ QPlatformMediaIntegration::instance()->mediaDevices()->prepareAudio();
+}
QPlatformMediaPlayer::~QPlatformMediaPlayer()
{
}
-/*! \fn QPlatformMediaPlayer::QPlatformMediaPlayer(QMediaPlayer *parent)
-
- Constructs a new media player control with the given \a parent.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::state() const
-
- Returns the state of a player control.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::stateChanged(QMediaPlayer::State newState)
-
- Signals that the state of a player control has changed to \a newState.
-
- \sa state()
-*/
-
void QPlatformMediaPlayer::stateChanged(QMediaPlayer::PlaybackState newState)
{
if (newState == m_state)
@@ -95,20 +26,6 @@ void QPlatformMediaPlayer::stateChanged(QMediaPlayer::PlaybackState newState)
player->d_func()->setState(newState);
}
-/*!
- \fn QPlatformMediaPlayer::mediaStatus() const
-
- Returns the status of the current media.
-*/
-
-
-/*!
- \fn QPlatformMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status)
-
- Signals that the \a status of the current media has changed.
-
- \sa mediaStatus()
-*/
void QPlatformMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status)
{
if (m_status == status)
@@ -119,254 +36,19 @@ void QPlatformMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status)
void QPlatformMediaPlayer::error(int error, const QString &errorString)
{
- player->d_func()->setError(error, errorString);
+ player->d_func()->setError(QMediaPlayer::Error(error), errorString);
}
-/*!
- \fn QPlatformMediaPlayer::duration() const
-
- Returns the duration of the current media in milliseconds.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::durationChanged(qint64 duration)
-
- Signals that the \a duration of the current media has changed.
-
- \sa duration()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::position() const
-
- Returns the current playback position in milliseconds.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::setPosition(qint64 position)
-
- Sets the playback \a position of the current media. This will initiate a seek and it may take
- some time for playback to reach the position set.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::positionChanged(qint64 position)
-
- Signals the playback \a position has changed.
-
- This is only emitted in when there has been a discontinous change in the playback postion, such
- as a seek or the position being reset.
-
- \sa position()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::volume() const
-
- Returns the audio volume of a player control.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::setVolume(int volume)
-
- Sets the audio \a volume of a player control.
-
- The volume is scaled linearly, ranging from \c 0 (silence) to \c 100 (full volume).
-*/
-
-/*!
- \fn QPlatformMediaPlayer::volumeChanged(int volume)
-
- Signals the audio \a volume of a player control has changed.
-
- \sa volume()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::isMuted() const
-
- Returns the mute state of a player control.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::setMuted(bool mute)
-
- Sets the \a mute state of a player control.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::mutedChanged(bool mute)
-
- Signals a change in the \a mute status of a player control.
-
- \sa isMuted()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::bufferProgress() const
-
- Returns the buffering progress of the current media. Progress is measured as a number between
- 0 and 1.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::bufferProgressChanged(float filled)
-
- Signal the amount of the local buffer filled as a relative number between 0 and 1.
-
- \sa bufferProgress()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::isAudioAvailable() const
-
- Identifies if there is audio output available for the current media.
-
- Returns true if audio output is available and false otherwise.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::audioAvailableChanged(bool audioAvailable)
-
- Signals that there has been a change in the availability of audio output \a audioAvailable.
-
- \sa isAudioAvailable()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::isVideoAvailable() const
-
- Identifies if there is video output available for the current media.
-
- Returns true if video output is available and false otherwise.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::videoAvailableChanged(bool videoAvailable)
-
- Signal that the availability of visual content has changed to \a videoAvailable.
-
- \sa isVideoAvailable()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::isSeekable() const
-
- Identifies if the current media is seekable.
-
- Returns true if it possible to seek within the current media, and false otherwise.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::seekableChanged(bool seekable)
-
- Signals that the \a seekable state of a player control has changed.
-
- \sa isSeekable()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::availablePlaybackRanges() const
-
- Returns a range of times in milliseconds that can be played back.
-
- Usually for local files this is a continuous interval equal to [0..duration()]
- or an empty time range if seeking is not supported, but for network sources
- it refers to the buffered parts of the media.
-*/
-
-/*!
- \fn qreal QPlatformMediaPlayer::playbackRate() const
-
- Returns the rate of playback.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::setPlaybackRate(qreal rate)
-
- Sets the \a rate of playback.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::media() const
-
- Returns the current media source.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::mediaStream() const
-
- Returns the current media stream. This is only a valid if a stream was passed to setMedia().
-
- \sa setMedia()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::setMedia(const QUrl &media, QIODevice *stream)
-
- Sets the current \a media source. If a \a stream is supplied; data will be read from that
- instead of attempting to resolve the media source. The media source may still be used to
- supply media information such as mime type.
-
- Setting the media to a null QUrl will cause the control to discard all
- information relating to the current media source and to cease all I/O operations related
- to that media.
-
- Qt resource files are never passed as is. If the control supports
- stream playback, a \a stream is supplied, pointing to an opened
- QFile. Otherwise, the resource is copied into a temporary file and \a media contains the
- url to that file.
-
- \sa streamPlaybackSupported()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::mediaChanged(const QUrl& content)
-
- Signals that the current media \a content has changed.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::play()
-
- Starts playback of the current media.
-
- If successful the player control will immediately enter the \l {QMediaPlayer::PlayingState}
- {playing} state.
-
- \sa state()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::pause()
-
- Pauses playback of the current media.
-
- If successful the player control will immediately enter the \l {QMediaPlayer::PausedState}
- {paused} state.
-
- \sa state(), play(), stop()
-*/
-
-/*!
- \fn QPlatformMediaPlayer::stop()
-
- Stops playback of the current media.
-
- If successful the player control will immediately enter the \l {QMediaPlayer::StoppedState}
- {stopped} state.
-*/
-
-/*!
- \fn QPlatformMediaPlayer::error(int error, const QString &errorString)
-
- Signals that an \a error has occurred. The \a errorString provides a more detailed explanation.
-*/
+void *QPlatformMediaPlayer::nativePipeline(QMediaPlayer *player)
+{
+ if (!player)
+ return nullptr;
-/*!
- \fn QPlatformMediaPlayer::playbackRateChanged(qreal rate)
+ auto playerPrivate = player->d_func();
+ if (!playerPrivate || !playerPrivate->control)
+ return nullptr;
- Signal emitted when playback rate changes to \a rate.
-*/
+ return playerPrivate->control->nativePipeline();
+}
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qplatformmediaplayer_p.h b/src/multimedia/platform/qplatformmediaplayer_p.h
index 0c61685ff..6e3590763 100644
--- a/src/multimedia/platform/qplatformmediaplayer_p.h
+++ b/src/multimedia/platform/qplatformmediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -112,36 +76,36 @@ public:
virtual int activeTrack(TrackType) { return -1; }
virtual void setActiveTrack(TrackType, int /*streamNumber*/) {}
- void durationChanged(qint64 duration) { player->durationChanged(duration); }
+ void durationChanged(qint64 duration) { emit player->durationChanged(duration); }
void positionChanged(qint64 position) {
if (m_position == position)
return;
m_position = position;
- player->positionChanged(position);
+ emit player->positionChanged(position);
}
void audioAvailableChanged(bool audioAvailable) {
if (m_audioAvailable == audioAvailable)
return;
m_audioAvailable = audioAvailable;
- player->hasAudioChanged(audioAvailable);
+ emit player->hasAudioChanged(audioAvailable);
}
void videoAvailableChanged(bool videoAvailable) {
if (m_videoAvailable == videoAvailable)
return;
m_videoAvailable = videoAvailable;
- player->hasVideoChanged(videoAvailable);
+ emit player->hasVideoChanged(videoAvailable);
}
void seekableChanged(bool seekable) {
if (m_seekable == seekable)
return;
m_seekable = seekable;
- player->seekableChanged(seekable);
+ emit player->seekableChanged(seekable);
}
- void playbackRateChanged(qreal rate) { player->playbackRateChanged(rate); }
- void bufferProgressChanged(float progress) { player->bufferProgressChanged(progress); }
- void metaDataChanged() { player->metaDataChanged(); }
- void tracksChanged() { player->tracksChanged(); }
- void activeTracksChanged() { player->activeTracksChanged(); }
+ void playbackRateChanged(qreal rate) { emit player->playbackRateChanged(rate); }
+ void bufferProgressChanged(float progress) { emit player->bufferProgressChanged(progress); }
+ void metaDataChanged() { emit player->metaDataChanged(); }
+ void tracksChanged() { emit player->tracksChanged(); }
+ void activeTracksChanged() { emit player->activeTracksChanged(); }
void stateChanged(QMediaPlayer::PlaybackState newState);
void mediaStatusChanged(QMediaPlayer::MediaStatus status);
@@ -152,17 +116,22 @@ public:
return isSeekable() && (m_loops < 0 || ++m_currentLoop < m_loops);
}
int loops() { return m_loops; }
- void setLoops(int loops) {
+ virtual void setLoops(int loops)
+ {
if (m_loops == loops)
return;
m_loops = loops;
Q_EMIT player->loopsChanged();
}
+ virtual void *nativePipeline() { return nullptr; }
+
+ // private API, the purpose is getting GstPipeline
+ static void *nativePipeline(QMediaPlayer *player);
+
protected:
- explicit QPlatformMediaPlayer(QMediaPlayer *parent = nullptr)
- : player(parent)
- {}
+ explicit QPlatformMediaPlayer(QMediaPlayer *parent = nullptr);
+
private:
QMediaPlayer *player = nullptr;
QMediaPlayer::MediaStatus m_status = QMediaPlayer::NoMedia;
diff --git a/src/multimedia/platform/qplatformmediaplugin.cpp b/src/multimedia/platform/qplatformmediaplugin.cpp
new file mode 100644
index 000000000..7828fa08e
--- /dev/null
+++ b/src/multimedia/platform/qplatformmediaplugin.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qplatformmediaplugin_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QPlatformMediaPlugin::QPlatformMediaPlugin(QObject *parent) : QObject(parent) { }
+
+QPlatformMediaPlugin::~QPlatformMediaPlugin() = default;
+
+QT_END_NAMESPACE
+
+#include "moc_qplatformmediaplugin_p.cpp"
diff --git a/src/multimedia/platform/qplatformmediaplugin_p.h b/src/multimedia/platform/qplatformmediaplugin_p.h
index 974576b73..4c8b9e458 100644
--- a/src/multimedia/platform/qplatformmediaplugin_p.h
+++ b/src/multimedia/platform/qplatformmediaplugin_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtSql module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -66,10 +30,8 @@ class Q_MULTIMEDIA_EXPORT QPlatformMediaPlugin : public QObject
{
Q_OBJECT
public:
- explicit QPlatformMediaPlugin(QObject *parent = nullptr)
- : QObject(parent)
- {}
- ~QPlatformMediaPlugin() = default;
+ explicit QPlatformMediaPlugin(QObject *parent = nullptr);
+ ~QPlatformMediaPlugin() override;
virtual QPlatformMediaIntegration *create(const QString &key) = 0;
diff --git a/src/multimedia/platform/qplatformmediarecorder.cpp b/src/multimedia/platform/qplatformmediarecorder.cpp
index f53ff62e4..30dba0a45 100644
--- a/src/multimedia/platform/qplatformmediarecorder.cpp
+++ b/src/multimedia/platform/qplatformmediarecorder.cpp
@@ -1,142 +1,28 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformmediarecorder_p.h"
+#include "qstandardpaths.h"
+#include "qmediastoragelocation_p.h"
#include <QObject>
QT_BEGIN_NAMESPACE
-
-/*!
- \class QPlatformMediaRecorder
- \obsolete
- \inmodule QtMultimedia
-
-
- \ingroup multimedia_control
-
- \brief The QPlatformMediaRecorder class provides access to the recording
- functionality.
-
- This control provides a means to set the \l {outputLocation()}{output location},
- and record(), pause(), resume(), and stop() recording. It also
- provides feedback on the \l {duration()}{duration} of the recording.
-
- \sa QMediaRecorder
-
-*/
-
-/*!
- Constructs a media recorder control with the given \a parent.
-*/
-
QPlatformMediaRecorder::QPlatformMediaRecorder(QMediaRecorder *parent)
: q(parent)
{
}
-/*!
- \fn QUrl QPlatformMediaRecorder::outputLocation() const
-
- Returns the current output location being used.
-*/
-
-/*!
- \fn bool QPlatformMediaRecorder::setOutputLocation(const QUrl &location)
-
- Sets the output \a location and returns if this operation is successful.
- If file at the output location already exists, it should be overwritten.
-
- The \a location can be relative or empty;
- in this case the service should use the system specific place and file naming scheme.
-
- After recording has started, the backend should report the actual file location
- with actualLocationChanged() signal.
-*/
-
-/*!
- \fn QMediaRecorder::RecorderState QPlatformMediaRecorder::state() const
-
- Return the current recording state.
-*/
-
-/*!
- \fn qint64 QPlatformMediaRecorder::duration() const
-
- Return the current duration in milliseconds.
-*/
-
-/*!
- \fn void QPlatformMediaRecorder::record(QMediaEncoderSettings &settings)
-
- Start media recording in accordance with \a{settings}.
-*/
-
-/*!
- \fn void QPlatformMediaRecorder::pause()
-
- Pause media recording. Not all platforms supports this operation
-*/
-void QPlatformMediaRecorder::pause() {
- error(QMediaRecorder::FormatError, QMediaRecorder::tr("Pause not supported"));
+void QPlatformMediaRecorder::pause()
+{
+ updateError(QMediaRecorder::FormatError, QMediaRecorder::tr("Pause not supported"));
}
-/*!
- \fn void QPlatformMediaRecorder::resume()
-
- Resume media recording. Not all platforms supports this operation
-*/
-void QPlatformMediaRecorder::resume() {
- error(QMediaRecorder::FormatError, QMediaRecorder::tr("Resume not supported"));
+void QPlatformMediaRecorder::resume()
+{
+ updateError(QMediaRecorder::FormatError, QMediaRecorder::tr("Resume not supported"));
}
-/*!
- \fn void QPlatformMediaRecorder::stop()
-
- Stop media recording
-*/
-
-/*!
- \fn void QPlatformMediaRecorder::stateChanged(QMediaRecorder::RecorderState state)
-
- Signals that the \a state of a media recorder has changed.
-*/
void QPlatformMediaRecorder::stateChanged(QMediaRecorder::RecorderState state)
{
if (m_state == state)
@@ -145,13 +31,6 @@ void QPlatformMediaRecorder::stateChanged(QMediaRecorder::RecorderState state)
emit q->recorderStateChanged(state);
}
-/*!
- \fn void QPlatformMediaRecorder::durationChanged(qint64 duration)
-
- Signals that the \a duration of the recorded media has changed.
-
- This only emitted when there is a discontinuous change in the duration such as being reset to 0.
-*/
void QPlatformMediaRecorder::durationChanged(qint64 duration)
{
if (m_duration == duration)
@@ -160,12 +39,6 @@ void QPlatformMediaRecorder::durationChanged(qint64 duration)
emit q->durationChanged(duration);
}
-/*!
- \fn void QPlatformMediaRecorder::actualLocationChanged(const QUrl &location)
-
- Signals that the actual media \a location has changed.
- This signal should be emitted at start of recording.
-*/
void QPlatformMediaRecorder::actualLocationChanged(const QUrl &location)
{
if (m_actualLocation == location)
@@ -174,20 +47,9 @@ void QPlatformMediaRecorder::actualLocationChanged(const QUrl &location)
emit q->actualLocationChanged(location);
}
-/*!
- \fn void QPlatformMediaRecorder::error(QMediaRecorder::Error error, const QString &errorString)
-
- Signals that an \a error has occurred. The \a errorString describes the error.
-*/
-void QPlatformMediaRecorder::error(QMediaRecorder::Error error, const QString &errorString)
+void QPlatformMediaRecorder::updateError(QMediaRecorder::Error error, const QString &errorString)
{
- if (error == m_error && errorString == m_errorString)
- return;
- m_error = error;
- m_errorString = errorString;
- if (error != QMediaRecorder::NoError)
- emit q->errorOccurred(error, errorString);
- emit q->errorChanged();
+ m_error.setAndNotify(error, errorString, *q);
}
void QPlatformMediaRecorder::metaDataChanged()
@@ -195,4 +57,19 @@ void QPlatformMediaRecorder::metaDataChanged()
emit q->metaDataChanged();
}
+QString QPlatformMediaRecorder::findActualLocation(const QMediaEncoderSettings &settings) const
+{
+ const auto audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified;
+
+ const auto primaryLocation =
+ audioOnly ? QStandardPaths::MusicLocation : QStandardPaths::MoviesLocation;
+ const QString suffix = settings.mimeType().preferredSuffix();
+ const QString location = QMediaStorageLocation::generateFileName(
+ outputLocation().toString(QUrl::PreferLocalFile), primaryLocation, suffix);
+
+ Q_ASSERT(!location.isEmpty());
+
+ return location;
+}
+
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qplatformmediarecorder_p.h b/src/multimedia/platform/qplatformmediarecorder_p.h
index 8760100c2..dea45ac70 100644
--- a/src/multimedia/platform/qplatformmediarecorder_p.h
+++ b/src/multimedia/platform/qplatformmediarecorder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -54,10 +18,13 @@
#include <QtCore/qurl.h>
#include <QtCore/qsize.h>
#include <QtCore/qmimetype.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qiodevice.h>
#include <QtMultimedia/qmediarecorder.h>
#include <QtMultimedia/qmediametadata.h>
#include <QtMultimedia/qmediaformat.h>
+#include <QtMultimedia/private/qerrorinfo_p.h>
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -151,14 +118,17 @@ public:
virtual void setMetaData(const QMediaMetaData &) {}
virtual QMediaMetaData metaData() const { return {}; }
- QMediaRecorder::Error error() const { return m_error;}
- QString errorString() const { return m_errorString; }
+ QMediaRecorder::Error error() const { return m_error.code(); }
+ QString errorString() const { return m_error.description(); }
QUrl outputLocation() const { return m_outputLocation; }
virtual void setOutputLocation(const QUrl &location) { m_outputLocation = location; }
QUrl actualLocation() const { return m_actualLocation; }
void clearActualLocation() { m_actualLocation.clear(); }
- void clearError() { error(QMediaRecorder::NoError, QString()); }
+ void clearError() { updateError(QMediaRecorder::NoError, QString()); }
+
+ QIODevice *outputDevice() const { return m_outputDevice; }
+ void setOutputDevice(QIODevice *device) { m_outputDevice = device; }
protected:
explicit QPlatformMediaRecorder(QMediaRecorder *parent);
@@ -166,17 +136,19 @@ protected:
void stateChanged(QMediaRecorder::RecorderState state);
void durationChanged(qint64 position);
void actualLocationChanged(const QUrl &location);
- void error(QMediaRecorder::Error error, const QString &errorString);
+ void updateError(QMediaRecorder::Error error, const QString &errorString);
void metaDataChanged();
QMediaRecorder *mediaRecorder() { return q; }
+ QString findActualLocation(const QMediaEncoderSettings &settings) const;
+
private:
QMediaRecorder *q = nullptr;
- QMediaRecorder::Error m_error = QMediaRecorder::NoError;
- QString m_errorString;
+ QErrorInfo<QMediaRecorder::Error> m_error;
QUrl m_actualLocation;
QUrl m_outputLocation;
+ QPointer<QIODevice> m_outputDevice;
qint64 m_duration = 0;
QMediaRecorder::RecorderState m_state = QMediaRecorder::StoppedState;
diff --git a/src/multimedia/platform/qplatformsurfacecapture.cpp b/src/multimedia/platform/qplatformsurfacecapture.cpp
new file mode 100644
index 000000000..a56f48b62
--- /dev/null
+++ b/src/multimedia/platform/qplatformsurfacecapture.cpp
@@ -0,0 +1,83 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "platform/qplatformsurfacecapture_p.h"
+#include "qvideoframe.h"
+#include "qguiapplication.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+QPlatformSurfaceCapture::QPlatformSurfaceCapture(Source initialSource) : m_source(initialSource)
+{
+ Q_ASSERT(std::visit([](auto source) { return source == decltype(source){}; }, initialSource));
+ qRegisterMetaType<QVideoFrame>();
+}
+
+void QPlatformSurfaceCapture::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+
+ if (!setActiveInternal(active))
+ return;
+
+ m_active = active;
+ emit activeChanged(active);
+}
+
+bool QPlatformSurfaceCapture::isActive() const
+{
+ return m_active;
+}
+
+void QPlatformSurfaceCapture::setSource(Source source)
+{
+ Q_ASSERT(source.index() == m_source.index());
+
+ if (m_source == source)
+ return;
+
+ if (m_active)
+ setActiveInternal(false);
+
+ m_source = source;
+
+ if (m_active && !setActiveInternal(true)) {
+ m_active = false;
+ emit activeChanged(false);
+ }
+
+ std::visit([this](auto source) { emit sourceChanged(source); }, m_source);
+}
+
+QPlatformSurfaceCapture::Error QPlatformSurfaceCapture::error() const
+{
+ return m_error.code();
+}
+
+QString QPlatformSurfaceCapture::errorString() const
+{
+ return m_error.description();
+}
+
+void QPlatformSurfaceCapture::updateError(Error error, const QString &errorString)
+{
+ m_error.setAndNotify(error, errorString, *this);
+}
+
+bool QPlatformSurfaceCapture::checkScreenWithError(ScreenSource &screen)
+{
+ if (!screen)
+ screen = QGuiApplication::primaryScreen();
+
+ if (screen)
+ return true;
+
+ updateError(NotFound, QLatin1String("No screens found"));
+ return false;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qplatformsurfacecapture_p.cpp"
diff --git a/src/multimedia/platform/qplatformsurfacecapture_p.h b/src/multimedia/platform/qplatformsurfacecapture_p.h
new file mode 100644
index 000000000..42fbda474
--- /dev/null
+++ b/src/multimedia/platform/qplatformsurfacecapture_p.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPLATFORMSURFACECAPTURE_H
+#define QPLATFORMSURFACECAPTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qplatformvideosource_p.h"
+#include "qscreen.h"
+#include "qcapturablewindow.h"
+#include "qpointer.h"
+#include "private/qerrorinfo_p.h"
+
+#include <optional>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoFrame;
+
+class Q_MULTIMEDIA_EXPORT QPlatformSurfaceCapture : public QPlatformVideoSource
+{
+ Q_OBJECT
+
+public:
+ enum Error {
+ NoError = 0,
+ InternalError = 1,
+ CapturingNotSupported = 2,
+ CaptureFailed = 4,
+ NotFound = 5,
+ };
+
+ using ScreenSource = QPointer<QScreen>;
+ using WindowSource = QCapturableWindow;
+
+ using Source = std::variant<ScreenSource, WindowSource>;
+
+ explicit QPlatformSurfaceCapture(Source initialSource);
+
+ void setActive(bool active) override;
+ bool isActive() const override;
+
+ void setSource(Source source);
+
+ template<typename Type>
+ Type source() const {
+ return *q_check_ptr(std::get_if<Type>(&m_source));
+ }
+
+ Source source() const { return m_source; }
+
+ Error error() const;
+ QString errorString() const;
+
+protected:
+ virtual bool setActiveInternal(bool) = 0;
+
+ bool checkScreenWithError(ScreenSource &screen);
+
+public Q_SLOTS:
+ void updateError(Error error, const QString &errorString);
+
+Q_SIGNALS:
+ void sourceChanged(WindowSource);
+ void sourceChanged(ScreenSource);
+ void errorChanged();
+ void errorOccurred(Error error, QString errorString);
+
+private:
+ QErrorInfo<Error> m_error;
+ Source m_source;
+ bool m_active = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLATFORMSURFACECAPTURE_H
diff --git a/src/multimedia/platform/qplatformvideodevices.cpp b/src/multimedia/platform/qplatformvideodevices.cpp
index e7f6d24d3..bcf664cd2 100644
--- a/src/multimedia/platform/qplatformvideodevices.cpp
+++ b/src/multimedia/platform/qplatformvideodevices.cpp
@@ -1,55 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformvideodevices_p.h"
-#include "qplatformmediadevices_p.h"
QT_BEGIN_NAMESPACE
-QPlatformVideoDevices::~QPlatformVideoDevices()
-{
-
-}
-
-void QPlatformVideoDevices::videoInputsChanged()
-{
- QPlatformMediaDevices::instance()->videoInputsChanged();
-}
+QPlatformVideoDevices::~QPlatformVideoDevices() = default;
QT_END_NAMESPACE
+
+#include "moc_qplatformvideodevices_p.cpp"
diff --git a/src/multimedia/platform/qplatformvideodevices_p.h b/src/multimedia/platform/qplatformvideodevices_p.h
index 0d49b5bce..008322be2 100644
--- a/src/multimedia/platform/qplatformvideodevices_p.h
+++ b/src/multimedia/platform/qplatformvideodevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMVIDEODEVICES_H
#define QPLATFORMVIDEODEVICES_H
@@ -52,23 +16,28 @@
#include <private/qtmultimediaglobal_p.h>
#include <qmediarecorder.h>
+#include <qobject.h>
QT_BEGIN_NAMESPACE
class QPlatformMediaIntegration;
-class Q_MULTIMEDIA_EXPORT QPlatformVideoDevices
+class Q_MULTIMEDIA_EXPORT QPlatformVideoDevices : public QObject
{
+ Q_OBJECT
public:
QPlatformVideoDevices(QPlatformMediaIntegration *integration)
: m_integration(integration)
{}
- virtual ~QPlatformVideoDevices();
+
+ ~QPlatformVideoDevices() override;
virtual QList<QCameraDevice> videoDevices() const = 0;
-protected:
+Q_SIGNALS:
void videoInputsChanged();
+
+protected:
QPlatformMediaIntegration *m_integration = nullptr;
};
diff --git a/src/multimedia/platform/qplatformvideosink.cpp b/src/multimedia/platform/qplatformvideosink.cpp
index 66126745b..abf82af0f 100644
--- a/src/multimedia/platform/qplatformvideosink.cpp
+++ b/src/multimedia/platform/qplatformvideosink.cpp
@@ -1,98 +1,77 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformvideosink_p.h"
+#include "qmultimediautils_p.h"
QT_BEGIN_NAMESPACE
-/*!
- \class QPlatformVideoSink
- \internal
+QPlatformVideoSink::QPlatformVideoSink(QVideoSink *parent) : QObject(parent), m_sink(parent) { }
- \inmodule QtMultimedia
+QPlatformVideoSink::~QPlatformVideoSink() = default;
- \brief The QPlatformVideoSink class provides a media control for rendering video to a window.
-
- QPlatformVideoSink is one of a number of possible video output controls.
-
- \sa QVideoWidget
-*/
-
-/*!
- Constructs a new video window control with the given \a parent.
-*/
-QPlatformVideoSink::QPlatformVideoSink(QVideoSink *parent)
- : QObject(parent),
- sink(parent)
+QSize QPlatformVideoSink::nativeSize() const
{
+ QMutexLocker locker(&m_mutex);
+ return m_nativeSize;
}
-/*!
- \fn QPlatformVideoSink::setWinId(WId id)
-
- Sets the \a id of the window a video overlay end point renders to.
-*/
-
-/*!
- \fn QPlatformVideoSink::setDisplayRect(const QRect &rect)
- Sets the sub-\a rect of a window where video is displayed.
-*/
-
-/*!
- \fn QPlatformVideoSink::setFullScreen(bool fullScreen)
-
- Sets whether a video overlay is a \a fullScreen overlay.
-*/
+void QPlatformVideoSink::setNativeSize(QSize s)
+{
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_nativeSize == s)
+ return;
+ m_nativeSize = s;
+ }
+ emit m_sink->videoSizeChanged();
+}
-/*!
- \fn QPlatformVideoSink::nativeSize() const
+void QPlatformVideoSink::setVideoFrame(const QVideoFrame &frame)
+{
+ bool sizeChanged = false;
+
+ {
+ QMutexLocker locker(&m_mutex);
+ if (frame == m_currentVideoFrame)
+ return;
+ m_currentVideoFrame = frame;
+ m_currentVideoFrame.setSubtitleText(m_subtitleText);
+ const auto size = qRotatedFrameSize(frame);
+ if (size != m_nativeSize) {
+ m_nativeSize = size;
+ sizeChanged = true;
+ }
+ }
+
+ // emit signals outside the mutex to avoid deadlocks on the user side
+ if (sizeChanged)
+ emit m_sink->videoSizeChanged();
+ emit m_sink->videoFrameChanged(frame);
+}
- Returns a suggested size for the video display based on the resolution and aspect ratio of the
- video.
-*/
+QVideoFrame QPlatformVideoSink::currentVideoFrame() const
+{
+ QMutexLocker locker(&m_mutex);
+ return m_currentVideoFrame;
+}
-/*!
- \fn QPlatformVideoSink::setAspectRatioMode(Qt::AspectRatioMode mode)
+void QPlatformVideoSink::setSubtitleText(const QString &subtitleText)
+{
+ {
+ QMutexLocker locker(&m_mutex);
+ if (m_subtitleText == subtitleText)
+ return;
+ m_subtitleText = subtitleText;
+ }
+ emit m_sink->subtitleTextChanged(subtitleText);
+}
- Sets the aspect ratio \a mode which determines how video is scaled to the fit the display region
- with respect to its aspect ratio.
-*/
+QString QPlatformVideoSink::subtitleText() const
+{
+ QMutexLocker locker(&m_mutex);
+ return m_subtitleText;
+}
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/qplatformvideosink_p.h b/src/multimedia/platform/qplatformvideosink_p.h
index a5dabba9f..53eca374f 100644
--- a/src/multimedia/platform/qplatformvideosink_p.h
+++ b/src/multimedia/platform/qplatformvideosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLATFORMVIDEOSINK_H
#define QPLATFORMVIDEOSINK_H
@@ -72,6 +36,8 @@ class Q_MULTIMEDIA_EXPORT QPlatformVideoSink : public QObject
Q_OBJECT
public:
+ ~QPlatformVideoSink() override;
+
virtual void setRhi(QRhi * /*rhi*/) {}
virtual void setWinId(WId) {}
@@ -79,55 +45,34 @@ public:
virtual void setFullScreen(bool) {}
virtual void setAspectRatioMode(Qt::AspectRatioMode) {}
- QSize nativeSize() const
- {
- QMutexLocker locker(&mutex);
- return m_nativeSize;
- }
+ QSize nativeSize() const;
virtual void setBrightness(float /*brightness*/) {}
virtual void setContrast(float /*contrast*/) {}
virtual void setHue(float /*hue*/) {}
virtual void setSaturation(float /*saturation*/) {}
- QVideoSink *videoSink() { return sink; }
-
- void setNativeSize(QSize s) {
- QMutexLocker locker(&mutex);
- if (m_nativeSize == s)
- return;
- m_nativeSize = s;
- sink->videoSizeChanged();
- }
- virtual void setVideoFrame(const QVideoFrame &frame) {
- setNativeSize(frame.size());
- if (frame == m_currentVideoFrame)
- return;
- m_currentVideoFrame = frame;
- m_currentVideoFrame.setSubtitleText(subtitleText());
- sink->videoFrameChanged(m_currentVideoFrame);
- }
- QVideoFrame currentVideoFrame() const { return m_currentVideoFrame; }
-
- void setSubtitleText(const QString &subtitleText)
- {
- QMutexLocker locker(&mutex);
- if (m_subtitleText == subtitleText)
- return;
- m_subtitleText = subtitleText;
- sink->subtitleTextChanged(subtitleText);
- }
- QString subtitleText() const
- {
- QMutexLocker locker(&mutex);
- return m_subtitleText;
- }
+ QVideoSink *videoSink() { return m_sink; }
+
+ void setNativeSize(QSize s);
+
+ virtual void setVideoFrame(const QVideoFrame &frame);
+
+ QVideoFrame currentVideoFrame() const;
+
+ void setSubtitleText(const QString &subtitleText);
+
+ QString subtitleText() const;
protected:
explicit QPlatformVideoSink(QVideoSink *parent);
- QVideoSink *sink = nullptr;
- mutable QMutex mutex;
+
+Q_SIGNALS:
+ void rhiChanged(QRhi *rhi);
+
private:
+ QVideoSink *m_sink = nullptr;
+ mutable QMutex m_mutex;
QSize m_nativeSize;
QString m_subtitleText;
QVideoFrame m_currentVideoFrame;
diff --git a/src/multimedia/platform/qplatformvideosource.cpp b/src/multimedia/platform/qplatformvideosource.cpp
new file mode 100644
index 000000000..a23ed91ae
--- /dev/null
+++ b/src/multimedia/platform/qplatformvideosource.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qplatformvideosource_p.h"
+
+QT_BEGIN_NAMESPACE
+
+std::optional<int> QPlatformVideoSource::ffmpegHWPixelFormat() const
+{
+ return {};
+};
+
+QT_END_NAMESPACE
+
+//#include "moc_qplatformvideosource_p.cpp
diff --git a/src/multimedia/platform/qplatformvideosource_p.h b/src/multimedia/platform/qplatformvideosource_p.h
new file mode 100644
index 000000000..3ed76d3e2
--- /dev/null
+++ b/src/multimedia/platform/qplatformvideosource_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QPLATFORMVIDEOSOURCE_P_H
+#define QPLATFORMVIDEOSOURCE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qvideoframeformat.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qnativeinterface.h>
+#include <QtCore/private/qglobal_p.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoFrame;
+class QPlatformMediaCaptureSession;
+
+class Q_MULTIMEDIA_EXPORT QPlatformVideoSource : public QObject
+{
+ Q_OBJECT
+public:
+ using QObject::QObject;
+
+ virtual void setActive(bool active) = 0;
+ virtual bool isActive() const = 0;
+
+ virtual QVideoFrameFormat frameFormat() const = 0;
+
+ virtual std::optional<int> ffmpegHWPixelFormat() const;
+
+ virtual void setCaptureSession(QPlatformMediaCaptureSession *) { }
+
+Q_SIGNALS:
+ void newVideoFrame(const QVideoFrame &);
+ void activeChanged(bool);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLATFORMVIDEOSOURCE_P_H
diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp
index ac7c8fa06..dc8e3dab8 100644
--- a/src/multimedia/playback/qmediaplayer.cpp
+++ b/src/multimedia/playback/qmediaplayer.cpp
@@ -1,44 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmediaplayer_p.h"
+#include <private/qmultimediautils_p.h>
#include <private/qplatformmediaintegration_p.h>
#include <qvideosink.h>
#include <qaudiooutput.h>
@@ -47,12 +12,15 @@
#include <QtCore/qmetaobject.h>
#include <QtCore/qtimer.h>
#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
#include <QtCore/qpointer.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qtemporaryfile.h>
-#include <QtCore/qdir.h>
#include <QtCore/qcoreapplication.h>
-#include <QtCore/qjniobject.h>
+
+#if defined(Q_OS_ANDROID)
+# include <QtCore/qjniobject.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -130,13 +98,15 @@ QT_BEGIN_NAMESPACE
\sa AudioOutput, VideoOutput
*/
-void QMediaPlayerPrivate::setState(QMediaPlayer::PlaybackState ps)
+void QMediaPlayerPrivate::setState(QMediaPlayer::PlaybackState toState)
{
Q_Q(QMediaPlayer);
- if (ps != state) {
- state = ps;
- emit q->playbackStateChanged(ps);
+ if (toState != state) {
+ const auto fromState = std::exchange(state, toState);
+ if (toState == QMediaPlayer::PlayingState || fromState == QMediaPlayer::PlayingState)
+ emit q->playingChanged(toState == QMediaPlayer::PlayingState);
+ emit q->playbackStateChanged(toState);
}
}
@@ -147,14 +117,11 @@ void QMediaPlayerPrivate::setStatus(QMediaPlayer::MediaStatus s)
emit q->mediaStatusChanged(s);
}
-void QMediaPlayerPrivate::setError(int error, const QString &errorString)
+void QMediaPlayerPrivate::setError(QMediaPlayer::Error error, const QString &errorString)
{
Q_Q(QMediaPlayer);
- this->error = QMediaPlayer::Error(error);
- this->errorString = errorString;
- emit q->errorChanged();
- emit q->errorOccurred(this->error, errorString);
+ this->error.setAndNotify(error, errorString, *q);
}
void QMediaPlayerPrivate::setMedia(const QUrl &media, QIODevice *stream)
@@ -220,25 +187,14 @@ void QMediaPlayerPrivate::setMedia(const QUrl &media, QIODevice *stream)
qWarning("Qt was built with -no-feature-temporaryfile: playback from resource file is not supported!");
#endif
}
-#if defined(Q_OS_ANDROID)
- } else if (media.scheme() == QLatin1String("content") && !stream) {
- // content scheme should happen only on android
- const int fd = QJniObject::callStaticMethod<jint>(
- "org/qtproject/qt/android/QtNative", "openFdForContentUrl",
- "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I",
- QNativeInterface::QAndroidApplication::context(),
- QJniObject::fromString(media.toString()).object(),
- QJniObject::fromString(QLatin1String("r")).object());
-
- file.reset(new QFile(QLatin1Char(':') + media.path()));
- file->open(fd, QFile::ReadOnly, QFile::FileHandleFlag::AutoCloseHandle);
- control->setMedia(media, file.get());
-#endif
} else {
qrcMedia = QUrl();
- QUrl url = media;
- if (url.scheme().isEmpty() || url.scheme() == QLatin1String("file"))
- url = QUrl::fromUserInput(media.path(), QDir::currentPath(), QUrl::AssumeLocalFile);
+ QUrl url = qMediaFromUserInput(media);
+ if (url.scheme() == QLatin1String("content") && !stream) {
+ file.reset(new QFile(media.url()));
+ stream = file.get();
+ }
+
control->setMedia(url, stream);
}
@@ -266,14 +222,14 @@ QMediaPlayer::QMediaPlayer(QObject *parent)
{
Q_D(QMediaPlayer);
- d->control = QPlatformMediaIntegration::instance()->createPlayer(this);
- if (!d->control) { // ### Should this be an assertion?
- d->setError(QMediaPlayer::ResourceError, QMediaPlayer::tr("Platform does not support media playback."));
- return;
+ auto maybeControl = QPlatformMediaIntegration::instance()->createPlayer(this);
+ if (maybeControl) {
+ d->control = maybeControl.value();
+ d->state = d->control->state();
+ } else {
+ qWarning() << "Failed to initialize QMediaPlayer" << maybeControl.error();
+ d->setError(QMediaPlayer::ResourceError, maybeControl.error());
}
- Q_ASSERT(d->control);
-
- d->state = d->control->state();
}
@@ -285,9 +241,13 @@ QMediaPlayer::~QMediaPlayer()
{
Q_D(QMediaPlayer);
- // Disconnect everything to prevent notifying
- // when a receiver is already destroyed.
- disconnect();
+ // prevents emitting audioOutputChanged and videoOutputChanged.
+ QSignalBlocker blocker(this);
+
+ // Reset audio output and video sink to ensure proper unregistering of the source
+ // To be investigated: registering of the source might be removed after switching on the ffmpeg
+ // backend;
+
setAudioOutput(nullptr);
d->setVideoSink(nullptr);
@@ -316,13 +276,20 @@ const QIODevice *QMediaPlayer::sourceDevice() const
return d->stream;
}
+/*!
+ \property QMediaPlayer::playbackState
+
+ Returns the \l{QMediaPlayer::}{PlaybackState}.
+
+ \sa playing
+*/
QMediaPlayer::PlaybackState QMediaPlayer::playbackState() const
{
Q_D(const QMediaPlayer);
// In case if EndOfMedia status is already received
// but state is not.
- if (d->control != nullptr
+ if (d->control
&& d->control->mediaStatus() == QMediaPlayer::EndOfMedia
&& d->state != d->control->state()) {
return d->control->state();
@@ -347,11 +314,7 @@ QMediaPlayer::MediaStatus QMediaPlayer::mediaStatus() const
qint64 QMediaPlayer::duration() const
{
Q_D(const QMediaPlayer);
-
- if (d->control != nullptr)
- return d->control->duration();
-
- return 0;
+ return d->control ? d->control->duration() : 0;
}
/*!
@@ -364,15 +327,11 @@ qint64 QMediaPlayer::duration() const
qint64 QMediaPlayer::position() const
{
Q_D(const QMediaPlayer);
-
- if (d->control != nullptr)
- return d->control->position();
-
- return 0;
+ return d->control ? d->control->position() : 0;
}
/*!
- Returns a number betwee 0 and 1 when buffering data.
+ Returns a number between 0 and 1 when buffering data.
0 means that there is no buffered data available, playback is usually
stalled in this case. Playback will resume once the buffer reaches 1,
@@ -383,11 +342,7 @@ qint64 QMediaPlayer::position() const
float QMediaPlayer::bufferProgress() const
{
Q_D(const QMediaPlayer);
-
- if (d->control != nullptr)
- return d->control->bufferProgress();
-
- return 0.;
+ return d->control ? d->control->bufferProgress() : 0;
}
/*!
@@ -402,11 +357,7 @@ float QMediaPlayer::bufferProgress() const
QMediaTimeRange QMediaPlayer::bufferedTimeRange() const
{
Q_D(const QMediaPlayer);
-
- if (d->control)
- return d->control->availablePlaybackRanges();
-
- return {};
+ return d->control ? d->control->availablePlaybackRanges() : QMediaTimeRange{};
}
/*!
@@ -422,11 +373,7 @@ QMediaTimeRange QMediaPlayer::bufferedTimeRange() const
bool QMediaPlayer::hasAudio() const
{
Q_D(const QMediaPlayer);
-
- if (d->control != nullptr)
- return d->control->isAudioAvailable();
-
- return false;
+ return d->control && d->control->isAudioAvailable();
}
/*!
@@ -442,11 +389,7 @@ bool QMediaPlayer::hasAudio() const
bool QMediaPlayer::hasVideo() const
{
Q_D(const QMediaPlayer);
-
- if (d->control != nullptr)
- return d->control->isVideoAvailable();
-
- return false;
+ return d->control && d->control->isVideoAvailable();
}
/*!
@@ -458,11 +401,13 @@ bool QMediaPlayer::hasVideo() const
bool QMediaPlayer::isSeekable() const
{
Q_D(const QMediaPlayer);
+ return d->control && d->control->isSeekable();
+}
- if (d->control != nullptr)
- return d->control->isSeekable();
-
- return false;
+bool QMediaPlayer::isPlaying() const
+{
+ Q_D(const QMediaPlayer);
+ return d->state == QMediaPlayer::PlayingState;
}
/*!
@@ -471,11 +416,7 @@ bool QMediaPlayer::isSeekable() const
qreal QMediaPlayer::playbackRate() const
{
Q_D(const QMediaPlayer);
-
- if (d->control != nullptr)
- return d->control->playbackRate();
-
- return 0.0;
+ return d->control ? d->control->playbackRate() : 0.;
}
/*!
@@ -507,11 +448,7 @@ qreal QMediaPlayer::playbackRate() const
int QMediaPlayer::loops() const
{
Q_D(const QMediaPlayer);
-
- if (d->control)
- return d->control->loops();
-
- return 1;
+ return d->control ? d->control->loops() : 1;
}
void QMediaPlayer::setLoops(int loops)
@@ -528,7 +465,7 @@ void QMediaPlayer::setLoops(int loops)
*/
QMediaPlayer::Error QMediaPlayer::error() const
{
- return d_func()->error;
+ return d_func()->error.code();
}
/*!
@@ -545,7 +482,7 @@ QMediaPlayer::Error QMediaPlayer::error() const
*/
QString QMediaPlayer::errorString() const
{
- return d_func()->errorString;
+ return d_func()->error.description();
}
/*!
@@ -553,7 +490,8 @@ QString QMediaPlayer::errorString() const
Starts or resumes playback of the media.
- Sets the \l playbackState property to PlayingState.
+ Sets the \l playbackState property to PlayingState, and changes
+ \l playing to \c true.
*/
/*!
@@ -569,8 +507,7 @@ void QMediaPlayer::play()
return;
// Reset error conditions
- d->error = NoError;
- d->errorString = QString();
+ d->setError(NoError, QString());
d->control->play();
}
@@ -580,7 +517,8 @@ void QMediaPlayer::play()
Pauses playback of the media.
- Sets the \l playbackState property to PausedState.
+ Sets the \l playbackState property to PausedState,
+ and changes \l playing to \c false.
*/
/*!
@@ -592,7 +530,7 @@ void QMediaPlayer::pause()
{
Q_D(QMediaPlayer);
- if (d->control != nullptr)
+ if (d->control)
d->control->pause();
}
@@ -601,7 +539,8 @@ void QMediaPlayer::pause()
Stops playback of the media.
- Sets the \l playbackState property to StoppedState.
+ Sets the \l playbackState property to StoppedState,
+ and changes \l playing to \c false.
*/
/*!
@@ -613,7 +552,7 @@ void QMediaPlayer::stop()
{
Q_D(QMediaPlayer);
- if (d->control != nullptr)
+ if (d->control)
d->control->stop();
}
@@ -621,7 +560,7 @@ void QMediaPlayer::setPosition(qint64 position)
{
Q_D(QMediaPlayer);
- if (d->control == nullptr)
+ if (!d->control)
return;
if (!d->control->isSeekable())
return;
@@ -632,7 +571,7 @@ void QMediaPlayer::setPlaybackRate(qreal rate)
{
Q_D(QMediaPlayer);
- if (d->control != nullptr)
+ if (d->control)
d->control->setPlaybackRate(rate);
}
@@ -651,7 +590,7 @@ void QMediaPlayer::setPlaybackRate(qreal rate)
Setting the media to a null QUrl will cause the player to discard all
information relating to the current media source and to cease all I/O operations related
- to that media.
+ to that media. Setting the media will stop the playback.
\note This function returns immediately after recording the specified source of the media.
It does not wait for the media to finish loading and does not check for errors. Listen for
@@ -729,12 +668,14 @@ void QMediaPlayer::setAudioOutput(QAudioOutput *output)
if (oldOutput == output)
return;
d->audioOutput = output;
- d->control->setAudioOutput(nullptr);
+ if (d->control)
+ d->control->setAudioOutput(nullptr);
if (oldOutput)
oldOutput->setDisconnectFunction({});
if (output) {
output->setDisconnectFunction([this](){ setAudioOutput(nullptr); });
- d->control->setAudioOutput(output->handle());
+ if (d->control)
+ d->control->setAudioOutput(output->handle());
}
emit audioOutputChanged();
}
@@ -759,6 +700,8 @@ QAudioOutput *QMediaPlayer::audioOutput() const
*/
/*!
+ \property QMediaPlayer::audioTracks
+
Lists the set of available audio tracks inside the media.
The QMediaMetaData returned describes the properties of individual
@@ -784,6 +727,8 @@ QList<QMediaMetaData> QMediaPlayer::audioTracks() const
*/
/*!
+ \property QMediaPlayer::videoTracks
+
Lists the set of available video tracks inside the media.
The QMediaMetaData returned describes the properties of individual
@@ -809,6 +754,8 @@ QList<QMediaMetaData> QMediaPlayer::videoTracks() const
*/
/*!
+ \property QMediaPlayer::subtitleTracks
+
Lists the set of available subtitle tracks inside the media.
The QMediaMetaData returned describes the properties of individual
@@ -840,9 +787,7 @@ QList<QMediaMetaData> QMediaPlayer::subtitleTracks() const
int QMediaPlayer::activeAudioTrack() const
{
Q_D(const QMediaPlayer);
- if (d->control)
- return d->control->activeTrack(QPlatformMediaPlayer::AudioStream);
- return 0;
+ return d->control ? d->control->activeTrack(QPlatformMediaPlayer::AudioStream) : 0;
}
/*!
@@ -866,9 +811,7 @@ int QMediaPlayer::activeAudioTrack() const
int QMediaPlayer::activeVideoTrack() const
{
Q_D(const QMediaPlayer);
- if (d->control)
- return d->control->activeTrack(QPlatformMediaPlayer::VideoStream);
- return -1;
+ return d->control ? d->control->activeTrack(QPlatformMediaPlayer::VideoStream) : -1;
}
/*!
@@ -892,9 +835,7 @@ int QMediaPlayer::activeVideoTrack() const
int QMediaPlayer::activeSubtitleTrack() const
{
Q_D(const QMediaPlayer);
- if (d->control)
- return d->control->activeTrack(QPlatformMediaPlayer::SubtitleStream);
- return -1;
+ return d->control ? d->control->activeTrack(QPlatformMediaPlayer::SubtitleStream) : -1;
}
void QMediaPlayer::setActiveAudioTrack(int index)
@@ -958,31 +899,32 @@ QObject *QMediaPlayer::videoOutput() const
void QMediaPlayer::setVideoOutput(QObject *output)
{
Q_D(QMediaPlayer);
- if (!d->control)
- return;
if (d->videoOutput == output)
return;
- QVideoSink *sink = qobject_cast<QVideoSink *>(output);
+ auto *sink = qobject_cast<QVideoSink *>(output);
if (!sink && output) {
auto *mo = output->metaObject();
- if (output)
- mo->invokeMethod(output, "videoSink", Q_RETURN_ARG(QVideoSink *, sink));
+ mo->invokeMethod(output, "videoSink", Q_RETURN_ARG(QVideoSink *, sink));
}
d->videoOutput = output;
d->setVideoSink(sink);
}
+/*!
+ Sets \a sink to be the QVideoSink instance to
+ retrieve video data.
+*/
void QMediaPlayer::setVideoSink(QVideoSink *sink)
{
Q_D(QMediaPlayer);
- if (!d->control)
- return;
-
d->videoOutput = nullptr;
d->setVideoSink(sink);
}
+/*!
+ Returns the QVideoSink instance.
+*/
QVideoSink *QMediaPlayer::videoSink() const
{
Q_D(const QMediaPlayer);
@@ -1013,11 +955,7 @@ void QMediaPlayer::setVideoOutput(const QList<QVideoSink *> &sinks)
bool QMediaPlayer::isAvailable() const
{
Q_D(const QMediaPlayer);
-
- if (!d->control)
- return false;
-
- return true;
+ return bool(d->control);
}
/*!
@@ -1032,6 +970,8 @@ bool QMediaPlayer::isAvailable() const
*/
/*!
+ \property QMediaPlayer::metaData
+
Returns meta data for the current media used by the media player.
Meta data can contain information such as the title of the video or its creation date.
@@ -1042,7 +982,7 @@ bool QMediaPlayer::isAvailable() const
QMediaMetaData QMediaPlayer::metaData() const
{
Q_D(const QMediaPlayer);
- return d->control->metaData();
+ return d->control ? d->control->metaData() : QMediaMetaData{};
}
// Enums
@@ -1053,7 +993,7 @@ QMediaMetaData QMediaPlayer::metaData() const
\value StoppedState The media player is not playing content, playback will begin from the start
of the current track.
- \value PlayingState The media player is currently playing content.
+ \value PlayingState The media player is currently playing content. This indicates the same as the \l playing property.
\value PausedState The media player has paused playback, playback of the current track will
resume from the position the player was paused at.
*/
@@ -1067,7 +1007,7 @@ QMediaMetaData QMediaPlayer::metaData() const
\header \li Property value
\li Description
\row \li PlayingState
- \li The media is currently playing.
+ \li The media is currently playing. This indicates the same as the \l playing property.
\row \li PausedState
\li Playback of the media has been suspended.
\row \li StoppedState
@@ -1082,6 +1022,12 @@ QMediaMetaData QMediaPlayer::metaData() const
*/
/*!
+ \qmlsignal QtMultimedia::MediaPlayer::playingChanged()
+
+ This signal is emitted when the \l playing property changes.
+*/
+
+/*!
\enum QMediaPlayer::MediaStatus
Defines the status of a media player's current media.
@@ -1288,7 +1234,7 @@ QMediaMetaData QMediaPlayer::metaData() const
Playback can start or resume only when the buffer is entirely filled.
When the buffer is filled, \c MediaPlayer.Buffered is true.
- When buffer progress is between \c 0.0 and \c 0.1, \c MediaPlayer.Buffering
+ When buffer progress is between \c 0.0 and \c 1.0, \c MediaPlayer.Buffering
is set to \c{true}.
A value lower than \c 1.0 implies that the property \c MediaPlayer.StalledMedia
@@ -1327,11 +1273,30 @@ QMediaMetaData QMediaPlayer::metaData() const
*/
/*!
+ \qmlproperty bool QtMultimedia::MediaPlayer::playing
+ \since 6.5
+
+ Indicates whether the media is currently playing.
+
+ \sa playbackState
+*/
+
+/*!
+ \property QMediaPlayer::playing
+ \brief Whether the media is playing.
+ \since 6.5
+
+ \sa playbackState, PlayingState
+*/
+
+/*!
\qmlproperty real QtMultimedia::MediaPlayer::playbackRate
- This property holds the rate at which audio is played at as a multiple of
+ This property holds the rate at which media is played at as a multiple of
the normal rate.
+ For more information, see \l{QMediaPlayer::playbackRate}.
+
Defaults to \c{1.0}.
*/
@@ -1339,11 +1304,11 @@ QMediaMetaData QMediaPlayer::metaData() const
\property QMediaPlayer::playbackRate
\brief the playback rate of the current media.
- This value is a multiplier applied to the media's standard play rate. By
- default this value is 1.0, indicating that the media is playing at the
- standard pace. Values higher than 1.0 will increase the rate of play.
- Values less than zero can be set and indicate the media should rewind at the
- multiplier of the standard pace.
+ This value is a multiplier applied to the media's standard playback
+ rate. By default this value is 1.0, indicating that the media is
+ playing at the standard speed. Values higher than 1.0 will increase
+ the playback speed, while values between 0.0 and 1.0 results in
+ slower playback. Negative playback rates are not supported.
Not all playback services support change of the playback rate. It is
framework defined as to the status and quality of audio and video
diff --git a/src/multimedia/playback/qmediaplayer.h b/src/multimedia/playback/qmediaplayer.h
index 0ccb0eaaf..015a30f05 100644
--- a/src/multimedia/playback/qmediaplayer.h
+++ b/src/multimedia/playback/qmediaplayer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIAPLAYER_H
#define QMEDIAPLAYER_H
@@ -44,7 +8,7 @@
#include <QtCore/qurl.h>
#include <QtMultimedia/qtmultimediaglobal.h>
#include <QtMultimedia/qmediaenumdebug.h>
-#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qtaudio.h>
QT_BEGIN_NAMESPACE
@@ -65,6 +29,7 @@ class Q_MULTIMEDIA_EXPORT QMediaPlayer : public QObject
Q_PROPERTY(bool hasAudio READ hasAudio NOTIFY hasAudioChanged)
Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged)
Q_PROPERTY(bool seekable READ isSeekable NOTIFY seekableChanged)
+ Q_PROPERTY(bool playing READ isPlaying NOTIFY playingChanged)
Q_PROPERTY(qreal playbackRate READ playbackRate WRITE setPlaybackRate NOTIFY playbackRateChanged)
Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged)
Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged)
@@ -146,9 +111,6 @@ public:
void setVideoOutput(QObject *);
QObject *videoOutput() const;
-#if 0
- void setVideoOutput(const QList<QVideoSink *> &sinks);
-#endif
void setVideoSink(QVideoSink *sink);
QVideoSink *videoSink() const;
@@ -171,6 +133,8 @@ public:
bool isSeekable() const;
qreal playbackRate() const;
+ bool isPlaying() const;
+
int loops() const;
void setLoops(int loops);
@@ -206,6 +170,7 @@ Q_SIGNALS:
void bufferProgressChanged(float progress);
void seekableChanged(bool seekable);
+ void playingChanged(bool playing);
void playbackRateChanged(qreal rate);
void loopsChanged();
diff --git a/src/multimedia/playback/qmediaplayer_p.h b/src/multimedia/playback/qmediaplayer_p.h
index 49ee3fd24..ece086d06 100644
--- a/src/multimedia/playback/qmediaplayer_p.h
+++ b/src/multimedia/playback/qmediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIAPLAYER_P_H
#define QMEDIAPLAYER_P_H
@@ -56,6 +20,7 @@
#include "qvideosink.h"
#include "qaudiooutput.h"
#include <private/qplatformmediaplayer_p.h>
+#include <private/qerrorinfo_p.h>
#include "private/qobject_p.h"
#include <QtCore/qobject.h>
@@ -76,11 +41,10 @@ class QMediaPlayerPrivate : public QObjectPrivate
public:
QMediaPlayerPrivate() = default;
- QPlatformMediaPlayer* control = nullptr;
- QString errorString;
+ QPlatformMediaPlayer *control = nullptr;
- QAudioOutput *audioOutput = nullptr;
- QVideoSink *videoSink = nullptr;
+ QPointer<QAudioOutput> audioOutput;
+ QPointer<QVideoSink> videoSink;
QPointer<QObject> videoOutput;
QUrl qrcMedia;
std::unique_ptr<QFile> qrcFile;
@@ -88,7 +52,7 @@ public:
QIODevice *stream = nullptr;
QMediaPlayer::PlaybackState state = QMediaPlayer::StoppedState;
- QMediaPlayer::Error error = QMediaPlayer::NoError;
+ QErrorInfo<QMediaPlayer::Error> error;
void setMedia(const QUrl &media, QIODevice *stream = nullptr);
@@ -96,7 +60,7 @@ public:
void setState(QMediaPlayer::PlaybackState state);
void setStatus(QMediaPlayer::MediaStatus status);
- void setError(int error, const QString &errorString);
+ void setError(QMediaPlayer::Error error, const QString &errorString);
void setVideoSink(QVideoSink *sink)
{
@@ -108,7 +72,8 @@ public:
videoSink = sink;
if (sink)
sink->setSource(q);
- control->setVideoSink(sink);
+ if (control)
+ control->setVideoSink(sink);
emit q->videoOutputChanged();
}
};
diff --git a/src/multimedia/pulseaudio/qaudioengine_pulse.cpp b/src/multimedia/pulseaudio/qaudioengine_pulse.cpp
index 7b3cf57b1..5fac7234a 100644
--- a/src/multimedia/pulseaudio/qaudioengine_pulse.cpp
+++ b/src/multimedia/pulseaudio/qaudioengine_pulse.cpp
@@ -1,101 +1,139 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qdebug.h>
#include <qaudiodevice.h>
+#include <QGuiApplication>
+#include <QIcon>
#include <QTimer>
#include "qaudioengine_pulse_p.h"
#include "qpulseaudiodevice_p.h"
#include "qpulsehelpers_p.h"
#include <sys/types.h>
#include <unistd.h>
+#include <mutex> // for lock_guard
QT_BEGIN_NAMESPACE
+template<typename Info>
+static bool updateDevicesMap(QReadWriteLock &lock, QByteArray defaultDeviceId,
+ QMap<int, QAudioDevice> &devices, QAudioDevice::Mode mode,
+ const Info &info)
+{
+ QWriteLocker locker(&lock);
+
+ bool isDefault = defaultDeviceId == info.name;
+ auto newDeviceInfo = std::make_unique<QPulseAudioDeviceInfo>(info.name, info.description, isDefault, mode);
+ newDeviceInfo->channelConfiguration = QPulseAudioInternal::channelConfigFromMap(info.channel_map);
+ newDeviceInfo->preferredFormat = QPulseAudioInternal::sampleSpecToAudioFormat(info.sample_spec);
+ newDeviceInfo->preferredFormat.setChannelConfig(newDeviceInfo->channelConfiguration);
+
+ auto &device = devices[info.index];
+ if (device.handle() && *newDeviceInfo == *device.handle())
+ return false;
+
+ device = newDeviceInfo.release()->create();
+ return true;
+}
+
+static bool updateDevicesMap(QReadWriteLock &lock, QByteArray defaultDeviceId,
+ QMap<int, QAudioDevice> &devices)
+{
+ QWriteLocker locker(&lock);
+
+ bool result = false;
+
+ for (QAudioDevice &device : devices) {
+ auto deviceInfo = device.handle();
+ const auto isDefault = deviceInfo->id == defaultDeviceId;
+ if (deviceInfo->isDefault != isDefault) {
+ Q_ASSERT(dynamic_cast<const QPulseAudioDeviceInfo *>(deviceInfo));
+ auto newDeviceInfo = std::make_unique<QPulseAudioDeviceInfo>(
+ *static_cast<const QPulseAudioDeviceInfo *>(deviceInfo));
+ newDeviceInfo->isDefault = isDefault;
+ device = newDeviceInfo.release()->create();
+ result = true;
+ }
+ }
+
+ return result;
+};
+
static void serverInfoCallback(pa_context *context, const pa_server_info *info, void *userdata)
{
+ using namespace Qt::Literals;
+ using namespace QPulseAudioInternal;
+
if (!info) {
- qWarning() << QString::fromLatin1("Failed to get server information: %s").arg(QString::fromUtf8(pa_strerror(pa_context_errno(context))));
+ qWarning() << "Failed to get server information:" << currentError(context);
return;
}
-#ifdef DEBUG_PULSE
- char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
-
- pa_sample_spec_snprint(ss, sizeof(ss), &info->sample_spec);
- pa_channel_map_snprint(cm, sizeof(cm), &info->channel_map);
-
- qDebug() << QString("User name: %1\n"
- "Host Name: %2\n"
- "Server Name: %3\n"
- "Server Version: %4\n"
- "Default Sample Specification: %5\n"
- "Default Channel Map: %6\n"
- "Default Sink: %7\n"
- "Default Source: %8\n").arg(
- info->user_name,
- info->host_name,
- info->server_name,
- info->server_version,
- ss,
- cm,
- info->default_sink_name,
- info->default_source_name);
-#endif
+ if (Q_UNLIKELY(qLcPulseAudioEngine().isEnabled(QtDebugMsg))) {
+ char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+ pa_sample_spec_snprint(ss, sizeof(ss), &info->sample_spec);
+ pa_channel_map_snprint(cm, sizeof(cm), &info->channel_map);
+
+ qCDebug(qLcPulseAudioEngine)
+ << QStringLiteral("User name: %1\n"
+ "Host Name: %2\n"
+ "Server Name: %3\n"
+ "Server Version: %4\n"
+ "Default Sample Specification: %5\n"
+ "Default Channel Map: %6\n"
+ "Default Sink: %7\n"
+ "Default Source: %8\n")
+ .arg(QString::fromUtf8(info->user_name),
+ QString::fromUtf8(info->host_name),
+ QString::fromUtf8(info->server_name),
+ QLatin1StringView(info->server_version), QLatin1StringView(ss),
+ QLatin1StringView(cm), QString::fromUtf8(info->default_sink_name),
+ QString::fromUtf8(info->default_source_name));
+ }
QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
- pulseEngine->m_serverLock.lockForWrite();
- pulseEngine->m_defaultSink = info->default_sink_name;
- pulseEngine->m_defaultSource = info->default_source_name;
- // ### ensure the QAudioDevices are updated if default changes and emit changed signal in the device manager
- pulseEngine->m_serverLock.unlock();
+
+ bool defaultSinkChanged = false;
+ bool defaultSourceChanged = false;
+
+ {
+ QWriteLocker locker(&pulseEngine->m_serverLock);
+
+ if (pulseEngine->m_defaultSink != info->default_sink_name) {
+ pulseEngine->m_defaultSink = info->default_sink_name;
+ defaultSinkChanged = true;
+ }
+
+ if (pulseEngine->m_defaultSource != info->default_source_name) {
+ pulseEngine->m_defaultSource = info->default_source_name;
+ defaultSourceChanged = true;
+ }
+ }
+
+ if (defaultSinkChanged
+ && updateDevicesMap(pulseEngine->m_sinkLock, pulseEngine->m_defaultSink,
+ pulseEngine->m_sinks))
+ emit pulseEngine->audioOutputsChanged();
+
+ if (defaultSourceChanged
+ && updateDevicesMap(pulseEngine->m_sourceLock, pulseEngine->m_defaultSource,
+ pulseEngine->m_sources))
+ emit pulseEngine->audioInputsChanged();
pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}
static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int isLast, void *userdata)
{
- QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
+ using namespace Qt::Literals;
+ using namespace QPulseAudioInternal;
+
+ QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine *>(userdata);
if (isLast < 0) {
- qWarning() << QString::fromLatin1("Failed to get sink information: %s").arg(QString::fromUtf8(pa_strerror(pa_context_errno(context))));
+ qWarning() << "Failed to get sink information:" << currentError(context);
return;
}
@@ -106,36 +144,32 @@ static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int
Q_ASSERT(info);
-#ifdef DEBUG_PULSE
- QMap<pa_sink_state, QString> stateMap;
- stateMap[PA_SINK_INVALID_STATE] = "n/a";
- stateMap[PA_SINK_RUNNING] = "RUNNING";
- stateMap[PA_SINK_IDLE] = "IDLE";
- stateMap[PA_SINK_SUSPENDED] = "SUSPENDED";
-
- qDebug() << QString("Sink #%1\n"
- "\tState: %2\n"
- "\tName: %3\n"
- "\tDescription: %4\n"
- ).arg(QString::number(info->index),
- stateMap.value(info->state),
- info->name,
- info->description);
-#endif
-
- QWriteLocker locker(&pulseEngine->m_sinkLock);
- bool isDefault = pulseEngine->m_defaultSink == info->name;
- auto *dinfo = new QPulseAudioDeviceInfo(info->name, info->description, isDefault, QAudioDevice::Output);
- dinfo->channelMap = info->channel_map;
- dinfo->channelConfiguration = QPulseAudioInternal::channelConfigFromMap(info->channel_map);
- dinfo->preferredFormat = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec);
- dinfo->preferredFormat.setChannelConfig(dinfo->channelConfiguration);
- pulseEngine->m_sinks.insert(info->index, dinfo->create());
- emit pulseEngine->audioOutputsChanged();
+ if (Q_UNLIKELY(qLcPulseAudioEngine().isEnabled(QtDebugMsg))) {
+ static const QMap<pa_sink_state, QString> stateMap{
+ { PA_SINK_INVALID_STATE, u"n/a"_s }, { PA_SINK_RUNNING, u"RUNNING"_s },
+ { PA_SINK_IDLE, u"IDLE"_s }, { PA_SINK_SUSPENDED, u"SUSPENDED"_s },
+ { PA_SINK_UNLINKED, u"UNLINKED"_s },
+ };
+
+ qCDebug(qLcPulseAudioEngine)
+ << QStringLiteral("Sink #%1\n"
+ "\tState: %2\n"
+ "\tName: %3\n"
+ "\tDescription: %4\n")
+ .arg(QString::number(info->index), stateMap.value(info->state),
+ QString::fromUtf8(info->name),
+ QString::fromUtf8(info->description));
+ }
+
+ if (updateDevicesMap(pulseEngine->m_sinkLock, pulseEngine->m_defaultSink, pulseEngine->m_sinks,
+ QAudioDevice::Output, *info))
+ emit pulseEngine->audioOutputsChanged();
}
static void sourceInfoCallback(pa_context *context, const pa_source_info *info, int isLast, void *userdata)
{
+ using namespace Qt::Literals;
+
Q_UNUSED(context);
QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
@@ -146,35 +180,30 @@ static void sourceInfoCallback(pa_context *context, const pa_source_info *info,
Q_ASSERT(info);
-#ifdef DEBUG_PULSE
- QMap<pa_source_state, QString> stateMap;
- stateMap[PA_SOURCE_INVALID_STATE] = "n/a";
- stateMap[PA_SOURCE_RUNNING] = "RUNNING";
- stateMap[PA_SOURCE_IDLE] = "IDLE";
- stateMap[PA_SOURCE_SUSPENDED] = "SUSPENDED";
-
- qDebug() << QString("Source #%1\n"
- "\tState: %2\n"
- "\tName: %3\n"
- "\tDescription: %4\n"
- ).arg(QString::number(info->index),
- stateMap.value(info->state),
- info->name,
- info->description);
-#endif
-
- QWriteLocker locker(&pulseEngine->m_sourceLock);
+ if (Q_UNLIKELY(qLcPulseAudioEngine().isEnabled(QtDebugMsg))) {
+ static const QMap<pa_source_state, QString> stateMap{
+ { PA_SOURCE_INVALID_STATE, u"n/a"_s }, { PA_SOURCE_RUNNING, u"RUNNING"_s },
+ { PA_SOURCE_IDLE, u"IDLE"_s }, { PA_SOURCE_SUSPENDED, u"SUSPENDED"_s },
+ { PA_SOURCE_UNLINKED, u"UNLINKED"_s },
+ };
+
+ qCDebug(qLcPulseAudioEngine)
+ << QStringLiteral("Source #%1\n"
+ "\tState: %2\n"
+ "\tName: %3\n"
+ "\tDescription: %4\n")
+ .arg(QString::number(info->index), stateMap.value(info->state),
+ QString::fromUtf8(info->name),
+ QString::fromUtf8(info->description));
+ }
+
// skip monitor channels
if (info->monitor_of_sink != PA_INVALID_INDEX)
return;
- bool isDefault = pulseEngine->m_defaultSink == info->name;
- auto *dinfo = new QPulseAudioDeviceInfo(info->name, info->description, isDefault, QAudioDevice::Input);
- dinfo->channelMap = info->channel_map;
- dinfo->channelConfiguration = QPulseAudioInternal::channelConfigFromMap(info->channel_map);
- dinfo->preferredFormat = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec);
- dinfo->preferredFormat.setChannelConfig(dinfo->channelConfiguration);
- pulseEngine->m_sources.insert(info->index, dinfo->create());
- emit pulseEngine->audioInputsChanged();
+
+ if (updateDevicesMap(pulseEngine->m_sourceLock, pulseEngine->m_defaultSource,
+ pulseEngine->m_sources, QAudioDevice::Input, *info))
+ emit pulseEngine->audioInputsChanged();
}
static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32_t index, void* userdata)
@@ -189,27 +218,23 @@ static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32
case PA_SUBSCRIPTION_EVENT_CHANGE:
switch (facility) {
case PA_SUBSCRIPTION_EVENT_SERVER: {
- pa_operation *op = pa_context_get_server_info(context, serverInfoCallback, userdata);
- if (op)
- pa_operation_unref(op);
- else
- qWarning("PulseAudioService: failed to get server info");
+ PAOperationUPtr op(pa_context_get_server_info(context, serverInfoCallback, userdata));
+ if (!op)
+ qWarning() << "PulseAudioService: failed to get server info";
break;
}
case PA_SUBSCRIPTION_EVENT_SINK: {
- pa_operation *op = pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata);
- if (op)
- pa_operation_unref(op);
- else
- qWarning("PulseAudioService: failed to get sink info");
+ PAOperationUPtr op(
+ pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata));
+ if (!op)
+ qWarning() << "PulseAudioService: failed to get sink info";
break;
}
case PA_SUBSCRIPTION_EVENT_SOURCE: {
- pa_operation *op = pa_context_get_source_info_by_index(context, index, sourceInfoCallback, userdata);
- if (op)
- pa_operation_unref(op);
- else
- qWarning("PulseAudioService: failed to get source info");
+ PAOperationUPtr op(pa_context_get_source_info_by_index(context, index,
+ sourceInfoCallback, userdata));
+ if (!op)
+ qWarning() << "PulseAudioService: failed to get source info";
break;
}
default:
@@ -218,16 +243,16 @@ static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32
break;
case PA_SUBSCRIPTION_EVENT_REMOVE:
switch (facility) {
- case PA_SUBSCRIPTION_EVENT_SINK:
- pulseEngine->m_sinkLock.lockForWrite();
+ case PA_SUBSCRIPTION_EVENT_SINK: {
+ QWriteLocker locker(&pulseEngine->m_sinkLock);
pulseEngine->m_sinks.remove(index);
- pulseEngine->m_sinkLock.unlock();
break;
- case PA_SUBSCRIPTION_EVENT_SOURCE:
- pulseEngine->m_sourceLock.lockForWrite();
+ }
+ case PA_SUBSCRIPTION_EVENT_SOURCE: {
+ QWriteLocker locker(&pulseEngine->m_sourceLock);
pulseEngine->m_sources.remove(index);
- pulseEngine->m_sourceLock.unlock();
break;
+ }
default:
break;
}
@@ -240,9 +265,10 @@ static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32
static void contextStateCallbackInit(pa_context *context, void *userdata)
{
Q_UNUSED(context);
-#ifdef DEBUG_PULSE
- qDebug() << QPulseAudioInternal::stateToQString(pa_context_get_state(context));
-#endif
+
+ if (Q_UNLIKELY(qLcPulseAudioEngine().isEnabled(QtDebugMsg)))
+ qCDebug(qLcPulseAudioEngine) << pa_context_get_state(context);
+
QPulseAudioEngine *pulseEngine = reinterpret_cast<QPulseAudioEngine*>(userdata);
pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}
@@ -252,9 +278,8 @@ static void contextStateCallback(pa_context *c, void *userdata)
QPulseAudioEngine *self = reinterpret_cast<QPulseAudioEngine*>(userdata);
pa_context_state_t state = pa_context_get_state(c);
-#ifdef DEBUG_PULSE
- qDebug() << QPulseAudioInternal::stateToQString(state);
-#endif
+ if (Q_UNLIKELY(qLcPulseAudioEngine().isEnabled(QtDebugMsg)))
+ qCDebug(qLcPulseAudioEngine) << state;
if (state == PA_CONTEXT_FAILED)
QMetaObject::invokeMethod(self, "onContextFailed", Qt::QueuedConnection);
@@ -279,17 +304,18 @@ QPulseAudioEngine::~QPulseAudioEngine()
void QPulseAudioEngine::prepare()
{
+ using namespace QPulseAudioInternal;
bool keepGoing = true;
bool ok = true;
m_mainLoop = pa_threaded_mainloop_new();
if (m_mainLoop == nullptr) {
- qWarning("PulseAudioService: unable to create pulseaudio mainloop");
+ qWarning() << "PulseAudioService: unable to create pulseaudio mainloop";
return;
}
if (pa_threaded_mainloop_start(m_mainLoop) != 0) {
- qWarning("PulseAudioService: unable to start pulseaudio mainloop");
+ qWarning() << "PulseAudioService: unable to start pulseaudio mainloop";
pa_threaded_mainloop_free(m_mainLoop);
m_mainLoop = nullptr;
return;
@@ -299,10 +325,19 @@ void QPulseAudioEngine::prepare()
lock();
- m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtPulseAudio:%1")).arg(::getpid()).toLatin1().constData());
+ pa_proplist *proplist = pa_proplist_new();
+ if (!QGuiApplication::applicationDisplayName().isEmpty())
+ pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, qUtf8Printable(QGuiApplication::applicationDisplayName()));
+ if (!QGuiApplication::desktopFileName().isEmpty())
+ pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, qUtf8Printable(QGuiApplication::desktopFileName()));
+ if (const QString windowIconName = QGuiApplication::windowIcon().name(); !windowIconName.isEmpty())
+ pa_proplist_sets(proplist, PA_PROP_WINDOW_ICON_NAME, qUtf8Printable(windowIconName));
+
+ m_context = pa_context_new_with_proplist(m_mainLoopApi, nullptr, proplist);
+ pa_proplist_free(proplist);
if (m_context == nullptr) {
- qWarning("PulseAudioService: Unable to create new pulseaudio context");
+ qWarning() << "PulseAudioService: Unable to create new pulseaudio context";
pa_threaded_mainloop_unlock(m_mainLoop);
pa_threaded_mainloop_free(m_mainLoop);
m_mainLoop = nullptr;
@@ -312,8 +347,8 @@ void QPulseAudioEngine::prepare()
pa_context_set_state_callback(m_context, contextStateCallbackInit, this);
- if (pa_context_connect(m_context, nullptr, (pa_context_flags_t)0, nullptr) < 0) {
- qWarning("PulseAudioService: pa_context_connect() failed");
+ if (pa_context_connect(m_context, nullptr, static_cast<pa_context_flags_t>(0), nullptr) < 0) {
+ qWarning() << "PulseAudioService: pa_context_connect() failed";
pa_context_unref(m_context);
pa_threaded_mainloop_unlock(m_mainLoop);
pa_threaded_mainloop_free(m_mainLoop);
@@ -332,9 +367,7 @@ void QPulseAudioEngine::prepare()
break;
case PA_CONTEXT_READY:
-#ifdef DEBUG_PULSE
- qDebug("Connection established.");
-#endif
+ qCDebug(qLcPulseAudioEngine) << "Connection established.";
keepGoing = false;
break;
@@ -346,8 +379,8 @@ void QPulseAudioEngine::prepare()
case PA_CONTEXT_FAILED:
default:
- qCritical() << QString::fromLatin1("PulseAudioService: Connection failure: %1")
- .arg(QString::fromUtf8(pa_strerror(pa_context_errno(m_context))));
+ qCritical() << "PulseAudioService: Connection failure:"
+ << currentError(m_context);
keepGoing = false;
ok = false;
}
@@ -360,15 +393,13 @@ void QPulseAudioEngine::prepare()
pa_context_set_state_callback(m_context, contextStateCallback, this);
pa_context_set_subscribe_callback(m_context, event_cb, this);
- pa_operation *op = pa_context_subscribe(m_context,
- pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK |
- PA_SUBSCRIPTION_MASK_SOURCE |
- PA_SUBSCRIPTION_MASK_SERVER),
- nullptr, nullptr);
- if (op)
- pa_operation_unref(op);
- else
- qWarning("PulseAudioService: failed to subscribe to context notifications");
+ PAOperationUPtr op(pa_context_subscribe(
+ m_context,
+ pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE
+ | PA_SUBSCRIPTION_MASK_SERVER),
+ nullptr, nullptr));
+ if (!op)
+ qWarning() << "PulseAudioService: failed to subscribe to context notifications";
} else {
pa_context_unref(m_context);
m_context = nullptr;
@@ -408,39 +439,34 @@ void QPulseAudioEngine::release()
void QPulseAudioEngine::updateDevices()
{
- lock();
+ std::lock_guard lock(*this);
// Get default input and output devices
- pa_operation *operation = pa_context_get_server_info(m_context, serverInfoCallback, this);
+ PAOperationUPtr operation(pa_context_get_server_info(m_context, serverInfoCallback, this));
if (operation) {
- while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
+ while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
- pa_operation_unref(operation);
} else {
- qWarning("PulseAudioService: failed to get server info");
+ qWarning() << "PulseAudioService: failed to get server info";
}
// Get output devices
- operation = pa_context_get_sink_info_list(m_context, sinkInfoCallback, this);
+ operation.reset(pa_context_get_sink_info_list(m_context, sinkInfoCallback, this));
if (operation) {
- while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
+ while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
- pa_operation_unref(operation);
} else {
- qWarning("PulseAudioService: failed to get sink info");
+ qWarning() << "PulseAudioService: failed to get sink info";
}
// Get input devices
- operation = pa_context_get_source_info_list(m_context, sourceInfoCallback, this);
+ operation.reset(pa_context_get_source_info_list(m_context, sourceInfoCallback, this));
if (operation) {
- while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
+ while (pa_operation_get_state(operation.get()) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
- pa_operation_unref(operation);
} else {
- qWarning("PulseAudioService: failed to get source info");
+ qWarning() << "PulseAudioService: failed to get source info";
}
-
- unlock();
}
void QPulseAudioEngine::onContextFailed()
@@ -451,7 +477,7 @@ void QPulseAudioEngine::onContextFailed()
release();
// Try to reconnect later
- QTimer::singleShot(3000, this, SLOT(prepare()));
+ QTimer::singleShot(3000, this, &QPulseAudioEngine::prepare);
}
QPulseAudioEngine *QPulseAudioEngine::instance()
@@ -461,24 +487,17 @@ QPulseAudioEngine *QPulseAudioEngine::instance()
QList<QAudioDevice> QPulseAudioEngine::availableDevices(QAudioDevice::Mode mode) const
{
- QList<QAudioDevice> devices;
- QByteArray defaultDevice;
-
- m_serverLock.lockForRead();
-
if (mode == QAudioDevice::Output) {
QReadLocker locker(&m_sinkLock);
- devices = m_sinks.values();
- defaultDevice = m_defaultSink;
- } else {
- QReadLocker locker(&m_sourceLock);
- devices = m_sources.values();
- defaultDevice = m_defaultSource;
+ return m_sinks.values();
}
- m_serverLock.unlock();
+ if (mode == QAudioDevice::Input) {
+ QReadLocker locker(&m_sourceLock);
+ return m_sources.values();
+ }
- return devices;
+ return {};
}
QByteArray QPulseAudioEngine::defaultDevice(QAudioDevice::Mode mode) const
diff --git a/src/multimedia/pulseaudio/qaudioengine_pulse_p.h b/src/multimedia/pulseaudio/qaudioengine_pulse_p.h
index 79a3d8d3d..2ed1fa0b1 100644
--- a/src/multimedia/pulseaudio/qaudioengine_pulse_p.h
+++ b/src/multimedia/pulseaudio/qaudioengine_pulse_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPULSEAUDIOENGINE_H
#define QPULSEAUDIOENGINE_H
diff --git a/src/multimedia/pulseaudio/qpulseaudiodevice.cpp b/src/multimedia/pulseaudio/qpulseaudiodevice.cpp
index c25467a8f..487b88f6d 100644
--- a/src/multimedia/pulseaudio/qpulseaudiodevice.cpp
+++ b/src/multimedia/pulseaudio/qpulseaudiodevice.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qpulseaudiodevice_p.h"
#include "qaudioengine_pulse_p.h"
diff --git a/src/multimedia/pulseaudio/qpulseaudiodevice_p.h b/src/multimedia/pulseaudio/qpulseaudiodevice_p.h
index 0175efca1..b44c71e0d 100644
--- a/src/multimedia/pulseaudio/qpulseaudiodevice_p.h
+++ b/src/multimedia/pulseaudio/qpulseaudiodevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIODEVICEINFOPULSE_H
#define QAUDIODEVICEINFOPULSE_H
@@ -69,8 +33,6 @@ class QPulseAudioDeviceInfo : public QAudioDevicePrivate
public:
QPulseAudioDeviceInfo(const char *device, const char *description, bool isDefault, QAudioDevice::Mode mode);
~QPulseAudioDeviceInfo() {}
-
- pa_channel_map channelMap;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/pulseaudio/qpulseaudiomediadevices.cpp b/src/multimedia/pulseaudio/qpulseaudiomediadevices.cpp
index 75058a887..4deee6033 100644
--- a/src/multimedia/pulseaudio/qpulseaudiomediadevices.cpp
+++ b/src/multimedia/pulseaudio/qpulseaudiomediadevices.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qpulseaudiomediadevices_p.h"
#include "qmediadevices.h"
@@ -52,6 +16,13 @@ QPulseAudioMediaDevices::QPulseAudioMediaDevices()
: QPlatformMediaDevices()
{
pulseEngine = new QPulseAudioEngine();
+
+ // TODO: it might make sense to connect device changing signals
+ // to each added QMediaDevices
+ QObject::connect(pulseEngine, &QPulseAudioEngine::audioInputsChanged,
+ this, &QPulseAudioMediaDevices::audioInputsChanged, Qt::DirectConnection);
+ QObject::connect(pulseEngine, &QPulseAudioEngine::audioOutputsChanged,
+ this, &QPulseAudioMediaDevices::audioOutputsChanged, Qt::DirectConnection);
}
QPulseAudioMediaDevices::~QPulseAudioMediaDevices()
@@ -69,19 +40,16 @@ QList<QAudioDevice> QPulseAudioMediaDevices::audioOutputs() const
return pulseEngine->availableDevices(QAudioDevice::Output);
}
-QList<QCameraDevice> QPulseAudioMediaDevices::videoInputs() const
-{
- return {};
-}
-
-QPlatformAudioSource *QPulseAudioMediaDevices::createAudioSource(const QAudioDevice &deviceInfo)
+QPlatformAudioSource *QPulseAudioMediaDevices::createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QPulseAudioSource(deviceInfo.id());
+ return new QPulseAudioSource(deviceInfo.id(), parent);
}
-QPlatformAudioSink *QPulseAudioMediaDevices::createAudioSink(const QAudioDevice &deviceInfo)
+QPlatformAudioSink *QPulseAudioMediaDevices::createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QPulseAudioSink(deviceInfo.id());
+ return new QPulseAudioSink(deviceInfo.id(), parent);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/pulseaudio/qpulseaudiomediadevices_p.h b/src/multimedia/pulseaudio/qpulseaudiomediadevices_p.h
index dd811f550..094dc3907 100644
--- a/src/multimedia/pulseaudio/qpulseaudiomediadevices_p.h
+++ b/src/multimedia/pulseaudio/qpulseaudiomediadevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPULSEAUDIOMEDIADEVICES_H
#define QPULSEAUDIOMEDIADEVICES_H
@@ -67,9 +31,10 @@ public:
QList<QAudioDevice> audioInputs() const override;
QList<QAudioDevice> audioOutputs() const override;
- QList<QCameraDevice> videoInputs() const override;
- QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override;
- QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override;
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
private:
QPulseAudioEngine *pulseEngine;
diff --git a/src/multimedia/pulseaudio/qpulseaudiosink.cpp b/src/multimedia/pulseaudio/qpulseaudiosink.cpp
index b3e39d746..610677eeb 100644
--- a/src/multimedia/pulseaudio/qpulseaudiosink.cpp
+++ b/src/multimedia/pulseaudio/qpulseaudiosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
@@ -43,21 +7,20 @@
#include <private/qaudiohelpers_p.h>
#include "qpulseaudiosink_p.h"
-#include "qpulseaudiodevice_p.h"
#include "qaudioengine_pulse_p.h"
#include "qpulsehelpers_p.h"
#include <sys/types.h>
#include <unistd.h>
-
-Q_DECLARE_LOGGING_CATEGORY(qLcPulseAudio)
+#include <mutex> // for std::lock_guard
QT_BEGIN_NAMESPACE
-const int PeriodTimeMs = 20;
+static constexpr uint SinkPeriodTimeMs = 20;
+static constexpr uint DefaultBufferLengthMs = 100;
#define LOW_LATENCY_CATEGORY_NAME "game"
-static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata)
+static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata)
{
Q_UNUSED(stream);
Q_UNUSED(userdata);
@@ -72,17 +35,19 @@ static void outputStreamStateCallback(pa_stream *stream, void *userdata)
pa_stream_state_t state = pa_stream_get_state(stream);
qCDebug(qLcPulseAudioOut) << "Stream state callback:" << state;
switch (state) {
- case PA_STREAM_CREATING:
- case PA_STREAM_READY:
- case PA_STREAM_TERMINATED:
- break;
-
- case PA_STREAM_FAILED:
- default:
- qWarning() << QString::fromLatin1("Stream error: %1").arg(QString::fromUtf8(pa_strerror(pa_context_errno(pa_stream_get_context(stream)))));
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
- break;
+ case PA_STREAM_CREATING:
+ case PA_STREAM_READY:
+ case PA_STREAM_TERMINATED:
+ break;
+
+ case PA_STREAM_FAILED:
+ default:
+ qWarning() << QStringLiteral("Stream error: %1")
+ .arg(QString::fromUtf8(pa_strerror(
+ pa_context_errno(pa_stream_get_context(stream)))));
+ QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
+ pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
+ break;
}
}
@@ -133,90 +98,73 @@ static void outputStreamDrainComplete(pa_stream *stream, int success, void *user
{
Q_UNUSED(stream);
- qCDebug(qLcPulseAudioOut) << "Stream drained:" << bool(success) << userdata;
- if (success && userdata)
+ qCDebug(qLcPulseAudioOut) << "Stream drained:" << static_cast<bool>(success) << userdata;
+
+ QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
+ pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
+
+ if (userdata && success)
static_cast<QPulseAudioSink *>(userdata)->streamDrainedCallback();
}
+static void outputStreamFlushComplete(pa_stream *stream, int success, void *userdata)
+{
+ Q_UNUSED(stream);
+
+ qCDebug(qLcPulseAudioOut) << "Stream flushed:" << static_cast<bool>(success) << userdata;
+}
+
static void streamAdjustPrebufferCallback(pa_stream *stream, int success, void *userdata)
{
Q_UNUSED(stream);
Q_UNUSED(success);
Q_UNUSED(userdata);
- qCDebug(qLcPulseAudioOut) << "Prebuffer adjusted:" << bool(success);
+ qCDebug(qLcPulseAudioOut) << "Prebuffer adjusted:" << static_cast<bool>(success);
}
-
-QPulseAudioSink::QPulseAudioSink(const QByteArray &device)
- : m_device(device)
+QPulseAudioSink::QPulseAudioSink(const QByteArray &device, QObject *parent)
+ : QPlatformAudioSink(parent), m_device(device), m_stateMachine(*this)
{
}
QPulseAudioSink::~QPulseAudioSink()
{
- close();
- QCoreApplication::processEvents();
-}
-
-void QPulseAudioSink::setError(QAudio::Error error)
-{
- if (m_errorState == error)
- return;
-
- m_errorState = error;
- emit errorChanged(error);
+ if (auto notifier = m_stateMachine.stop())
+ close();
}
QAudio::Error QPulseAudioSink::error() const
{
- return m_errorState;
-}
-
-void QPulseAudioSink::setState(QAudio::State state)
-{
- if (m_deviceState == state)
- return;
-
- m_deviceState = state;
- emit stateChanged(state);
+ return m_stateMachine.error();
}
QAudio::State QPulseAudioSink::state() const
{
- return m_deviceState;
+ return m_stateMachine.state();
}
void QPulseAudioSink::streamUnderflowCallback()
{
- if (m_audioSource && m_audioSource->atEnd()) {
+ bool atEnd = m_audioSource && m_audioSource->atEnd();
+ if (atEnd && m_stateMachine.state() != QAudio::StoppedState) {
qCDebug(qLcPulseAudioOut) << "Draining stream at end of buffer";
- pa_operation *o = pa_stream_drain(m_stream, outputStreamDrainComplete, this);
- if (o)
- pa_operation_unref(o);
- } else if (m_deviceState != QAudio::IdleState && !m_resuming) {
- setError(QAudio::UnderrunError);
- setState(QAudio::IdleState);
+ exchangeDrainOperation(pa_stream_drain(m_stream, outputStreamDrainComplete, this));
}
+
+ m_stateMachine.updateActiveOrIdle(
+ false, (m_pullMode && atEnd) ? QAudio::NoError : QAudio::UnderrunError);
}
void QPulseAudioSink::streamDrainedCallback()
{
- setError(QAudio::NoError);
- setState(QAudio::IdleState);
+ if (!exchangeDrainOperation(nullptr))
+ return;
}
void QPulseAudioSink::start(QIODevice *device)
{
- setState(QAudio::StoppedState);
- setError(QAudio::NoError);
-
- // Handle change of mode
- if (m_audioSource && !m_pullMode)
- delete m_audioSource;
- m_audioSource = nullptr;
-
- close();
+ reset();
m_pullMode = true;
m_audioSource = device;
@@ -230,27 +178,29 @@ void QPulseAudioSink::start(QIODevice *device)
gettimeofday(&lastTimingInfo, nullptr);
lastProcessedUSecs = 0;
- connect(m_audioSource, &QIODevice::readyRead, this, &QPulseAudioSink::startReading);
- setState(QAudio::ActiveState);
+ connect(m_audioSource, &QIODevice::readyRead, this, &QPulseAudioSink::startPulling);
+
+ m_stateMachine.start();
}
-void QPulseAudioSink::startReading()
+void QPulseAudioSink::startPulling()
{
- if (!m_tickTimer.isActive())
- m_tickTimer.start(m_periodTime, this);
+ Q_ASSERT(m_pullMode);
+ if (m_tickTimer.isActive())
+ return;
+
+ m_tickTimer.start(m_pullingPeriodTime, this);
}
-QIODevice *QPulseAudioSink::start()
+void QPulseAudioSink::stopTimer()
{
- setState(QAudio::StoppedState);
- setError(QAudio::NoError);
-
- // Handle change of mode
- if (m_audioSource && !m_pullMode)
- delete m_audioSource;
- m_audioSource = nullptr;
+ if (m_tickTimer.isActive())
+ m_tickTimer.stop();
+}
- close();
+QIODevice *QPulseAudioSink::start()
+{
+ reset();
m_pullMode = false;
@@ -258,13 +208,13 @@ QIODevice *QPulseAudioSink::start()
return nullptr;
m_audioSource = new PulseOutputPrivate(this);
- m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
+ m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
// ensure we only process timing infos that are up to date
gettimeofday(&lastTimingInfo, nullptr);
lastProcessedUSecs = 0;
- setState(QAudio::IdleState);
+ m_stateMachine.start(false);
return m_audioSource;
}
@@ -276,10 +226,9 @@ bool QPulseAudioSink::open()
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
- emit stateChanged(m_deviceState);
+ if (!pulseEngine->context()
+ || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
+ m_stateMachine.stopOrUpdateError(QAudio::FatalError);
return false;
}
@@ -288,9 +237,7 @@ bool QPulseAudioSink::open()
Q_ASSERT(spec.channels == channel_map.channels);
if (!pa_sample_spec_valid(&spec)) {
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- emit stateChanged(m_deviceState);
+ m_stateMachine.stopOrUpdateError(QAudio::OpenError);
return false;
}
@@ -298,11 +245,12 @@ bool QPulseAudioSink::open()
m_totalTimeValue = 0;
if (m_streamName.isNull())
- m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8();
+ m_streamName =
+ QStringLiteral("QtmPulseStream-%1-%2").arg(::getpid()).arg(quintptr(this)).toUtf8();
if (Q_UNLIKELY(qLcPulseAudioOut().isEnabled(QtDebugMsg))) {
qCDebug(qLcPulseAudioOut) << "Opening stream with.";
- qCDebug(qLcPulseAudioOut) << "\tFormat: " << QPulseAudioInternal::sampleFormatToQString(spec.format);
+ qCDebug(qLcPulseAudioOut) << "\tFormat: " << spec.format;
qCDebug(qLcPulseAudioOut) << "\tRate: " << spec.rate;
qCDebug(qLcPulseAudioOut) << "\tChannels: " << spec.channels;
qCDebug(qLcPulseAudioOut) << "\tFrame size: " << pa_frame_size(&spec);
@@ -310,7 +258,6 @@ bool QPulseAudioSink::open()
pulseEngine->lock();
-
pa_proplist *propList = pa_proplist_new();
#if 0
qint64 bytesPerSecond = m_format.sampleRate() * m_format.bytesPerFrame();
@@ -332,18 +279,18 @@ bool QPulseAudioSink::open()
pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, r);
#endif
- m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(), &m_spec, &channel_map, propList);
+ m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(),
+ &m_spec, &channel_map, propList);
+ pa_proplist_free(propList);
+
if (!m_stream) {
qCWarning(qLcPulseAudioOut) << "QAudioSink: pa_stream_new_with_proplist() failed!";
pulseEngine->unlock();
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- emit stateChanged(m_deviceState);
+
+ m_stateMachine.stopOrUpdateError(QAudio::OpenError);
return false;
}
- pa_proplist_free(propList);
-
pa_stream_set_state_callback(m_stream, outputStreamStateCallback, this);
pa_stream_set_write_callback(m_stream, outputStreamWriteCallback, this);
@@ -352,21 +299,26 @@ bool QPulseAudioSink::open()
pa_stream_set_latency_update_callback(m_stream, outputStreamLatencyCallback, this);
pa_buffer_attr requestedBuffer;
- requestedBuffer.fragsize = (uint32_t)-1;
- requestedBuffer.maxlength = (uint32_t)-1;
- requestedBuffer.minreq = (uint32_t)-1;
- requestedBuffer.prebuf = (uint32_t)-1;
- requestedBuffer.tlength = m_bufferSize;
-
- pa_stream_flags flags = pa_stream_flags(PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_ADJUST_LATENCY);
- if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : nullptr, flags, nullptr, nullptr) < 0) {
+ // Request a target buffer size
+ auto targetBufferSize = m_userBufferSize ? *m_userBufferSize : defaultBufferSize();
+ requestedBuffer.tlength =
+ targetBufferSize ? static_cast<uint32_t>(targetBufferSize) : static_cast<uint32_t>(-1);
+ // Rest should be determined by PulseAudio
+ requestedBuffer.fragsize = static_cast<uint32_t>(-1);
+ requestedBuffer.maxlength = static_cast<uint32_t>(-1);
+ requestedBuffer.minreq = static_cast<uint32_t>(-1);
+ requestedBuffer.prebuf = static_cast<uint32_t>(-1);
+
+ pa_stream_flags flags =
+ pa_stream_flags(PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY);
+ if (pa_stream_connect_playback(m_stream, m_device.data(), &requestedBuffer, flags, nullptr,
+ nullptr)
+ < 0) {
qCWarning(qLcPulseAudioOut) << "pa_stream_connect_playback() failed!";
pa_stream_unref(m_stream);
m_stream = nullptr;
pulseEngine->unlock();
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
- emit stateChanged(m_deviceState);
+ m_stateMachine.stopOrUpdateError(QAudio::OpenError);
return false;
}
@@ -374,20 +326,25 @@ bool QPulseAudioSink::open()
pa_threaded_mainloop_wait(pulseEngine->mainloop());
const pa_buffer_attr *buffer = pa_stream_get_buffer_attr(m_stream);
- m_periodTime = PeriodTimeMs;
- m_periodSize = pa_usec_to_bytes(m_periodTime * 1000, &m_spec);
m_bufferSize = buffer->tlength;
- m_maxBufferSize = buffer->maxlength;
- m_audioBuffer = new char[m_maxBufferSize];
+
+ if (m_pullMode) {
+ // Adjust period time to reduce chance of it being higher than amount of bytes requested by
+ // PulseAudio server
+ m_pullingPeriodTime =
+ qMin(SinkPeriodTimeMs, pa_bytes_to_usec(m_bufferSize, &m_spec) / 1000 / 2);
+ m_pullingPeriodSize = pa_usec_to_bytes(m_pullingPeriodTime * 1000, &m_spec);
+ }
+
+ m_audioBuffer.resize(buffer->maxlength);
const qint64 streamSize = m_audioSource ? m_audioSource->size() : 0;
if (m_pullMode && streamSize > 0 && static_cast<qint64>(buffer->prebuf) > streamSize) {
pa_buffer_attr newBufferAttr;
newBufferAttr = *buffer;
newBufferAttr.prebuf = streamSize;
- pa_operation *o = pa_stream_set_buffer_attr(m_stream, &newBufferAttr, streamAdjustPrebufferCallback, nullptr);
- if (o)
- pa_operation_unref(o);
+ PAOperationUPtr(pa_stream_set_buffer_attr(m_stream, &newBufferAttr,
+ streamAdjustPrebufferCallback, nullptr));
}
if (Q_UNLIKELY(qLcPulseAudioOut().isEnabled(QtDebugMsg))) {
@@ -401,11 +358,13 @@ bool QPulseAudioSink::open()
pulseEngine->unlock();
- connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSink::onPulseContextFailed);
+ connect(pulseEngine, &QPulseAudioEngine::contextFailed, this,
+ &QPulseAudioSink::onPulseContextFailed);
m_opened = true;
- startReading();
+ if (m_pullMode)
+ startPulling();
m_elapsedTimeOffset = 0;
@@ -417,12 +376,12 @@ void QPulseAudioSink::close()
if (!m_opened)
return;
- m_tickTimer.stop();
+ stopTimer();
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
if (m_stream) {
- pulseEngine->lock();
+ std::lock_guard lock(*pulseEngine);
pa_stream_set_state_callback(m_stream, nullptr, nullptr);
pa_stream_set_write_callback(m_stream, nullptr, nullptr);
@@ -430,37 +389,37 @@ void QPulseAudioSink::close()
pa_stream_set_overflow_callback(m_stream, nullptr, nullptr);
pa_stream_set_latency_update_callback(m_stream, nullptr, nullptr);
- pa_operation *o = pa_stream_drain(m_stream, outputStreamDrainComplete, nullptr);
- if (o)
- pa_operation_unref(o);
+ if (auto prevOp = exchangeDrainOperation(nullptr))
+ // cancel draining operation to prevent calling draining callback after closing.
+ pa_operation_cancel(prevOp.get());
+
+ PAOperationUPtr operation(pa_stream_flush(m_stream, outputStreamFlushComplete, nullptr));
pa_stream_disconnect(m_stream);
pa_stream_unref(m_stream);
m_stream = nullptr;
-
- pulseEngine->unlock();
}
- disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSink::onPulseContextFailed);
+ disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this,
+ &QPulseAudioSink::onPulseContextFailed);
if (m_audioSource) {
if (m_pullMode) {
disconnect(m_audioSource, &QIODevice::readyRead, this, nullptr);
+ m_audioSource->reset();
} else {
delete m_audioSource;
m_audioSource = nullptr;
}
}
+
m_opened = false;
- if (m_audioBuffer) {
- delete[] m_audioBuffer;
- m_audioBuffer = nullptr;
- }
+ m_audioBuffer.clear();
}
void QPulseAudioSink::timerEvent(QTimerEvent *event)
{
- if (event->timerId() == m_tickTimer.timerId())
+ if (event->timerId() == m_tickTimer.timerId() && m_pullMode)
userFeed();
QPlatformAudioSink::timerEvent(event);
@@ -468,54 +427,48 @@ void QPulseAudioSink::timerEvent(QTimerEvent *event)
void QPulseAudioSink::userFeed()
{
- if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState)
+ int writableSize = bytesFree();
+
+ if (writableSize == 0) {
+ // PulseAudio server doesn't want any more data
+ m_stateMachine.activateFromIdle();
return;
+ }
- m_resuming = false;
+ // Write up to writableSize
+ const int inputSize =
+ std::min({ m_pullingPeriodSize, static_cast<int>(m_audioBuffer.size()), writableSize });
- if (m_pullMode) {
- setError(QAudio::NoError);
- setState(QAudio::ActiveState);
- int writableSize = bytesFree();
- int chunks = writableSize / m_periodSize;
- if (chunks == 0)
- return;
-
- int input = m_periodSize; // always request 1 chunk of data from user
- if (input > m_maxBufferSize)
- input = m_maxBufferSize;
-
- Q_ASSERT(m_audioBuffer);
- int audioBytesPulled = m_audioSource->read(m_audioBuffer, input);
- Q_ASSERT(audioBytesPulled <= input);
- if (audioBytesPulled > 0) {
- if (audioBytesPulled > input) {
- qCWarning(qLcPulseAudioOut) << "Invalid audio data size provided by pull source:"
- << audioBytesPulled << "should be less than" << input;
- audioBytesPulled = input;
- }
- qint64 bytesWritten = write(m_audioBuffer, audioBytesPulled);
- Q_ASSERT(bytesWritten == audioBytesPulled); //unfinished write should not happen since the data provided is less than writableSize
- Q_UNUSED(bytesWritten);
-
- if (chunks > 1) {
- // PulseAudio needs more data. Ask for it immediately.
- QMetaObject::invokeMethod(this, "userFeed", Qt::QueuedConnection);
- }
- } else if (audioBytesPulled == 0) {
- m_tickTimer.stop();
- qCDebug(qLcPulseAudioOut) << "No more data available, source is done:" << m_audioSource->atEnd();
- setError(m_audioSource->atEnd() ? QAudio::NoError : QAudio::UnderrunError);
- setState(QAudio::IdleState);
+ Q_ASSERT(!m_audioBuffer.empty());
+ int audioBytesPulled = m_audioSource->read(m_audioBuffer.data(), inputSize);
+ Q_ASSERT(audioBytesPulled <= inputSize);
+
+ if (audioBytesPulled > 0) {
+ if (audioBytesPulled > inputSize) {
+ qCWarning(qLcPulseAudioOut)
+ << "Invalid audio data size provided by pull source:" << audioBytesPulled
+ << "should be less than" << inputSize;
+ audioBytesPulled = inputSize;
}
+ auto bytesWritten = write(m_audioBuffer.data(), audioBytesPulled);
+ if (bytesWritten != audioBytesPulled)
+ qWarning() << "Unfinished write:" << bytesWritten << "vs" << audioBytesPulled;
+
+ m_stateMachine.activateFromIdle();
+
+ if (inputSize < writableSize) // PulseAudio needs more data.
+ QMetaObject::invokeMethod(this, &QPulseAudioSink::userFeed, Qt::QueuedConnection);
+ } else if (audioBytesPulled == 0) {
+ stopTimer();
+ const auto atEnd = m_audioSource->atEnd();
+ qCDebug(qLcPulseAudioOut) << "No more data available, source is done:" << atEnd;
}
-
- if (m_deviceState != QAudio::ActiveState)
- return;
}
qint64 QPulseAudioSink::write(const char *data, qint64 len)
{
+ using namespace QPulseAudioInternal;
+
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
pulseEngine->lock();
@@ -524,9 +477,10 @@ qint64 QPulseAudioSink::write(const char *data, qint64 len)
void *dest = nullptr;
if (pa_stream_begin_write(m_stream, &dest, &nbytes) < 0) {
- qCWarning(qLcPulseAudioOut) << "pa_stream_begin_write error:"
- << pa_strerror(pa_context_errno(pulseEngine->context()));
- setError(QAudio::IOError);
+ pulseEngine->unlock();
+ qCWarning(qLcPulseAudioOut)
+ << "pa_stream_begin_write error:" << currentError(pulseEngine->context());
+ m_stateMachine.updateActiveOrIdle(false, QAudio::IOError);
return 0;
}
@@ -543,65 +497,76 @@ qint64 QPulseAudioSink::write(const char *data, qint64 len)
data = reinterpret_cast<char *>(dest);
if ((pa_stream_write(m_stream, data, len, nullptr, 0, PA_SEEK_RELATIVE)) < 0) {
- qCWarning(qLcPulseAudioOut) << "pa_stream_write error:"
- << pa_strerror(pa_context_errno(pulseEngine->context()));
- setError(QAudio::IOError);
+ pulseEngine->unlock();
+ qCWarning(qLcPulseAudioOut)
+ << "pa_stream_write error:" << currentError(pulseEngine->context());
+ m_stateMachine.updateActiveOrIdle(false, QAudio::IOError);
return 0;
}
pulseEngine->unlock();
m_totalTimeValue += len;
- setError(QAudio::NoError);
- setState(QAudio::ActiveState);
-
+ m_stateMachine.updateActiveOrIdle(true);
return len;
}
void QPulseAudioSink::stop()
{
- if (m_deviceState == QAudio::StoppedState)
- return;
+ if (auto notifier = m_stateMachine.stop()) {
+ {
+ QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
+ std::lock_guard lock(*pulseEngine);
- close();
+ if (auto prevOp = exchangeDrainOperation(nullptr))
+ // cancel the draining callback that is not relevant already
+ pa_operation_cancel(prevOp.get());
- setError(QAudio::NoError);
- setState(QAudio::StoppedState);
+ PAOperationUPtr drainOp(pa_stream_drain(m_stream, outputStreamDrainComplete, nullptr));
+ pulseEngine->wait(drainOp.get());
+ }
+
+ close();
+ }
}
qsizetype QPulseAudioSink::bytesFree() const
{
- if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState)
+ if (!m_stateMachine.isActiveOrIdle())
return 0;
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pulseEngine->lock();
- int writableSize = pa_stream_writable_size(m_stream);
- pulseEngine->unlock();
- return writableSize;
+ std::lock_guard lock(*QPulseAudioEngine::instance());
+ return pa_stream_writable_size(m_stream);
}
void QPulseAudioSink::setBufferSize(qsizetype value)
{
- m_bufferSize = value;
+ m_userBufferSize = value;
}
qsizetype QPulseAudioSink::bufferSize() const
{
- return m_bufferSize;
+ if (m_bufferSize)
+ return m_bufferSize;
+
+ if (m_userBufferSize)
+ return *m_userBufferSize;
+
+ return defaultBufferSize();
}
static qint64 operator-(timeval t1, timeval t2)
{
constexpr qint64 secsToUSecs = 1000000;
- return (t1.tv_sec - t2.tv_sec)*secsToUSecs + (t1.tv_usec - t2.tv_usec);
+ return (t1.tv_sec - t2.tv_sec) * secsToUSecs + (t1.tv_usec - t2.tv_usec);
}
qint64 QPulseAudioSink::processedUSecs() const
{
- if (!m_stream || m_deviceState == QAudio::StoppedState)
+ const auto state = this->state();
+ if (!m_stream || state == QAudio::StoppedState)
return 0;
- if (m_deviceState == QAudio::SuspendedState)
+ if (state == QAudio::SuspendedState)
return lastProcessedUSecs;
auto info = pa_stream_get_timing_info(m_stream);
@@ -612,13 +577,16 @@ qint64 QPulseAudioSink::processedUSecs() const
if (info->timestamp - lastTimingInfo > 0) {
lastTimingInfo.tv_sec = info->timestamp.tv_sec;
lastTimingInfo.tv_usec = info->timestamp.tv_usec;
- averageLatency = 0; // also use that as long as we don't have valid data from the timing info
+ averageLatency =
+ 0; // also use that as long as we don't have valid data from the timing info
// Only use timing values when playing, otherwise the latency numbers can be way off
- if (info->since_underrun >= 0 && pa_bytes_to_usec(info->since_underrun, &m_spec) > info->sink_usec) {
+ if (info->since_underrun >= 0
+ && pa_bytes_to_usec(info->since_underrun, &m_spec) > info->sink_usec) {
latencyList.append(info->sink_usec);
// Average over the last X timing infos to keep numbers more stable.
- // 10 seems to be a decent number that keeps values relatively stable but doesn't make the list too big
+ // 10 seems to be a decent number that keeps values relatively stable but doesn't make
+ // the list too big
const int latencyListMaxSize = 10;
if (latencyList.size() > latencyListMaxSize)
latencyList.pop_front();
@@ -631,7 +599,8 @@ qint64 QPulseAudioSink::processedUSecs() const
}
const qint64 usecsRead = info->read_index < 0 ? 0 : pa_bytes_to_usec(info->read_index, &m_spec);
- const qint64 usecsWritten = info->write_index < 0 ? 0 : pa_bytes_to_usec(info->write_index, &m_spec);
+ const qint64 usecsWritten =
+ info->write_index < 0 ? 0 : pa_bytes_to_usec(info->write_index, &m_spec);
// processed data is the amount read by the server minus its latency
qint64 usecs = usecsRead - averageLatency;
@@ -659,27 +628,22 @@ qint64 QPulseAudioSink::processedUSecs() const
void QPulseAudioSink::resume()
{
- if (m_deviceState == QAudio::SuspendedState) {
- m_resuming = true;
-
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
-
- pulseEngine->lock();
-
- pa_operation *operation = pa_stream_cork(m_stream, 0, outputStreamSuccessCallback, nullptr);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
+ if (auto notifier = m_stateMachine.resume()) {
+ {
+ QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- operation = pa_stream_trigger(m_stream, outputStreamSuccessCallback, nullptr);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
+ std::lock_guard lock(*pulseEngine);
- pulseEngine->unlock();
+ PAOperationUPtr operation(
+ pa_stream_cork(m_stream, 0, outputStreamSuccessCallback, nullptr));
+ pulseEngine->wait(operation.get());
- m_tickTimer.start(m_periodTime, this);
+ operation.reset(pa_stream_trigger(m_stream, outputStreamSuccessCallback, nullptr));
+ pulseEngine->wait(operation.get());
+ }
- setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState);
- setError(QAudio::NoError);
+ if (m_pullMode)
+ startPulling();
}
}
@@ -695,33 +659,28 @@ QAudioFormat QPulseAudioSink::format() const
void QPulseAudioSink::suspend()
{
- if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) {
- setError(QAudio::NoError);
- setState(QAudio::SuspendedState);
-
- m_tickTimer.stop();
+ if (auto notifier = m_stateMachine.suspend()) {
+ stopTimer();
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_operation *operation;
-
- pulseEngine->lock();
- operation = pa_stream_cork(m_stream, 1, outputStreamSuccessCallback, nullptr);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
+ std::lock_guard lock(*pulseEngine);
- pulseEngine->unlock();
+ PAOperationUPtr operation(
+ pa_stream_cork(m_stream, 1, outputStreamSuccessCallback, nullptr));
+ pulseEngine->wait(operation.get());
}
}
void QPulseAudioSink::reset()
{
- stop();
+ if (auto notifier = m_stateMachine.stopOrUpdateError())
+ close();
}
PulseOutputPrivate::PulseOutputPrivate(QPulseAudioSink *audio)
{
- m_audioDevice = qobject_cast<QPulseAudioSink*>(audio);
+ m_audioDevice = qobject_cast<QPulseAudioSink *>(audio);
}
qint64 PulseOutputPrivate::readData(char *data, qint64 len)
@@ -736,13 +695,13 @@ qint64 PulseOutputPrivate::writeData(const char *data, qint64 len)
{
qint64 written = 0;
- if ((m_audioDevice->m_deviceState == QAudio::ActiveState
- || m_audioDevice->m_deviceState == QAudio::IdleState)) {
- while(written < len) {
- int chunk = m_audioDevice->write(data+written, (len-written));
+ const auto state = m_audioDevice->state();
+ if (state == QAudio::ActiveState || state == QAudio::IdleState) {
+ while (written < len) {
+ int chunk = m_audioDevice->write(data + written, (len - written));
if (chunk <= 0)
return written;
- written+=chunk;
+ written += chunk;
}
}
@@ -764,10 +723,25 @@ qreal QPulseAudioSink::volume() const
void QPulseAudioSink::onPulseContextFailed()
{
- close();
+ if (auto notifier = m_stateMachine.stop(QAudio::FatalError))
+ close();
+}
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
+PAOperationUPtr QPulseAudioSink::exchangeDrainOperation(pa_operation *newOperation)
+{
+ return PAOperationUPtr(m_drainOperation.exchange(newOperation));
+}
+
+qsizetype QPulseAudioSink::defaultBufferSize() const
+{
+ if (m_spec.rate > 0)
+ return pa_usec_to_bytes(DefaultBufferLengthMs * 1000, &m_spec);
+
+ auto spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format);
+ if (pa_sample_spec_valid(&spec))
+ return pa_usec_to_bytes(DefaultBufferLengthMs * 1000, &spec);
+
+ return 0;
}
QT_END_NAMESPACE
diff --git a/src/multimedia/pulseaudio/qpulseaudiosink_p.h b/src/multimedia/pulseaudio/qpulseaudiosink_p.h
index 1a7b6d212..cf0b181ec 100644
--- a/src/multimedia/pulseaudio/qpulseaudiosink_p.h
+++ b/src/multimedia/pulseaudio/qpulseaudiosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOOUTPUTPULSE_H
#define QAUDIOOUTPUTPULSE_H
@@ -60,8 +24,10 @@
#include "qaudio.h"
#include "qaudiodevice.h"
-#include <private/qaudiosystem_p.h>
+#include "pulseaudio/qpulsehelpers_p.h"
+#include <private/qaudiosystem_p.h>
+#include <private/qaudiostatemachine_p.h>
#include <pulse/pulseaudio.h>
QT_BEGIN_NAMESPACE
@@ -72,7 +38,7 @@ class QPulseAudioSink : public QPlatformAudioSink
Q_OBJECT
public:
- QPulseAudioSink(const QByteArray &device);
+ QPulseAudioSink(const QByteArray &device, QObject *parent);
~QPulseAudioSink();
void start(QIODevice *device) override;
@@ -100,9 +66,8 @@ protected:
void timerEvent(QTimerEvent *event) override;
private:
- void setState(QAudio::State state);
- void setError(QAudio::Error error);
- void startReading();
+ void startPulling();
+ void stopTimer();
bool open();
void close();
@@ -112,7 +77,11 @@ private Q_SLOTS:
void userFeed();
void onPulseContextFailed();
+ PAOperationUPtr exchangeDrainOperation(pa_operation *newOperation);
+
private:
+ qsizetype defaultBufferSize() const;
+
pa_sample_spec m_spec = {};
// calculate timing manually, as pulseaudio doesn't give us good enough data
mutable timeval lastTimingInfo = {};
@@ -126,7 +95,7 @@ private:
QIODevice *m_audioSource = nullptr;
pa_stream *m_stream = nullptr;
- char *m_audioBuffer = nullptr;
+ std::vector<char> m_audioBuffer;
qint64 m_totalTimeValue = 0;
qint64 m_elapsedTimeOffset = 0;
@@ -134,15 +103,15 @@ private:
mutable qint64 lastProcessedUSecs = 0;
qreal m_volume = 1.0;
- QAudio::Error m_errorState = QAudio::NoError;
- QAudio::State m_deviceState = QAudio::StoppedState;
- int m_periodSize = 0;
- int m_bufferSize = 0;
- int m_maxBufferSize = 0;
- int m_periodTime = 0;
+ std::atomic<pa_operation *> m_drainOperation = nullptr;
+ qsizetype m_bufferSize = 0;
+ std::optional<qsizetype> m_userBufferSize = std::nullopt;
+ int m_pullingPeriodSize = 0;
+ int m_pullingPeriodTime = 0;
bool m_pullMode = true;
bool m_opened = false;
- bool m_resuming = false;
+
+ QAudioStateMachine m_stateMachine;
};
class PulseOutputPrivate : public QIODevice
diff --git a/src/multimedia/pulseaudio/qpulseaudiosource.cpp b/src/multimedia/pulseaudio/qpulseaudiosource.cpp
index 4db294b55..488daa48b 100644
--- a/src/multimedia/pulseaudio/qpulseaudiosource.cpp
+++ b/src/multimedia/pulseaudio/qpulseaudiosource.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
@@ -48,10 +12,11 @@
#include "qpulsehelpers_p.h"
#include <sys/types.h>
#include <unistd.h>
+#include <mutex> // for lock_guard
QT_BEGIN_NAMESPACE
-const int PeriodTimeMs = 50;
+const int SourcePeriodTimeMs = 50;
static void inputStreamReadCallback(pa_stream *stream, size_t length, void *userdata)
{
@@ -64,37 +29,38 @@ static void inputStreamReadCallback(pa_stream *stream, size_t length, void *user
static void inputStreamStateCallback(pa_stream *stream, void *userdata)
{
+ using namespace QPulseAudioInternal;
+
Q_UNUSED(userdata);
pa_stream_state_t state = pa_stream_get_state(stream);
-#ifdef DEBUG_PULSE
- qDebug() << "Stream state: " << QPulseAudioInternal::stateToQString(state);
-#endif
+ qCDebug(qLcPulseAudioIn) << "Stream state: " << state;
switch (state) {
- case PA_STREAM_CREATING:
+ case PA_STREAM_CREATING:
break;
- case PA_STREAM_READY: {
-#ifdef DEBUG_PULSE
- QPulseAudioSource *audioInput = static_cast<QPulseAudioSource*>(userdata);
+ case PA_STREAM_READY:
+ if (Q_UNLIKELY(qLcPulseAudioIn().isEnabled(QtDebugMsg))) {
+ QPulseAudioSource *audioInput = static_cast<QPulseAudioSource *>(userdata);
const pa_buffer_attr *buffer_attr = pa_stream_get_buffer_attr(stream);
- qDebug() << "*** maxlength: " << buffer_attr->maxlength;
- qDebug() << "*** prebuf: " << buffer_attr->prebuf;
- qDebug() << "*** fragsize: " << buffer_attr->fragsize;
- qDebug() << "*** minreq: " << buffer_attr->minreq;
- qDebug() << "*** tlength: " << buffer_attr->tlength;
-
- pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(audioInput->format());
- qDebug() << "*** bytes_to_usec: " << pa_bytes_to_usec(buffer_attr->fragsize, &spec);
-#endif
- }
- break;
- case PA_STREAM_TERMINATED:
- break;
- case PA_STREAM_FAILED:
- default:
- qWarning() << QString::fromLatin1("Stream error: %1").arg(QString::fromUtf8(pa_strerror(pa_context_errno(pa_stream_get_context(stream)))));
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
- break;
+ qCDebug(qLcPulseAudioIn) << "*** maxlength: " << buffer_attr->maxlength;
+ qCDebug(qLcPulseAudioIn) << "*** prebuf: " << buffer_attr->prebuf;
+ qCDebug(qLcPulseAudioIn) << "*** fragsize: " << buffer_attr->fragsize;
+ qCDebug(qLcPulseAudioIn) << "*** minreq: " << buffer_attr->minreq;
+ qCDebug(qLcPulseAudioIn) << "*** tlength: " << buffer_attr->tlength;
+
+ pa_sample_spec spec =
+ QPulseAudioInternal::audioFormatToSampleSpec(audioInput->format());
+ qCDebug(qLcPulseAudioIn)
+ << "*** bytes_to_usec: " << pa_bytes_to_usec(buffer_attr->fragsize, &spec);
+ }
+ break;
+ case PA_STREAM_TERMINATED:
+ break;
+ case PA_STREAM_FAILED:
+ default:
+ qWarning() << "Stream error: " << currentError(stream);
+ QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
+ pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
+ break;
}
}
@@ -118,71 +84,49 @@ static void inputStreamSuccessCallback(pa_stream *stream, int success, void *use
Q_UNUSED(userdata);
Q_UNUSED(success);
- //if (!success)
- //TODO: Is cork success? i->operation_success = success;
+ // if (!success)
+ // TODO: Is cork success? i->operation_success = success;
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}
-QPulseAudioSource::QPulseAudioSource(const QByteArray &device)
- : m_totalTimeValue(0)
- , m_audioSource(nullptr)
- , m_errorState(QAudio::NoError)
- , m_deviceState(QAudio::StoppedState)
- , m_volume(qreal(1.0f))
- , m_pullMode(true)
- , m_opened(false)
- , m_bytesAvailable(0)
- , m_bufferSize(0)
- , m_periodSize(0)
- , m_periodTime(PeriodTimeMs)
- , m_stream(nullptr)
- , m_device(device)
+QPulseAudioSource::QPulseAudioSource(const QByteArray &device, QObject *parent)
+ : QPlatformAudioSource(parent),
+ m_totalTimeValue(0),
+ m_audioSource(nullptr),
+ m_volume(qreal(1.0f)),
+ m_pullMode(true),
+ m_opened(false),
+ m_bufferSize(0),
+ m_periodSize(0),
+ m_periodTime(SourcePeriodTimeMs),
+ m_stream(nullptr),
+ m_device(device),
+ m_stateMachine(*this)
{
- m_timer = new QTimer(this);
- connect(m_timer, SIGNAL(timeout()), SLOT(userFeed()));
}
QPulseAudioSource::~QPulseAudioSource()
{
- close();
- disconnect(m_timer, SIGNAL(timeout()));
- QCoreApplication::processEvents();
- delete m_timer;
-}
-
-void QPulseAudioSource::setError(QAudio::Error error)
-{
- if (m_errorState == error)
- return;
-
- m_errorState = error;
- emit errorChanged(error);
+ // TODO: Investigate draining the stream
+ if (auto notifier = m_stateMachine.stop())
+ close();
}
QAudio::Error QPulseAudioSource::error() const
{
- return m_errorState;
-}
-
-void QPulseAudioSource::setState(QAudio::State state)
-{
- if (m_deviceState == state)
- return;
-
- m_deviceState = state;
- emit stateChanged(state);
+ return m_stateMachine.error();
}
QAudio::State QPulseAudioSource::state() const
{
- return m_deviceState;
+ return m_stateMachine.state();
}
void QPulseAudioSource::setFormat(const QAudioFormat &format)
{
- if (m_deviceState == QAudio::StoppedState)
+ if (!m_stateMachine.isActiveOrIdle())
m_format = format;
}
@@ -193,15 +137,7 @@ QAudioFormat QPulseAudioSource::format() const
void QPulseAudioSource::start(QIODevice *device)
{
- setState(QAudio::StoppedState);
- setError(QAudio::NoError);
-
- if (!m_pullMode && m_audioSource) {
- delete m_audioSource;
- m_audioSource = nullptr;
- }
-
- close();
+ reset();
if (!open())
return;
@@ -209,20 +145,12 @@ void QPulseAudioSource::start(QIODevice *device)
m_pullMode = true;
m_audioSource = device;
- setState(QAudio::ActiveState);
+ m_stateMachine.start();
}
QIODevice *QPulseAudioSource::start()
{
- setState(QAudio::StoppedState);
- setError(QAudio::NoError);
-
- if (!m_pullMode && m_audioSource) {
- delete m_audioSource;
- m_audioSource = nullptr;
- }
-
- close();
+ reset();
if (!open())
return nullptr;
@@ -231,20 +159,15 @@ QIODevice *QPulseAudioSource::start()
m_audioSource = new PulseInputPrivate(this);
m_audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
- setState(QAudio::IdleState);
+ m_stateMachine.start(false);
return m_audioSource;
}
void QPulseAudioSource::stop()
{
- if (m_deviceState == QAudio::StoppedState)
- return;
-
- close();
-
- setError(QAudio::NoError);
- setState(QAudio::StoppedState);
+ if (auto notifier = m_stateMachine.stop())
+ close();
}
bool QPulseAudioSource::open()
@@ -254,9 +177,9 @@ bool QPulseAudioSource::open()
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
+ if (!pulseEngine->context()
+ || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) {
+ m_stateMachine.stopOrUpdateError(QAudio::FatalError);
return false;
}
@@ -265,27 +188,27 @@ bool QPulseAudioSource::open()
Q_ASSERT(spec.channels == channel_map.channels);
if (!pa_sample_spec_valid(&spec)) {
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
+ m_stateMachine.stopOrUpdateError(QAudio::OpenError);
return false;
}
m_spec = spec;
-#ifdef DEBUG_PULSE
-// QTime now(QTime::currentTime());
-// qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
-#endif
+ //if (Q_UNLIKELY(qLcPulseAudioIn().isEnabled(QtDebugMsg)) {
+ // QTime now(QTime::currentTime());
+ // qCDebug(qLcPulseAudioIn) << now.second() << "s " << now.msec() << "ms :open()";
+ //}
if (m_streamName.isNull())
- m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8();
-
-#ifdef DEBUG_PULSE
- qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format);
- qDebug() << "Rate: " << spec.rate;
- qDebug() << "Channels: " << spec.channels;
- qDebug() << "Frame size: " << pa_frame_size(&spec);
-#endif
+ m_streamName =
+ QStringLiteral("QtmPulseStream-%1-%2").arg(::getpid()).arg(quintptr(this)).toUtf8();
+
+ if (Q_UNLIKELY(qLcPulseAudioIn().isEnabled(QtDebugMsg))) {
+ qCDebug(qLcPulseAudioIn) << "Format: " << spec.format;
+ qCDebug(qLcPulseAudioIn) << "Rate: " << spec.rate;
+ qCDebug(qLcPulseAudioIn) << "Channels: " << spec.channels;
+ qCDebug(qLcPulseAudioIn) << "Frame size: " << pa_frame_size(&spec);
+ }
pulseEngine->lock();
@@ -297,37 +220,41 @@ bool QPulseAudioSource::open()
pa_stream_set_underflow_callback(m_stream, inputStreamUnderflowCallback, this);
pa_stream_set_overflow_callback(m_stream, inputStreamOverflowCallback, this);
- m_periodSize = pa_usec_to_bytes(PeriodTimeMs*1000, &spec);
+ m_periodSize = pa_usec_to_bytes(SourcePeriodTimeMs * 1000, &spec);
int flags = 0;
pa_buffer_attr buffer_attr;
- buffer_attr.maxlength = (uint32_t) -1;
- buffer_attr.prebuf = (uint32_t) -1;
- buffer_attr.tlength = (uint32_t) -1;
- buffer_attr.minreq = (uint32_t) -1;
+ buffer_attr.maxlength = static_cast<uint32_t>(-1);
+ buffer_attr.prebuf = static_cast<uint32_t>(-1);
+ buffer_attr.tlength = static_cast<uint32_t>(-1);
+ buffer_attr.minreq = static_cast<uint32_t>(-1);
flags |= PA_STREAM_ADJUST_LATENCY;
if (m_bufferSize > 0)
- buffer_attr.fragsize = (uint32_t) m_bufferSize;
+ buffer_attr.fragsize = static_cast<uint32_t>(m_bufferSize);
else
- buffer_attr.fragsize = (uint32_t) m_periodSize;
+ buffer_attr.fragsize = static_cast<uint32_t>(m_periodSize);
+
+ flags |= PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING;
- flags |= PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING;
- if (pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr, (pa_stream_flags_t)flags) < 0) {
+ int connectionResult = pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr,
+ static_cast<pa_stream_flags_t>(flags));
+ if (connectionResult < 0) {
qWarning() << "pa_stream_connect_record() failed!";
pa_stream_unref(m_stream);
m_stream = nullptr;
pulseEngine->unlock();
- setError(QAudio::OpenError);
- setState(QAudio::StoppedState);
+ m_stateMachine.stopOrUpdateError(QAudio::OpenError);
return false;
}
-// auto *ss = pa_stream_get_sample_spec(m_stream);
-// qDebug() << "connected stream:";
-// qDebug() << " channels" << ss->channels << spec.channels;
-// qDebug() << " format" << ss->format << spec.format;
-// qDebug() << " rate" << ss->rate << spec.rate;
+ //if (Q_UNLIKELY(qLcPulseAudioIn().isEnabled(QtDebugMsg))) {
+ // auto *ss = pa_stream_get_sample_spec(m_stream);
+ // qCDebug(qLcPulseAudioIn) << "connected stream:";
+ // qCDebug(qLcPulseAudioIn) << " channels" << ss->channels << spec.channels;
+ // qCDebug(qLcPulseAudioIn) << " format" << ss->format << spec.format;
+ // qCDebug(qLcPulseAudioIn) << " rate" << ss->rate << spec.rate;
+ //}
while (pa_stream_get_state(m_stream) != PA_STREAM_READY)
pa_threaded_mainloop_wait(pulseEngine->mainloop());
@@ -335,15 +262,16 @@ bool QPulseAudioSource::open()
const pa_buffer_attr *actualBufferAttr = pa_stream_get_buffer_attr(m_stream);
m_periodSize = actualBufferAttr->fragsize;
m_periodTime = pa_bytes_to_usec(m_periodSize, &spec) / 1000;
- if (actualBufferAttr->tlength != (uint32_t)-1)
+ if (actualBufferAttr->tlength != static_cast<uint32_t>(-1))
m_bufferSize = actualBufferAttr->tlength;
pulseEngine->unlock();
- connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSource::onPulseContextFailed);
+ connect(pulseEngine, &QPulseAudioEngine::contextFailed, this,
+ &QPulseAudioSource::onPulseContextFailed);
m_opened = true;
- m_timer->start(m_periodTime);
+ m_timer.start(m_periodTime, this);
m_elapsedTimeOffset = 0;
m_totalTimeValue = 0;
@@ -356,12 +284,12 @@ void QPulseAudioSource::close()
if (!m_opened)
return;
- m_timer->stop();
+ m_timer.stop();
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
if (m_stream) {
- pulseEngine->lock();
+ std::lock_guard lock(*pulseEngine);
pa_stream_set_state_callback(m_stream, nullptr, nullptr);
pa_stream_set_read_callback(m_stream, nullptr, nullptr);
@@ -371,11 +299,10 @@ void QPulseAudioSource::close()
pa_stream_disconnect(m_stream);
pa_stream_unref(m_stream);
m_stream = nullptr;
-
- pulseEngine->unlock();
}
- disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSource::onPulseContextFailed);
+ disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this,
+ &QPulseAudioSource::onPulseContextFailed);
if (!m_pullMode && m_audioSource) {
delete m_audioSource;
@@ -384,32 +311,31 @@ void QPulseAudioSource::close()
m_opened = false;
}
-int QPulseAudioSource::checkBytesReady()
+qsizetype QPulseAudioSource::bytesReady() const
{
- if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) {
- m_bytesAvailable = 0;
- } else {
- m_bytesAvailable = pa_stream_readable_size(m_stream);
- }
+ using namespace QPulseAudioInternal;
- return m_bytesAvailable;
-}
+ if (!m_stateMachine.isActiveOrIdle())
+ return 0;
-qsizetype QPulseAudioSource::bytesReady() const
-{
- return qMax(m_bytesAvailable, 0);
+ std::lock_guard lock(*QPulseAudioEngine::instance());
+
+ int bytes = pa_stream_readable_size(m_stream);
+ if (bytes < 0) {
+ qWarning() << "pa_stream_readable_size() failed:" << currentError(m_stream);
+ return 0;
+ }
+
+ return static_cast<qsizetype>(bytes);
}
qint64 QPulseAudioSource::read(char *data, qint64 len)
{
- Q_ASSERT(data != nullptr || len == 0);
+ using namespace QPulseAudioInternal;
- m_bytesAvailable = checkBytesReady();
-
- setError(QAudio::NoError);
- if (state() == QAudio::IdleState)
- setState(QAudio::ActiveState);
+ Q_ASSERT(data != nullptr || len == 0);
+ m_stateMachine.updateActiveOrIdle(true, QAudio::NoError);
int readBytes = 0;
if (!m_pullMode && !m_tempBuffer.isEmpty()) {
@@ -429,21 +355,22 @@ qint64 QPulseAudioSource::read(char *data, qint64 len)
while (pa_stream_readable_size(m_stream) > 0) {
size_t readLength = 0;
-#ifdef DEBUG_PULSE
- qDebug() << "QPulseAudioSource::read -- " << pa_stream_readable_size(m_stream) << " bytes available from pulse audio";
-#endif
+ if (Q_UNLIKELY(qLcPulseAudioIn().isEnabled(QtDebugMsg))) {
+ auto readableSize = pa_stream_readable_size(m_stream);
+ qCDebug(qLcPulseAudioIn) << "QPulseAudioSource::read -- " << readableSize
+ << " bytes available from pulse audio";
+ }
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
pulseEngine->lock();
const void *audioBuffer;
- // Second and third parameters (audioBuffer and length) to pa_stream_peek are output parameters,
- // the audioBuffer pointer is set to point to the actual pulse audio data,
- // and the length is set to the length of this data.
+ // Second and third parameters (audioBuffer and length) to pa_stream_peek are output
+ // parameters, the audioBuffer pointer is set to point to the actual pulse audio data, and
+ // the length is set to the length of this data.
if (pa_stream_peek(m_stream, &audioBuffer, &readLength) < 0) {
- qWarning() << QString::fromLatin1("pa_stream_peek() failed: %1")
- .arg(QString::fromUtf8(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream)))));
+ qWarning() << "pa_stream_peek() failed:" << currentError(m_stream);
pulseEngine->unlock();
return 0;
}
@@ -456,10 +383,7 @@ qint64 QPulseAudioSource::read(char *data, qint64 len)
if (actualLength < qint64(readLength)) {
pulseEngine->unlock();
-
- setError(QAudio::UnderrunError);
- setState(QAudio::IdleState);
-
+ m_stateMachine.updateActiveOrIdle(false, QAudio::UnderrunError);
return actualLength;
}
} else {
@@ -467,18 +391,19 @@ qint64 QPulseAudioSource::read(char *data, qint64 len)
applyVolume(audioBuffer, data + readBytes, actualLength);
}
-#ifdef DEBUG_PULSE
- qDebug() << "QPulseAudioSource::read -- wrote " << actualLength << " to client";
-#endif
+ qCDebug(qLcPulseAudioIn) << "QPulseAudioSource::read -- wrote " << actualLength
+ << " to client";
if (actualLength < qint64(readLength)) {
-#ifdef DEBUG_PULSE
- qDebug() << "QPulseAudioSource::read -- appending " << readLength - actualLength << " bytes of data to temp buffer";
-#endif
int diff = readLength - actualLength;
int oldSize = m_tempBuffer.size();
+
+ qCDebug(qLcPulseAudioIn) << "QPulseAudioSource::read -- appending " << diff
+ << " bytes of data to temp buffer";
+
m_tempBuffer.resize(m_tempBuffer.size() + diff);
- applyVolume(static_cast<const char *>(audioBuffer) + actualLength, m_tempBuffer.data() + oldSize, diff);
+ applyVolume(static_cast<const char *>(audioBuffer) + actualLength,
+ m_tempBuffer.data() + oldSize, diff);
QMetaObject::invokeMethod(this, "userFeed", Qt::QueuedConnection);
}
@@ -492,9 +417,8 @@ qint64 QPulseAudioSource::read(char *data, qint64 len)
break;
}
-#ifdef DEBUG_PULSE
- qDebug() << "QPulseAudioSource::read -- returning after reading " << readBytes << " bytes";
-#endif
+ qCDebug(qLcPulseAudioIn) << "QPulseAudioSource::read -- returning after reading " << readBytes
+ << " bytes";
return readBytes;
}
@@ -510,22 +434,18 @@ void QPulseAudioSource::applyVolume(const void *src, void *dest, int len)
void QPulseAudioSource::resume()
{
- if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) {
- QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_operation *operation;
-
- pulseEngine->lock();
-
- operation = pa_stream_cork(m_stream, 0, inputStreamSuccessCallback, nullptr);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
+ if (auto notifier = m_stateMachine.resume()) {
+ {
+ QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pulseEngine->unlock();
+ std::lock_guard lock(*pulseEngine);
- m_timer->start(m_periodTime);
+ PAOperationUPtr operation(
+ pa_stream_cork(m_stream, 0, inputStreamSuccessCallback, nullptr));
+ pulseEngine->wait(operation.get());
+ }
- setState(QAudio::ActiveState);
- setError(QAudio::NoError);
+ m_timer.start(m_periodTime, this);
}
}
@@ -559,81 +479,69 @@ qint64 QPulseAudioSource::processedUSecs() const
pa_usec_t usecs = 0;
int result = pa_stream_get_time(m_stream, &usecs);
Q_UNUSED(result);
-// if (result != 0)
-// qWarning() << "no timing info from pulse";
+ //if (result != 0)
+ // qWarning() << "no timing info from pulse";
return usecs;
}
void QPulseAudioSource::suspend()
{
- if (m_deviceState == QAudio::ActiveState) {
- setError(QAudio::NoError);
- setState(QAudio::SuspendedState);
-
- m_timer->stop();
+ if (auto notifier = m_stateMachine.suspend()) {
+ m_timer.stop();
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
- pa_operation *operation;
- pulseEngine->lock();
+ std::lock_guard lock(*pulseEngine);
- operation = pa_stream_cork(m_stream, 1, inputStreamSuccessCallback, nullptr);
- pulseEngine->wait(operation);
- pa_operation_unref(operation);
-
- pulseEngine->unlock();
+ PAOperationUPtr operation(pa_stream_cork(m_stream, 1, inputStreamSuccessCallback, nullptr));
+ pulseEngine->wait(operation.get());
}
}
-void QPulseAudioSource::userFeed()
+void QPulseAudioSource::timerEvent(QTimerEvent *event)
{
- if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState)
- return;
-#ifdef DEBUG_PULSE
-// QTime now(QTime::currentTime());
-// qDebug()<< now.second() << "s " << now.msec() << "ms :userFeed() IN";
-#endif
- deviceReady();
+ if (event->timerId() == m_timer.timerId())
+ userFeed();
+
+ QPlatformAudioSource::timerEvent(event);
}
-bool QPulseAudioSource::deviceReady()
+void QPulseAudioSource::userFeed()
{
- if (m_pullMode) {
+ if (!m_stateMachine.isActiveOrIdle())
+ return;
+
+ //if (Q_UNLIKELY(qLcPulseAudioIn().isEnabled(QtDebugMsg)) {
+ // QTime now(QTime::currentTime());
+ // qCDebug(qLcPulseAudioIn) << now.second() << "s " << now.msec() << "ms :userFeed() IN";
+ //}
+
+ if (m_pullMode) {
// reads some audio data and writes it to QIODevice
read(nullptr,0);
- } else {
+ } else if (m_audioSource != nullptr) {
// emits readyRead() so user will call read() on QIODevice to get some audio data
- if (m_audioSource != nullptr) {
- PulseInputPrivate *a = qobject_cast<PulseInputPrivate*>(m_audioSource);
- a->trigger();
- }
+ PulseInputPrivate *a = qobject_cast<PulseInputPrivate*>(m_audioSource);
+ a->trigger();
}
- m_bytesAvailable = checkBytesReady();
-
- if (m_deviceState != QAudio::ActiveState)
- return true;
-
- return true;
}
void QPulseAudioSource::reset()
{
- stop();
- m_bytesAvailable = 0;
+ if (auto notifier = m_stateMachine.stopOrUpdateError())
+ close();
}
void QPulseAudioSource::onPulseContextFailed()
{
- close();
-
- setError(QAudio::FatalError);
- setState(QAudio::StoppedState);
+ if (auto notifier = m_stateMachine.stopOrUpdateError(QAudio::FatalError))
+ close();
}
PulseInputPrivate::PulseInputPrivate(QPulseAudioSource *audio)
{
- m_audioDevice = qobject_cast<QPulseAudioSource*>(audio);
+ m_audioDevice = qobject_cast<QPulseAudioSource *>(audio);
}
qint64 PulseInputPrivate::readData(char *data, qint64 len)
diff --git a/src/multimedia/pulseaudio/qpulseaudiosource_p.h b/src/multimedia/pulseaudio/qpulseaudiosource_p.h
index 7a9f047dd..d652f81a0 100644
--- a/src/multimedia/pulseaudio/qpulseaudiosource_p.h
+++ b/src/multimedia/pulseaudio/qpulseaudiosource_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -61,6 +25,7 @@
#include "qaudio.h"
#include "qaudiodevice.h"
#include <private/qaudiosystem_p.h>
+#include <private/qaudiostatemachine_p.h>
#include <pulse/pulseaudio.h>
@@ -73,7 +38,7 @@ class QPulseAudioSource : public QPlatformAudioSource
Q_OBJECT
public:
- QPulseAudioSource(const QByteArray &device);
+ QPulseAudioSource(const QByteArray &device, QObject *parent);
~QPulseAudioSource();
qint64 read(char *data, qint64 len);
@@ -99,38 +64,35 @@ public:
qint64 m_totalTimeValue;
QIODevice *m_audioSource;
QAudioFormat m_format;
- QAudio::Error m_errorState;
- QAudio::State m_deviceState;
qreal m_volume;
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
private slots:
void userFeed();
- bool deviceReady();
void onPulseContextFailed();
private:
- void setState(QAudio::State state);
- void setError(QAudio::Error error);
-
void applyVolume(const void *src, void *dest, int len);
- int checkBytesReady();
bool open();
void close();
bool m_pullMode;
bool m_opened;
- int m_bytesAvailable;
int m_bufferSize;
int m_periodSize;
unsigned int m_periodTime;
- QTimer *m_timer;
+ QBasicTimer m_timer;
qint64 m_elapsedTimeOffset;
pa_stream *m_stream;
QByteArray m_streamName;
QByteArray m_device;
QByteArray m_tempBuffer;
pa_sample_spec m_spec;
+
+ QAudioStateMachine m_stateMachine;
};
class PulseInputPrivate : public QIODevice
@@ -138,7 +100,7 @@ class PulseInputPrivate : public QIODevice
Q_OBJECT
public:
PulseInputPrivate(QPulseAudioSource *audio);
- ~PulseInputPrivate() {};
+ ~PulseInputPrivate() override = default;
qint64 readData(char *data, qint64 len) override;
qint64 writeData(const char *data, qint64 len) override;
diff --git a/src/multimedia/pulseaudio/qpulsehelpers.cpp b/src/multimedia/pulseaudio/qpulsehelpers.cpp
index 0136fd125..bc03e133f 100644
--- a/src/multimedia/pulseaudio/qpulsehelpers.cpp
+++ b/src/multimedia/pulseaudio/qpulsehelpers.cpp
@@ -1,47 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qpulsehelpers_p.h"
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qLcPulseAudioOut, "qt.multimedia.pulseaudio.output")
+Q_LOGGING_CATEGORY(qLcPulseAudioIn, "qt.multimedia.pulseaudio.input")
+Q_LOGGING_CATEGORY(qLcPulseAudioEngine, "qt.multimedia.pulseaudio.engine")
namespace QPulseAudioInternal
{
@@ -233,6 +199,86 @@ QAudioFormat sampleSpecToAudioFormat(const pa_sample_spec &spec)
return format;
}
+QUtf8StringView currentError(const pa_context *context)
+{
+ return pa_strerror(pa_context_errno(context));
+}
+
+QUtf8StringView currentError(const pa_stream *stream)
+{
+ return currentError(pa_stream_get_context(stream));
+}
+
+} // namespace QPulseAudioInternal
+
+static QLatin1StringView stateToQStringView(pa_stream_state_t state)
+{
+ using namespace Qt::StringLiterals;
+ switch (state)
+ {
+ case PA_STREAM_UNCONNECTED: return "Unconnected"_L1;
+ case PA_STREAM_CREATING: return "Creating"_L1;
+ case PA_STREAM_READY: return "Ready"_L1;
+ case PA_STREAM_FAILED: return "Failed"_L1;
+ case PA_STREAM_TERMINATED: return "Terminated"_L1;
+ default: Q_UNREACHABLE_RETURN("Unknown stream state"_L1);
+ }
+}
+
+static QLatin1StringView sampleFormatToQStringView(pa_sample_format format)
+{
+ using namespace Qt::StringLiterals;
+ switch (format)
+ {
+ case PA_SAMPLE_U8: return "Unsigned 8 Bit PCM."_L1;
+ case PA_SAMPLE_ALAW: return "8 Bit a-Law "_L1;
+ case PA_SAMPLE_ULAW: return "8 Bit mu-Law"_L1;
+ case PA_SAMPLE_S16LE: return "Signed 16 Bit PCM, little endian (PC)."_L1;
+ case PA_SAMPLE_S16BE: return "Signed 16 Bit PCM, big endian."_L1;
+ case PA_SAMPLE_FLOAT32LE: return "32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0"_L1;
+ case PA_SAMPLE_FLOAT32BE: return "32 Bit IEEE floating point, big endian, range -1.0 to 1.0"_L1;
+ case PA_SAMPLE_S32LE: return "Signed 32 Bit PCM, little endian (PC)."_L1;
+ case PA_SAMPLE_S32BE: return "Signed 32 Bit PCM, big endian."_L1;
+ case PA_SAMPLE_S24LE: return "Signed 24 Bit PCM packed, little endian (PC)."_L1;
+ case PA_SAMPLE_S24BE: return "Signed 24 Bit PCM packed, big endian."_L1;
+ case PA_SAMPLE_S24_32LE: return "Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC)."_L1;
+ case PA_SAMPLE_S24_32BE: return "Signed 24 Bit PCM in LSB of 32 Bit words, big endian."_L1;
+ case PA_SAMPLE_MAX: return "Upper limit of valid sample types."_L1;
+ case PA_SAMPLE_INVALID: return "Invalid sample format"_L1;
+ default: Q_UNREACHABLE_RETURN("Unknown sample format"_L1);
+ }
+}
+
+static QLatin1StringView stateToQStringView(pa_context_state_t state)
+{
+ using namespace Qt::StringLiterals;
+ switch (state)
+ {
+ case PA_CONTEXT_UNCONNECTED: return "Unconnected"_L1;
+ case PA_CONTEXT_CONNECTING: return "Connecting"_L1;
+ case PA_CONTEXT_AUTHORIZING: return "Authorizing"_L1;
+ case PA_CONTEXT_SETTING_NAME: return "Setting Name"_L1;
+ case PA_CONTEXT_READY: return "Ready"_L1;
+ case PA_CONTEXT_FAILED: return "Failed"_L1;
+ case PA_CONTEXT_TERMINATED: return "Terminated"_L1;
+ default: Q_UNREACHABLE_RETURN("Unknown context state"_L1);
+ }
+}
+
+
+QDebug operator<<(QDebug dbg, pa_stream_state_t state)
+{
+ return dbg << stateToQStringView(state);
+}
+
+QDebug operator<<(QDebug dbg, pa_sample_format format)
+{
+ return dbg << sampleFormatToQStringView(format);
+}
+
+QDebug operator<<(QDebug dbg, pa_context_state_t state)
+{
+ return dbg << stateToQStringView(state);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/pulseaudio/qpulsehelpers_p.h b/src/multimedia/pulseaudio/qpulsehelpers_p.h
index 00f26757e..d271fde48 100644
--- a/src/multimedia/pulseaudio/qpulsehelpers_p.h
+++ b/src/multimedia/pulseaudio/qpulsehelpers_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPULSEHELPER_H
#define QPULSEHELPER_H
@@ -55,10 +19,20 @@
#include <qaudioformat.h>
#include <pulse/pulseaudio.h>
#include <QtCore/QLoggingCategory>
+#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qLcPulseAudioOut)
+Q_DECLARE_LOGGING_CATEGORY(qLcPulseAudioIn)
+Q_DECLARE_LOGGING_CATEGORY(qLcPulseAudioEngine)
+
+struct PAOperationDeleter
+{
+ void operator()(pa_operation *op) const { pa_operation_unref(op); }
+};
+
+using PAOperationUPtr = std::unique_ptr<pa_operation, PAOperationDeleter>;
namespace QPulseAudioInternal
{
@@ -67,63 +41,14 @@ QAudioFormat sampleSpecToAudioFormat(const pa_sample_spec &spec);
pa_channel_map channelMapForAudioFormat(const QAudioFormat &format);
QAudioFormat::ChannelConfig channelConfigFromMap(const pa_channel_map &map);
-static inline QString stateToQString(pa_stream_state_t state)
-{
- using namespace Qt::StringLiterals;
- switch (state)
- {
- case PA_STREAM_UNCONNECTED: return "Unconnected"_L1;
- case PA_STREAM_CREATING: return "Creating"_L1;
- case PA_STREAM_READY: return "Ready"_L1;
- case PA_STREAM_FAILED: return "Failed"_L1;
- case PA_STREAM_TERMINATED: return "Terminated"_L1;
- }
-
- return u"Unknown state: %0"_s.arg(int(state));
-}
+QUtf8StringView currentError(const pa_context *);
+QUtf8StringView currentError(const pa_stream *);
-static inline QString sampleFormatToQString(pa_sample_format format)
-{
- using namespace Qt::StringLiterals;
- switch (format)
- {
- case PA_SAMPLE_U8: return "Unsigned 8 Bit PCM."_L1;
- case PA_SAMPLE_ALAW: return "8 Bit a-Law "_L1;
- case PA_SAMPLE_ULAW: return "8 Bit mu-Law"_L1;
- case PA_SAMPLE_S16LE: return "Signed 16 Bit PCM, little endian (PC)."_L1;
- case PA_SAMPLE_S16BE: return "Signed 16 Bit PCM, big endian."_L1;
- case PA_SAMPLE_FLOAT32LE: return "32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0"_L1;
- case PA_SAMPLE_FLOAT32BE: return "32 Bit IEEE floating point, big endian, range -1.0 to 1.0"_L1;
- case PA_SAMPLE_S32LE: return "Signed 32 Bit PCM, little endian (PC)."_L1;
- case PA_SAMPLE_S32BE: return "Signed 32 Bit PCM, big endian."_L1;
- case PA_SAMPLE_S24LE: return "Signed 24 Bit PCM packed, little endian (PC)."_L1;
- case PA_SAMPLE_S24BE: return "Signed 24 Bit PCM packed, big endian."_L1;
- case PA_SAMPLE_S24_32LE: return "Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC)."_L1;
- case PA_SAMPLE_S24_32BE: return "Signed 24 Bit PCM in LSB of 32 Bit words, big endian."_L1;
- case PA_SAMPLE_MAX: return "Upper limit of valid sample types."_L1;
- case PA_SAMPLE_INVALID: return "Invalid sample format"_L1;
- }
-
- return u"Invalid value: %0"_s.arg(int(format));
-}
-
-static inline QString stateToQString(pa_context_state_t state)
-{
- using namespace Qt::StringLiterals;
- switch (state)
- {
- case PA_CONTEXT_UNCONNECTED: return "Unconnected"_L1;
- case PA_CONTEXT_CONNECTING: return "Connecting"_L1;
- case PA_CONTEXT_AUTHORIZING: return "Authorizing"_L1;
- case PA_CONTEXT_SETTING_NAME: return "Setting Name"_L1;
- case PA_CONTEXT_READY: return "Ready"_L1;
- case PA_CONTEXT_FAILED: return "Failed"_L1;
- case PA_CONTEXT_TERMINATED: return "Terminated"_L1;
- }
+} // namespace QPulseAudioInternal
- return u"Unknown state: %0"_s.arg(int(state));
-}
-}
+QDebug operator<<(QDebug, pa_stream_state_t);
+QDebug operator<<(QDebug, pa_sample_format);
+QDebug operator<<(QDebug, pa_context_state_t);
QT_END_NAMESPACE
diff --git a/src/multimedia/qerrorinfo_p.h b/src/multimedia/qerrorinfo_p.h
new file mode 100644
index 000000000..6428cb00f
--- /dev/null
+++ b/src/multimedia/qerrorinfo_p.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QERRORINFO_P_H
+#define QERRORINFO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/qtmultimediaglobal.h>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+template <typename ErrorCode, ErrorCode NoError = ErrorCode::NoError>
+class QErrorInfo
+{
+public:
+ QErrorInfo(ErrorCode error = NoError, QString description = {})
+ : m_code(error), m_description(std::move(description))
+ {
+ }
+
+ template <typename Notifier>
+ void setAndNotify(ErrorCode code, QString description, Notifier &notifier)
+ {
+ const bool changed = code != m_code || description != m_description;
+
+ m_code = code;
+ m_description = std::move(description);
+
+ if (code != NoError)
+ emit notifier.errorOccurred(m_code, m_description);
+
+ if (changed)
+ emit notifier.errorChanged();
+ }
+
+ ErrorCode code() const { return m_code; }
+ QString description() const { return m_description; };
+
+private:
+ ErrorCode m_code;
+ QString m_description;
+};
+
+QT_END_NAMESPACE
+
+#endif // QERRORINFO_P_H
diff --git a/src/multimedia/qmaybe_p.h b/src/multimedia/qmaybe_p.h
new file mode 100644
index 000000000..5fe2d4d6c
--- /dev/null
+++ b/src/multimedia/qmaybe_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMAYBE_P_H
+#define QMAYBE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <qstring.h>
+#include <optional>
+#include <utility>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+struct QUnexpect
+{
+};
+
+static constexpr QUnexpect unexpect{};
+
+template <typename Value, typename Error = QString>
+class QMaybe
+{
+public:
+ QMaybe(const Value &v)
+ {
+ if constexpr (std::is_pointer_v<Value>) {
+ if (!v)
+ return; // nullptr is treated as nullopt (for raw pointer types only)
+ }
+ m_value = v;
+ }
+
+ QMaybe(Value &&v)
+ {
+ if constexpr (std::is_pointer_v<Value>) {
+ if (!v)
+ return; // nullptr is treated as nullopt (for raw pointer types only)
+ }
+ m_value = std::move(v);
+ }
+
+ QMaybe(const QMaybe &other) = default;
+
+ QMaybe &operator=(const QMaybe &other) = default;
+
+ QMaybe(const Error &error) : m_error(error) { }
+
+ template <class... Args>
+ QMaybe(QUnexpect, Args &&...args) : m_error{ std::forward<Args>(args)... }
+ {
+ static_assert(std::is_constructible_v<Error, Args &&...>,
+ "Invalid arguments for creating an error type");
+ }
+
+ constexpr explicit operator bool() const noexcept { return m_value.has_value(); }
+
+ constexpr Value &value()
+ {
+ Q_ASSERT(m_value.has_value());
+ return *m_value;
+ }
+
+ constexpr const Value &value() const
+ {
+ Q_ASSERT(m_value.has_value());
+ return *m_value;
+ }
+
+ constexpr Value *operator->() noexcept { return std::addressof(value()); }
+ constexpr const Value *operator->() const noexcept { return std::addressof(value()); }
+
+ constexpr Value &operator*() &noexcept { return value(); }
+ constexpr const Value &operator*() const &noexcept { return value(); }
+
+ constexpr const Error &error() const { return m_error; }
+
+private:
+ std::optional<Value> m_value;
+ Error m_error;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMAYBE_P_H
diff --git a/src/multimedia/qmediadevices.cpp b/src/multimedia/qmediadevices.cpp
index 23df0473b..1d77f4de8 100644
--- a/src/multimedia/qmediadevices.cpp
+++ b/src/multimedia/qmediadevices.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmediadevices.h"
#include "private/qplatformmediaintegration_p.h"
@@ -66,6 +30,19 @@ QT_BEGIN_NAMESPACE
from the system, it will update the corresponding device list and emit a signal
notifying about the change.
+ The QMediaDevices::audioInputs and QMediaDevices::audioOutputs functions can be used
+ to enumerate all microphones and speakers/headsets on the system. This example first
+ gets a list of all connected microphones, and then prints their identifier, description,
+ and if it is the default device or not.
+
+ \snippet multimedia-snippets/devices.cpp Media Audio Input Device Enumeration
+
+ Similarly, the QMediaDevices::videoInputs will return a list of all connected cameras.
+ In this example we list all connected cameras and their identifier, description, and
+ if it is the default camera or not.
+
+ \snippet multimedia-snippets/devices.cpp Media Video Input Device Enumeration
+
QMediaDevices monitors the system defaults for each device group. It will notify about
any changes done through the system settings. For example, if the user selects a new
default audio output in the system settings, QMediaDevices will update the default audio
@@ -140,6 +117,8 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \property QMediaDevices::audioInputs
+
Returns a list of available audio input devices on the system.
Those devices are usually microphones. Devices can be either built-in, or
@@ -147,7 +126,7 @@ QT_BEGIN_NAMESPACE
*/
QList<QAudioDevice> QMediaDevices::audioInputs()
{
- return QPlatformMediaDevices::instance()->audioInputs();
+ return QPlatformMediaIntegration::instance()->mediaDevices()->audioInputs();
}
/*!
@@ -159,6 +138,8 @@ QList<QAudioDevice> QMediaDevices::audioInputs()
*/
/*!
+ \property QMediaDevices::audioOutputs
+
Returns a list of available audio output devices on the system.
Those devices are usually loudspeakers or head sets. Devices can be either
@@ -166,7 +147,7 @@ QList<QAudioDevice> QMediaDevices::audioInputs()
*/
QList<QAudioDevice> QMediaDevices::audioOutputs()
{
- return QPlatformMediaDevices::instance()->audioOutputs();
+ return QPlatformMediaIntegration::instance()->mediaDevices()->audioOutputs();
}
/*!
@@ -175,10 +156,13 @@ QList<QAudioDevice> QMediaDevices::audioOutputs()
*/
/*!
+ \property QMediaDevices::videoInputs
+
Returns a list of available cameras on the system.
*/
QList<QCameraDevice> QMediaDevices::videoInputs()
{
+ QPlatformMediaIntegration::instance()->mediaDevices()->initVideoDevicesConnection();
return QPlatformMediaIntegration::instance()->videoInputs();
}
@@ -191,6 +175,8 @@ QList<QCameraDevice> QMediaDevices::videoInputs()
*/
/*!
+ \property QMediaDevices::defaultAudioInput
+
Returns the default audio input device.
The default device can change during the runtime of the application.
@@ -216,6 +202,8 @@ QAudioDevice QMediaDevices::defaultAudioInput()
*/
/*!
+ \property QMediaDevices::defaultAudioOutput
+
Returns the default audio output device.
The default device can change during the runtime of the application. The
@@ -244,9 +232,11 @@ QAudioDevice QMediaDevices::defaultAudioOutput()
*/
/*!
+ \property QMediaDevices::defaultVideoInput
+
Returns the default camera on the system.
- /note The returned object should be checked using isNull() before being used,
+ \note The returned object should be checked using isNull() before being used,
in case there is no default camera or no cameras at all.
The default device can change during the runtime of the application. The
@@ -271,17 +261,27 @@ QCameraDevice QMediaDevices::defaultVideoInput()
QMediaDevices::QMediaDevices(QObject *parent)
: QObject(parent)
{
- QPlatformMediaDevices::instance()->addMediaDevices(this);
+ auto platformDevices = QPlatformMediaIntegration::instance()->mediaDevices();
+ connect(platformDevices, &QPlatformMediaDevices::videoInputsChanged, this,
+ &QMediaDevices::videoInputsChanged);
+ connect(platformDevices, &QPlatformMediaDevices::audioInputsChanged, this,
+ &QMediaDevices::audioInputsChanged);
+ connect(platformDevices, &QPlatformMediaDevices::audioOutputsChanged, this,
+ &QMediaDevices::audioOutputsChanged);
}
/*!
\internal
*/
-QMediaDevices::~QMediaDevices()
+QMediaDevices::~QMediaDevices() = default;
+
+void QMediaDevices::connectNotify(const QMetaMethod &signal)
{
- QPlatformMediaDevices::instance()->removeMediaDevices(this);
-}
+ if (signal == QMetaMethod::fromSignal(&QMediaDevices::videoInputsChanged))
+ QPlatformMediaIntegration::instance()->mediaDevices()->initVideoDevicesConnection();
+ QObject::connectNotify(signal);
+}
QT_END_NAMESPACE
diff --git a/src/multimedia/qmediadevices.h b/src/multimedia/qmediadevices.h
index ee69a3da8..832799095 100644
--- a/src/multimedia/qmediadevices.h
+++ b/src/multimedia/qmediadevices.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIADEVICES_H
#define QMEDIADEVICES_H
@@ -78,6 +42,9 @@ Q_SIGNALS:
void audioOutputsChanged();
void videoInputsChanged();
+protected:
+ void connectNotify(const QMetaMethod &signal) override;
+
private:
friend class QMediaDevicesPrivate;
};
diff --git a/src/multimedia/qmediaenumdebug.h b/src/multimedia/qmediaenumdebug.h
index 6fc151bbf..e9efb0983 100644
--- a/src/multimedia/qmediaenumdebug.h
+++ b/src/multimedia/qmediaenumdebug.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIAENUMDEBUG_H
#define QMEDIAENUMDEBUG_H
diff --git a/src/multimedia/qmediaformat.cpp b/src/multimedia/qmediaformat.cpp
index 27dd913cf..a05648636 100644
--- a/src/multimedia/qmediaformat.cpp
+++ b/src/multimedia/qmediaformat.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmediaformat.h"
#include "private/qplatformmediaintegration_p.h"
@@ -403,6 +367,11 @@ QMediaFormat::~QMediaFormat() = default;
QMediaFormat::QMediaFormat(const QMediaFormat &other) noexcept = default;
/*!
+ \fn void QMediaFormat::swap(QMediaFormat &other) noexcept
+
+ Swaps the media format with \a other.
+*/
+/*!
Copies \a other into this QMediaFormat object.
*/
QMediaFormat &QMediaFormat::operator=(const QMediaFormat &other) noexcept = default;
@@ -486,14 +455,6 @@ QMimeType QMediaFormat::mimeType() const
return QMimeDatabase().mimeTypeForName(QString::fromLatin1(mimeTypeForFormat[fmt + 1]));
}
-static QPlatformMediaFormatInfo *formatInfo()
-{
- QPlatformMediaFormatInfo *result = nullptr;
- if (auto *pi = QPlatformMediaIntegration::instance())
- result = pi->formatInfo();
- return result;
-}
-
/*!
\enum QMediaFormat::ConversionMode
@@ -532,8 +493,7 @@ static QPlatformMediaFormatInfo *formatInfo()
*/
QList<QMediaFormat::FileFormat> QMediaFormat::supportedFileFormats(QMediaFormat::ConversionMode m)
{
- auto *fi = formatInfo();
- return fi != nullptr ? fi->supportedFileFormats(*this, m) : QList<QMediaFormat::FileFormat>{};
+ return QPlatformMediaIntegration::instance()->formatInfo()->supportedFileFormats(*this, m);
}
/*!
@@ -559,8 +519,7 @@ QList<QMediaFormat::FileFormat> QMediaFormat::supportedFileFormats(QMediaFormat:
*/
QList<QMediaFormat::VideoCodec> QMediaFormat::supportedVideoCodecs(QMediaFormat::ConversionMode m)
{
- auto *fi = formatInfo();
- return fi != nullptr ? fi->supportedVideoCodecs(*this, m) : QList<QMediaFormat::VideoCodec>{};
+ return QPlatformMediaIntegration::instance()->formatInfo()->supportedVideoCodecs(*this, m);
}
/*!
@@ -586,8 +545,7 @@ QList<QMediaFormat::VideoCodec> QMediaFormat::supportedVideoCodecs(QMediaFormat:
*/
QList<QMediaFormat::AudioCodec> QMediaFormat::supportedAudioCodecs(QMediaFormat::ConversionMode m)
{
- auto *fi = formatInfo();
- return fi != nullptr ? fi->supportedAudioCodecs(*this, m) : QList<QMediaFormat::AudioCodec>{};
+ return QPlatformMediaIntegration::instance()->formatInfo()->supportedAudioCodecs(*this, m);
}
/*!
@@ -758,6 +716,17 @@ QString QMediaFormat::videoCodecDescription(QMediaFormat::VideoCodec codec)
return QString::fromUtf8(descriptions[int(codec) + 1]);
}
+/*!
+ \fn bool QMediaFormat::operator!=(const QMediaFormat &other) const
+
+ Returns \c true if \a other is not equal to the current media format,
+ otherwise returns \c false.
+*/
+
+/*!
+ Returns \c true if \a other is equal to the current media format, otherwise
+ returns \c false.
+*/
bool QMediaFormat::operator==(const QMediaFormat &other) const
{
Q_ASSERT(!d);
@@ -893,4 +862,22 @@ void QMediaFormat::resolveForEncoding(ResolveFlags flags)
}
}
+/*!
+ \variable QMediaFormat::audio
+ \internal
+*/
+/*!
+ \variable QMediaFormat::d
+ \internal
+*/
+/*!
+ \variable QMediaFormat::video
+ \internal
+*/
+/*!
+ \variable QMediaFormat::fmt
+ \internal
+*/
QT_END_NAMESPACE
+
+#include "moc_qmediaformat.cpp"
diff --git a/src/multimedia/qmediaformat.h b/src/multimedia/qmediaformat.h
index cff960514..225d21896 100644
--- a/src/multimedia/qmediaformat.h
+++ b/src/multimedia/qmediaformat.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIAFORMAT_H
#define QMEDIAFORMAT_H
diff --git a/src/multimedia/qmediametadata.cpp b/src/multimedia/qmediametadata.cpp
index 129889095..dc238721f 100644
--- a/src/multimedia/qmediametadata.cpp
+++ b/src/multimedia/qmediametadata.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmediametadata.h"
#include <QtCore/qcoreapplication.h>
@@ -99,7 +63,7 @@ QT_BEGIN_NAMESPACE
\row \li Composer \li The composer of the media. \li QStringList
\row \li LeadPerformer \li The lead performer in the media. \li QStringList
- \row \li ThumbnailImage \li An embedded thumbnail image. \li QImage
+ \row \li ThumbnailImage \li An embedded thumbnail image if present in metadata. \li QImage
\row \li CoverArtImage \li An embedded cover art image. \li QImage
\header \li {3,1}
@@ -280,6 +244,46 @@ QMetaType QMediaMetaData::keyType(Key key)
*/
/*!
+ \enum QMediaMetaData::Key
+
+ The following meta data keys can be used:
+
+ \value Title Media title
+ \value Author Media author
+ \value Comment Comment
+ \value Description Brief desripttion
+ \value Genre Genre the media belongs to
+ \value Date Creation date
+ \value Language Media language
+ \value Publisher Media publisher info.
+ \value Copyright Media copyright info.
+ \value Url Publisher's website URL
+ \value Duration Media playback duration
+ \value MediaType Type of the media
+ \value FileFormat File format
+ \value AudioBitRate
+ \value AudioCodec
+ \value VideoBitRate
+ \value VideoCodec
+ \value VideoFrameRate
+ \value AlbumTitle Album's title
+ \value AlbumArtist Artist's info.
+ \value ContributingArtist
+ \value TrackNumber
+ \value Composer Media composer's info.
+ \value LeadPerformer
+ \value ThumbnailImage Media thumbnail image (if embedded in metadata)
+ \value CoverArtImage Media cover art
+ \value Orientation
+ \value Resolution
+*/
+
+/*!
+ \variable QMediaMetaData::NumMetaData
+ \internal
+*/
+
+/*!
\qmlmethod variant QtMultimedia::mediaMetaData::value(Key key)
Returns the meta data value for Key \a key, or a null QVariant if no
@@ -398,7 +402,7 @@ QString QMediaMetaData::stringValue(QMediaMetaData::Key key) const
return QMediaFormat::videoCodecName(value.value<QMediaFormat::VideoCodec>());
case Resolution: {
QSize size = value.toSize();
- return QString::fromUtf8("%1 x %2").arg(size.width()).arg(size.height());
+ return QStringLiteral("%1 x %2").arg(size.width()).arg(size.height());
}
case ThumbnailImage:
case CoverArtImage:
@@ -508,3 +512,5 @@ QString QMediaMetaData::metaDataKeyToString(QMediaMetaData::Key key)
*/
QT_END_NAMESPACE
+
+#include "moc_qmediametadata.cpp"
diff --git a/src/multimedia/qmediametadata.h b/src/multimedia/qmediametadata.h
index 3040d3473..d6f4477d3 100644
--- a/src/multimedia/qmediametadata.h
+++ b/src/multimedia/qmediametadata.h
@@ -1,45 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIAMETADATA_H
#define QMEDIAMETADATA_H
+#if 0
+#pragma qt_class(QMediaMetaData)
+#endif
+
#include <QtCore/qpair.h>
#include <QtCore/qvariant.h>
#include <QtCore/qstring.h>
diff --git a/src/multimedia/qmediastoragelocation.cpp b/src/multimedia/qmediastoragelocation.cpp
index 2c30562ee..61c4061f4 100644
--- a/src/multimedia/qmediastoragelocation.cpp
+++ b/src/multimedia/qmediastoragelocation.cpp
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmediastoragelocation_p.h"
#include <QStandardPaths>
+#include <QUrl>
QT_BEGIN_NAMESPACE
@@ -57,7 +22,7 @@ QDir QMediaStorageLocation::defaultDirectory(QStandardPaths::StandardLocation ty
dirCandidates << QDir::currentPath();
dirCandidates << QDir::tempPath();
- for (const QString &path : qAsConst(dirCandidates)) {
+ for (const QString &path : std::as_const(dirCandidates)) {
QDir dir(path);
if (dir.exists() && QFileInfo(path).isWritable())
return dir;
@@ -69,13 +34,13 @@ QDir QMediaStorageLocation::defaultDirectory(QStandardPaths::StandardLocation ty
static QString generateFileName(const QDir &dir, const QString &prefix, const QString &extension)
{
auto lastMediaIndex = 0;
- const auto list = dir.entryList({ QString::fromLatin1("%1*.%2").arg(prefix, extension) });
+ const auto list = dir.entryList({ QStringLiteral("%1*.%2").arg(prefix, extension) });
for (const QString &fileName : list) {
- auto mediaIndex = QStringView{fileName}.mid(prefix.length(), fileName.size() - prefix.length() - extension.length() - 1).toInt();
+ auto mediaIndex = QStringView{fileName}.mid(prefix.size(), fileName.size() - prefix.size() - extension.size() - 1).toInt();
lastMediaIndex = qMax(lastMediaIndex, mediaIndex);
}
- const QString name = QString::fromLatin1("%1%2.%3")
+ const QString name = QStringLiteral("%1%2.%3")
.arg(prefix)
.arg(lastMediaIndex + 1, 4, 10, QLatin1Char('0'))
.arg(extension);
@@ -88,11 +53,16 @@ QString QMediaStorageLocation::generateFileName(const QString &requestedName,
QStandardPaths::StandardLocation type,
const QString &extension)
{
- auto prefix = QLatin1String("clip_");
+ using namespace Qt::StringLiterals;
+
+ if (QUrl(requestedName).scheme() == "content"_L1)
+ return requestedName;
+
+ auto prefix = "clip_"_L1;
switch (type) {
- case QStandardPaths::PicturesLocation: prefix = QLatin1String("image_"); break;
- case QStandardPaths::MoviesLocation: prefix = QLatin1String("video_"); break;
- case QStandardPaths::MusicLocation: prefix = QLatin1String("record_"); break;
+ case QStandardPaths::PicturesLocation: prefix = "image_"_L1; break;
+ case QStandardPaths::MoviesLocation: prefix = "video_"_L1; break;
+ case QStandardPaths::MusicLocation: prefix = "record_"_L1; break;
default: break;
}
@@ -101,14 +71,14 @@ QString QMediaStorageLocation::generateFileName(const QString &requestedName,
QString path = requestedName;
- if (QFileInfo(path).isRelative())
+ if (QFileInfo(path).isRelative() && QUrl(path).isRelative())
path = defaultDirectory(type).absoluteFilePath(path);
if (QFileInfo(path).isDir())
return generateFileName(QDir(path), prefix, extension);
if (!path.endsWith(extension))
- path.append(QString(QLatin1String(".%1")).arg(extension));
+ path.append(QStringLiteral(".%1").arg(extension));
return path;
}
diff --git a/src/multimedia/qmediastoragelocation_p.h b/src/multimedia/qmediastoragelocation_p.h
index 5310c8dc6..39c4b7162 100644
--- a/src/multimedia/qmediastoragelocation_p.h
+++ b/src/multimedia/qmediastoragelocation_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIASTORAGELOCATION_H
#define QMEDIASTORAGELOCATION_H
diff --git a/src/multimedia/qmediatimerange.cpp b/src/multimedia/qmediatimerange.cpp
index 80ab5d928..2af9cefc6 100644
--- a/src/multimedia/qmediatimerange.cpp
+++ b/src/multimedia/qmediatimerange.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qdebug.h>
@@ -166,7 +130,7 @@ void QMediaTimeRangePrivate::addInterval(const QMediaTimeRange::Interval &interv
// Find a place to insert the interval
int i;
- for (i = 0; i < intervals.count(); i++) {
+ for (i = 0; i < intervals.size(); i++) {
// Insert before this element
if(interval.s < intervals[i].s) {
intervals.insert(i, interval);
@@ -175,7 +139,7 @@ void QMediaTimeRangePrivate::addInterval(const QMediaTimeRange::Interval &interv
}
// Interval needs to be added to the end of the list
- if (i == intervals.count())
+ if (i == intervals.size())
intervals.append(interval);
// Do we need to correct the element before us?
@@ -183,7 +147,7 @@ void QMediaTimeRangePrivate::addInterval(const QMediaTimeRange::Interval &interv
i--;
// Merge trailing ranges
- while (i < intervals.count() - 1
+ while (i < intervals.size() - 1
&& intervals[i].e >= intervals[i + 1].s - 1) {
intervals[i].e = qMax(intervals[i].e, intervals[i + 1].e);
intervals.removeAt(i + 1);
@@ -196,7 +160,7 @@ void QMediaTimeRangePrivate::removeInterval(const QMediaTimeRange::Interval &int
if (!interval.isNormal())
return;
- for (int i = 0; i < intervals.count(); i++) {
+ for (int i = 0; i < intervals.size(); i++) {
const QMediaTimeRange::Interval r = intervals.at(i);
if (r.e < interval.s) {
@@ -296,6 +260,12 @@ QMediaTimeRange::QMediaTimeRange(const QMediaTimeRange &range) noexcept = defaul
*/
/*!
+ \fn void QMediaTimeRange::swap(QMediaTimeRange &other) noexcept
+
+ Swaps the current instance with the \a other.
+*/
+
+/*!
\fn QMediaTimeRange::~QMediaTimeRange()
Destructor.
@@ -347,7 +317,7 @@ qint64 QMediaTimeRange::earliestTime() const
qint64 QMediaTimeRange::latestTime() const
{
if (!d->intervals.isEmpty())
- return d->intervals[d->intervals.count() - 1].end();
+ return d->intervals[d->intervals.size() - 1].end();
return 0;
}
@@ -536,7 +506,7 @@ bool QMediaTimeRange::isEmpty() const
*/
bool QMediaTimeRange::isContinuous() const
{
- return (d->intervals.count() <= 1);
+ return (d->intervals.size() <= 1);
}
/*!
@@ -546,7 +516,7 @@ bool QMediaTimeRange::isContinuous() const
*/
bool QMediaTimeRange::contains(qint64 time) const
{
- for (int i = 0; i < d->intervals.count(); i++) {
+ for (int i = 0; i < d->intervals.size(); i++) {
if (d->intervals[i].contains(time))
return true;
diff --git a/src/multimedia/qmediatimerange.h b/src/multimedia/qmediatimerange.h
index 8fcef3be8..7245a845a 100644
--- a/src/multimedia/qmediatimerange.h
+++ b/src/multimedia/qmediatimerange.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIATIMERANGE_H
#define QMEDIATIMERANGE_H
diff --git a/src/multimedia/qmultimediautils.cpp b/src/multimedia/qmultimediautils.cpp
index 54653751b..9740b6d60 100644
--- a/src/multimedia/qmultimediautils.cpp
+++ b/src/multimedia/qmultimediautils.cpp
@@ -1,50 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmultimediautils_p.h"
+#include "qvideoframe.h"
+#include "qvideoframeformat.h"
+
+#include <QtCore/qdir.h>
QT_BEGIN_NAMESPACE
-void qt_real_to_fraction(qreal value, int *numerator, int *denominator)
+Fraction qRealToFraction(qreal value)
{
- if (!numerator || !denominator)
- return;
+ int integral = int(floor(value));
+ value -= qreal(integral);
+ if (value == 0.)
+ return {integral, 1};
const int dMax = 1000;
int n1 = 0, d1 = 1, n2 = 1, d2 = 1;
@@ -53,19 +23,7 @@ void qt_real_to_fraction(qreal value, int *numerator, int *denominator)
mid = qreal(n1 + n2) / (d1 + d2);
if (qAbs(value - mid) < 0.000001) {
- if (d1 + d2 <= dMax) {
- *numerator = n1 + n2;
- *denominator = d1 + d2;
- return;
- } else if (d2 > d1) {
- *numerator = n2;
- *denominator = d2;
- return;
- } else {
- *numerator = n1;
- *denominator = d1;
- return;
- }
+ break;
} else if (value > mid) {
n1 = n1 + n2;
d1 = d1 + d2;
@@ -75,13 +33,68 @@ void qt_real_to_fraction(qreal value, int *numerator, int *denominator)
}
}
- if (d1 > dMax) {
- *numerator = n2;
- *denominator = d2;
- } else {
- *numerator = n1;
- *denominator = d1;
- }
+ if (d1 + d2 <= dMax)
+ return {n1 + n2 + integral * (d1 + d2), d1 + d2};
+ else if (d2 < d1)
+ return { n2 + integral * d2, d2 };
+ else
+ return { n1 + integral * d1, d1 };
+}
+
+QSize qCalculateFrameSize(QSize resolution, Fraction par)
+{
+ if (par.numerator == par.denominator || par.numerator < 1 || par.denominator < 1)
+ return resolution;
+
+ if (par.numerator > par.denominator)
+ return { resolution.width() * par.numerator / par.denominator, resolution.height() };
+
+ return { resolution.width(), resolution.height() * par.denominator / par.numerator };
+}
+
+QSize qRotatedFrameSize(QSize size, int rotation)
+{
+ Q_ASSERT(rotation % 90 == 0);
+ return rotation % 180 ? size.transposed() : size;
+}
+
+QSize qRotatedFrameSize(const QVideoFrame &frame)
+{
+ return qRotatedFrameSize(frame.size(), frame.rotation());
+}
+
+QUrl qMediaFromUserInput(QUrl url)
+{
+ using namespace Qt::Literals;
+ if (url.scheme().isEmpty() || url.scheme() == "file"_L1)
+ url = QUrl::fromUserInput(url.toString(), QDir::currentPath(), QUrl::AssumeLocalFile);
+ return url;
+}
+
+bool qIsAutoHdrEnabled()
+{
+ static const bool autoHdrEnabled = qEnvironmentVariableIntValue("QT_MEDIA_AUTO_HDR");
+
+ return autoHdrEnabled;
+}
+
+QRhiSwapChain::Format qGetRequiredSwapChainFormat(const QVideoFrameFormat &format)
+{
+ constexpr auto sdrMaxLuminance = 100.0f;
+ const auto formatMaxLuminance = format.maxLuminance();
+
+ return formatMaxLuminance > sdrMaxLuminance ? QRhiSwapChain::HDRExtendedSrgbLinear
+ : QRhiSwapChain::SDR;
+}
+
+bool qShouldUpdateSwapChainFormat(QRhiSwapChain *swapChain,
+ QRhiSwapChain::Format requiredSwapChainFormat)
+{
+ if (!swapChain)
+ return false;
+
+ return qIsAutoHdrEnabled() && swapChain->format() != requiredSwapChainFormat
+ && swapChain->isFormatSupported(requiredSwapChainFormat);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/qmultimediautils_p.h b/src/multimedia/qmultimediautils_p.h
index 3b1def5d6..a5d60e066 100644
--- a/src/multimedia/qmultimediautils_p.h
+++ b/src/multimedia/qmultimediautils_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMULTIMEDIAUTILS_P_H
#define QMULTIMEDIAUTILS_P_H
@@ -52,11 +16,48 @@
//
#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtCore/private/qglobal_p.h>
+#include <QtMultimedia/qtvideo.h>
+#include <QtMultimedia/private/qmaybe_p.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qurl.h>
+#include <QtGui/rhi/qrhi.h>
QT_BEGIN_NAMESPACE
-Q_MULTIMEDIA_EXPORT void qt_real_to_fraction(qreal value, int *numerator, int *denominator);
+class QRhiSwapChain;
+class QVideoFrame;
+class QVideoFrameFormat;
+
+struct Fraction {
+ int numerator;
+ int denominator;
+};
+
+Q_MULTIMEDIA_EXPORT Fraction qRealToFraction(qreal value);
+
+Q_MULTIMEDIA_EXPORT QSize qCalculateFrameSize(QSize resolution, Fraction pixelAspectRatio);
+
+// TODO: after adding pixel aspect ratio to QVideoFrameFormat, the function should
+// consider PAR as well as rotation
+Q_MULTIMEDIA_EXPORT QSize qRotatedFrameSize(QSize size, int rotation);
+
+inline QSize qRotatedFrameSize(QSize size, QtVideo::Rotation rotation)
+{
+ return qRotatedFrameSize(size, qToUnderlying(rotation));
+}
+
+Q_MULTIMEDIA_EXPORT QSize qRotatedFrameSize(const QVideoFrame &frame);
+
+Q_MULTIMEDIA_EXPORT QUrl qMediaFromUserInput(QUrl fileName);
+
+Q_MULTIMEDIA_EXPORT bool qIsAutoHdrEnabled();
+
+Q_MULTIMEDIA_EXPORT QRhiSwapChain::Format
+qGetRequiredSwapChainFormat(const QVideoFrameFormat &format);
+
+Q_MULTIMEDIA_EXPORT bool
+qShouldUpdateSwapChainFormat(QRhiSwapChain *swapChain,
+ QRhiSwapChain::Format requiredSwapChainFormat);
QT_END_NAMESPACE
diff --git a/src/multimedia/qnx/qqnxaudiodevice.cpp b/src/multimedia/qnx/qqnxaudiodevice.cpp
index 65574de1f..4e1390e8b 100644
--- a/src/multimedia/qnx/qqnxaudiodevice.cpp
+++ b/src/multimedia/qnx/qqnxaudiodevice.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxaudiodevice_p.h"
diff --git a/src/multimedia/qnx/qqnxaudiodevice_p.h b/src/multimedia/qnx/qqnxaudiodevice_p.h
index ed1afa720..52c89c14f 100644
--- a/src/multimedia/qnx/qqnxaudiodevice_p.h
+++ b/src/multimedia/qnx/qqnxaudiodevice_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNXAUDIODEVICE_P_H
#define QNXAUDIODEVICE_P_H
diff --git a/src/multimedia/qnx/qqnxaudiosink.cpp b/src/multimedia/qnx/qqnxaudiosink.cpp
index 9d846b774..fa4b97ab6 100644
--- a/src/multimedia/qnx/qqnxaudiosink.cpp
+++ b/src/multimedia/qnx/qqnxaudiosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxaudiosink_p.h"
@@ -50,11 +14,14 @@
QT_BEGIN_NAMESPACE
-QQnxAudioSink::QQnxAudioSink(const QAudioDevice &deviceInfo)
- : m_source(0)
+QQnxAudioSink::QQnxAudioSink(const QAudioDevice &deviceInfo, QObject *parent)
+ : QPlatformAudioSink(parent)
+ , m_source(0)
, m_pushSource(false)
+ , m_timer(new QTimer(this))
, m_error(QAudio::NoError)
, m_state(QAudio::StoppedState)
+ , m_suspendedInState(QAudio::SuspendedState)
, m_volume(1.0)
, m_periodSize(0)
, m_bytesWritten(0)
@@ -62,9 +29,9 @@ QQnxAudioSink::QQnxAudioSink(const QAudioDevice &deviceInfo)
, m_deviceInfo(deviceInfo)
, m_pcmNotifier(0)
{
- m_timer.setSingleShot(false);
- m_timer.setInterval(20);
- connect(&m_timer, &QTimer::timeout, this, &QQnxAudioSink::pullData);
+ m_timer->setSingleShot(false);
+ m_timer->setInterval(20);
+ connect(m_timer, &QTimer::timeout, this, &QQnxAudioSink::pullData);
const std::optional<snd_pcm_channel_info_t> info = QnxAudioUtils::pcmChannelInfo(
m_deviceInfo.id(), QAudioDevice::Output);
@@ -88,7 +55,7 @@ void QQnxAudioSink::start(QIODevice *source)
if (open()) {
changeState(QAudio::ActiveState, QAudio::NoError);
- m_timer.start();
+ m_timer->start();
} else {
changeState(QAudio::StoppedState, QAudio::OpenError);
}
@@ -344,7 +311,7 @@ bool QQnxAudioSink::open()
void QQnxAudioSink::close()
{
if (!m_pushSource)
- m_timer.stop();
+ m_timer->stop();
destroyPcmNotifiers();
@@ -448,18 +415,17 @@ qint64 QQnxAudioSink::write(const char *data, qint64 len)
void QQnxAudioSink::suspendInternal(QAudio::State suspendState)
{
if (!m_pushSource)
- m_timer.stop();
+ m_timer->stop();
+ m_suspendedInState = m_state;
changeState(suspendState, QAudio::NoError);
}
void QQnxAudioSink::resumeInternal()
{
- const QAudio::State state = m_pushSource ? QAudio::IdleState : QAudio::ActiveState;
+ changeState(m_suspendedInState, QAudio::NoError);
- changeState(state, QAudio::NoError);
-
- m_timer.start();
+ m_timer->start();
}
QAudio::State suspendState(const snd_pcm_event_t &event)
diff --git a/src/multimedia/qnx/qqnxaudiosink_p.h b/src/multimedia/qnx/qqnxaudiosink_p.h
index fb3616635..1275121b3 100644
--- a/src/multimedia/qnx/qqnxaudiosink_p.h
+++ b/src/multimedia/qnx/qqnxaudiosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNXAUDIOOUTPUT_H
#define QNXAUDIOOUTPUT_H
@@ -72,7 +36,7 @@ class QQnxAudioSink : public QPlatformAudioSink
Q_OBJECT
public:
- explicit QQnxAudioSink(const QAudioDevice &deviceInfo);
+ explicit QQnxAudioSink(const QAudioDevice &deviceInfo, QObject *parent);
~QQnxAudioSink();
void start(QIODevice *source) override;
@@ -115,10 +79,11 @@ private:
QIODevice *m_source;
bool m_pushSource;
- QTimer m_timer;
+ QTimer *m_timer;
QAudio::Error m_error;
QAudio::State m_state;
+ QAudio::State m_suspendedInState;
QAudioFormat m_format;
qreal m_volume;
int m_periodSize;
diff --git a/src/multimedia/qnx/qqnxaudiosource.cpp b/src/multimedia/qnx/qqnxaudiosource.cpp
index e46b2a6ce..becbaa0c0 100644
--- a/src/multimedia/qnx/qqnxaudiosource.cpp
+++ b/src/multimedia/qnx/qqnxaudiosource.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxaudiosource_p.h"
@@ -45,8 +9,9 @@
QT_BEGIN_NAMESPACE
-QQnxAudioSource::QQnxAudioSource(const QAudioDevice &deviceInfo)
- : m_audioSource(0)
+QQnxAudioSource::QQnxAudioSource(const QAudioDevice &deviceInfo, QObject *parent)
+ : QPlatformAudioSource(parent)
+ , m_audioSource(0)
, m_pcmNotifier(0)
, m_error(QAudio::NoError)
, m_state(QAudio::StoppedState)
@@ -287,7 +252,7 @@ bool QQnxAudioSource::open()
m_pcmNotifier = new QSocketNotifier(snd_pcm_file_descriptor(m_pcmHandle.get(), SND_PCM_CHANNEL_CAPTURE),
QSocketNotifier::Read, this);
- connect(m_pcmNotifier, SIGNAL(activated(QSocketDescriptor)), SLOT(userFeed()));
+ connect(m_pcmNotifier, SIGNAL(activated(QSocketDescriptor)), this, SLOT(userFeed()));
return true;
}
diff --git a/src/multimedia/qnx/qqnxaudiosource_p.h b/src/multimedia/qnx/qqnxaudiosource_p.h
index 3c93574d4..6b9cf61ac 100644
--- a/src/multimedia/qnx/qqnxaudiosource_p.h
+++ b/src/multimedia/qnx/qqnxaudiosource_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNXAUDIOINPUT_H
#define QNXAUDIOINPUT_H
@@ -69,7 +33,7 @@ class QQnxAudioSource : public QPlatformAudioSource
Q_OBJECT
public:
- explicit QQnxAudioSource(const QAudioDevice &deviceInfo);
+ explicit QQnxAudioSource(const QAudioDevice &deviceInfo, QObject *parent);
~QQnxAudioSource();
void start(QIODevice*) override;
diff --git a/src/multimedia/qnx/qqnxaudioutils.cpp b/src/multimedia/qnx/qqnxaudioutils.cpp
index 83f67f5d4..ddcb4f0de 100644
--- a/src/multimedia/qnx/qqnxaudioutils.cpp
+++ b/src/multimedia/qnx/qqnxaudioutils.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxaudioutils_p.h"
diff --git a/src/multimedia/qnx/qqnxaudioutils_p.h b/src/multimedia/qnx/qqnxaudioutils_p.h
index 5eabf630d..c0d2e5b1c 100644
--- a/src/multimedia/qnx/qqnxaudioutils_p.h
+++ b/src/multimedia/qnx/qqnxaudioutils_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNXAUDIOUTILS_H
#define QNXAUDIOUTILS_H
diff --git a/src/multimedia/qnx/qqnxmediadevices.cpp b/src/multimedia/qnx/qqnxmediadevices.cpp
index df9ef8e0b..d9e33fcdc 100644
--- a/src/multimedia/qnx/qqnxmediadevices.cpp
+++ b/src/multimedia/qnx/qqnxmediadevices.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxmediadevices_p.h"
#include "qmediadevices.h"
@@ -91,14 +55,16 @@ QList<QAudioDevice> QQnxMediaDevices::audioOutputs() const
return ::enumeratePcmDevices(QAudioDevice::Output);
}
-QPlatformAudioSource *QQnxMediaDevices::createAudioSource(const QAudioDevice &deviceInfo)
+QPlatformAudioSource *QQnxMediaDevices::createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QQnxAudioSource(deviceInfo);
+ return new QQnxAudioSource(deviceInfo, parent);
}
-QPlatformAudioSink *QQnxMediaDevices::createAudioSink(const QAudioDevice &deviceInfo)
+QPlatformAudioSink *QQnxMediaDevices::createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QQnxAudioSink(deviceInfo);
+ return new QQnxAudioSink(deviceInfo, parent);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/qnx/qqnxmediadevices_p.h b/src/multimedia/qnx/qqnxmediadevices_p.h
index 68cf9607b..b8ccf5807 100644
--- a/src/multimedia/qnx/qqnxmediadevices_p.h
+++ b/src/multimedia/qnx/qqnxmediadevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXMEDIADEVICES_H
#define QQNXMEDIADEVICES_H
@@ -64,8 +28,10 @@ public:
QList<QAudioDevice> audioInputs() const override;
QList<QAudioDevice> audioOutputs() const override;
- QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override;
- QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override;
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/qt_cmdline.cmake b/src/multimedia/qt_cmdline.cmake
index 4c5710464..5aabbbcb7 100644
--- a/src/multimedia/qt_cmdline.cmake
+++ b/src/multimedia/qt_cmdline.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_commandline_option(alsa TYPE boolean)
qt_commandline_option(evr TYPE boolean)
qt_commandline_option(wmf TYPE boolean)
diff --git a/src/multimedia/qtmultimediaglobal.h b/src/multimedia/qtmultimediaglobal.h
index f4007605c..6b6200add 100644
--- a/src/multimedia/qtmultimediaglobal.h
+++ b/src/multimedia/qtmultimediaglobal.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTMULTIMEDIAGLOBAL_H
#define QTMULTIMEDIAGLOBAL_H
diff --git a/src/multimedia/qtmultimediaglobal_p.h b/src/multimedia/qtmultimediaglobal_p.h
index 84361910e..e8ff737a7 100644
--- a/src/multimedia/qtmultimediaglobal_p.h
+++ b/src/multimedia/qtmultimediaglobal_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimedia module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTMULTIMEDIAGLOBAL_P_H
#define QTMULTIMEDIAGLOBAL_P_H
diff --git a/src/multimedia/recording/qcapturablewindow.cpp b/src/multimedia/recording/qcapturablewindow.cpp
new file mode 100644
index 000000000..34b6a1f5d
--- /dev/null
+++ b/src/multimedia/recording/qcapturablewindow.cpp
@@ -0,0 +1,156 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qcapturablewindow.h"
+#include "qcapturablewindow_p.h"
+#include "qplatformmediaintegration_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QCapturableWindowPrivate)
+
+/*!
+ \class QCapturableWindow
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_video
+ \since 6.6
+
+ \brief Used for getting the basic information of a capturable window.
+
+ The class contains a set of window information, except the method
+ QCapturableWindow::isValid which pulls the current state
+ whenever it's called.
+
+ \sa QWindowCapture
+*/
+/*!
+ \qmlvaluetype CapturableWindow
+ \instantiates QCapturableWindow
+ \brief The CapturableWindow type is used getting basic
+ of a window that is available for capturing via WindowCapture.
+
+ \inqmlmodule QtMultimedia
+ \ingroup multimedia_qml
+ \ingroup multimedia_video_qml
+ \since 6.6
+
+ The class contains a dump of window information, except the property
+ 'isValid' which pulls the actual window state every time.
+
+ \sa WindowCapture
+*/
+
+/*!
+ \fn QCapturableWindow::QCapturableWindow(QCapturableWindow &&other)
+
+ Constructs a QCapturableWindow by moving from \a other.
+*/
+
+/*!
+ \fn void QCapturableWindow::swap(QCapturableWindow &other) noexcept
+
+ Swaps the current window information with \a other.
+*/
+
+/*!
+ \fn QCapturableWindow &QCapturableWindow::operator=(QCapturableWindow &&other)
+
+ Moves \a other into this QCapturableWindow.
+*/
+
+/*!
+ Constructs a null capturable window information that doesn't refer to any window.
+*/
+QCapturableWindow::QCapturableWindow() = default;
+
+/*!
+ Destroys the window information.
+ */
+QCapturableWindow::~QCapturableWindow() = default;
+
+/*!
+ Construct a new window information using \a other QCapturableWindow.
+*/
+QCapturableWindow::QCapturableWindow(const QCapturableWindow &other) = default;
+
+/*!
+ Assigns the \a other window information to this QCapturableWindow.
+*/
+QCapturableWindow& QCapturableWindow::operator=(const QCapturableWindow &other) = default;
+
+/*!
+ \fn bool QCapturableWindow::operator==(const QCapturableWindow &lhs, const QCapturableWindow &rhs)
+
+ Returns \c true if window information \a lhs and \a rhs refer to the same window,
+ otherwise returns \c false.
+*/
+
+/*!
+ \fn bool QCapturableWindow::operator!=(const QCapturableWindow &lhs, const QCapturableWindow &rhs)
+
+ Returns \c true if window information \a lhs and \a rhs refer to different windows,
+ otherwise returns \c false.
+*/
+bool operator==(const QCapturableWindow &lhs, const QCapturableWindow &rhs) noexcept
+{
+ return lhs.d == rhs.d || (lhs.d && rhs.d && lhs.d->id == rhs.d->id);
+}
+
+/*!
+ \qmlproperty string QtMultimedia::CapturableWindow::isValid
+
+ This property identifies whether a window information is valid.
+
+ An invalid window information refers to non-existing window or doesn't refer to any one.
+*/
+
+/*!
+ Identifies whether a window information is valid.
+
+ An invalid window information refers to non-existing window or doesn't refer to any one.
+
+ Returns true if the window is valid, and false if it is not.
+*/
+bool QCapturableWindow::isValid() const
+{
+ return d && QPlatformMediaIntegration::instance()->isCapturableWindowValid(*d);
+}
+
+/*!
+ \qmlproperty string QtMultimedia::CapturableWindow::description
+
+ This property holds the description of the reffered window.
+*/
+
+/*!
+ Returns a description of the window. In most cases it represents the window title.
+*/
+QString QCapturableWindow::description() const
+{
+ if (!d)
+ return {};
+
+ if (d->description.isEmpty() && d->id)
+ return QLatin1String("Window 0x") + QString::number(d->id, 16);
+
+ return d->description;
+}
+
+QCapturableWindow::QCapturableWindow(QCapturableWindowPrivate *capturablePrivate)
+ : d(capturablePrivate)
+{
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QCapturableWindow &window)
+{
+ dbg << QStringLiteral("Capturable window '%1'").arg(window.description());
+ return dbg;
+}
+#endif
+
+
+QT_END_NAMESPACE
+
+#include "moc_qcapturablewindow.cpp"
diff --git a/src/multimedia/recording/qcapturablewindow.h b/src/multimedia/recording/qcapturablewindow.h
new file mode 100644
index 000000000..f18dbf72d
--- /dev/null
+++ b/src/multimedia/recording/qcapturablewindow.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCAPTURABLEWINDOW_H
+#define QCAPTURABLEWINDOW_H
+
+#include <QtMultimedia/qtmultimediaglobal.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCapturableWindowPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QCapturableWindowPrivate, Q_MULTIMEDIA_EXPORT)
+
+class QMediaCaptureSession;
+class QWindowCapturePrivate;
+
+class QCapturableWindow
+{
+ Q_GADGET_EXPORT(Q_MULTIMEDIA_EXPORT)
+ Q_PROPERTY(QString description READ description CONSTANT)
+ Q_PROPERTY(bool isValid READ isValid CONSTANT)
+public:
+ Q_MULTIMEDIA_EXPORT QCapturableWindow();
+
+ Q_MULTIMEDIA_EXPORT ~QCapturableWindow();
+
+ Q_MULTIMEDIA_EXPORT QCapturableWindow(const QCapturableWindow &other);
+
+ QCapturableWindow(QCapturableWindow &&other) noexcept = default;
+
+ Q_MULTIMEDIA_EXPORT QCapturableWindow& operator=(const QCapturableWindow &other);
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QCapturableWindow);
+
+ void swap(QCapturableWindow &other) noexcept
+ { d.swap(other.d); }
+
+ Q_MULTIMEDIA_EXPORT friend bool operator==(const QCapturableWindow &lhs, const QCapturableWindow &rhs) noexcept;
+
+ friend bool operator!=(const QCapturableWindow &lhs, const QCapturableWindow &rhs) noexcept
+ { return !(lhs == rhs); }
+
+ Q_MULTIMEDIA_EXPORT bool isValid() const;
+
+ Q_MULTIMEDIA_EXPORT QString description() const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ Q_MULTIMEDIA_EXPORT friend QDebug operator<<(QDebug, const QCapturableWindow &);
+#endif
+
+private:
+ Q_MULTIMEDIA_EXPORT QCapturableWindow(QCapturableWindowPrivate *capturablePrivate);
+ friend class QCapturableWindowPrivate;
+
+ QExplicitlySharedDataPointer<QCapturableWindowPrivate> d;
+};
+
+Q_DECLARE_SHARED(QCapturableWindow)
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QCapturableWindow)
+
+#endif // QCAPTURABLEWINDOW_H
diff --git a/src/multimedia/recording/qcapturablewindow_p.h b/src/multimedia/recording/qcapturablewindow_p.h
new file mode 100644
index 000000000..9cb186a77
--- /dev/null
+++ b/src/multimedia/recording/qcapturablewindow_p.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCAPTURABLEWINDOW_P_H
+#define QCAPTURABLEWINDOW_P_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/QSharedData>
+#include <QtMultimedia/qcapturablewindow.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QCapturableWindowPrivate : public QSharedData {
+public:
+ using Id = size_t;
+
+ QString description;
+ Id id = 0;
+
+ static const QCapturableWindowPrivate *handle(const QCapturableWindow &window)
+ {
+ return window.d.get();
+ }
+
+ QCapturableWindow create() { return QCapturableWindow(this); }
+};
+
+QT_END_NAMESPACE
+
+#endif // QCAPTURABLEWINDOW_P_H
diff --git a/src/multimedia/recording/qmediacapturesession.cpp b/src/multimedia/recording/qmediacapturesession.cpp
index 9860ea65b..f175cd98e 100644
--- a/src/multimedia/recording/qmediacapturesession.cpp
+++ b/src/multimedia/recording/qmediacapturesession.cpp
@@ -1,50 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmediacapturesession.h"
+#include "qmediacapturesession_p.h"
#include "qaudiodevice.h"
#include "qcamera.h"
#include "qmediarecorder.h"
#include "qimagecapture.h"
#include "qvideosink.h"
-
-#include <qpointer.h>
+#include "qscreencapture.h"
+#include "qwindowcapture.h"
#include "qplatformmediaintegration_p.h"
#include "qplatformmediacapture_p.h"
@@ -53,33 +18,19 @@
QT_BEGIN_NAMESPACE
-class QMediaCaptureSessionPrivate
+void QMediaCaptureSessionPrivate::setVideoSink(QVideoSink *sink)
{
-public:
- QMediaCaptureSession *q = nullptr;
- QPlatformMediaCaptureSession *captureSession;
- QAudioInput *audioInput = nullptr;
- QAudioOutput *audioOutput = nullptr;
- QCamera *camera = nullptr;
- QImageCapture *imageCapture = nullptr;
- QMediaRecorder *recorder = nullptr;
- QVideoSink *videoSink = nullptr;
- QPointer<QObject> videoOutput;
-
- void setVideoSink(QVideoSink *sink)
- {
- if (sink == videoSink)
- return;
- if (videoSink)
- videoSink->setSource(nullptr);
- videoSink = sink;
- if (sink)
- sink->setSource(q);
+ if (sink == videoSink)
+ return;
+ if (videoSink)
+ videoSink->setSource(nullptr);
+ videoSink = sink;
+ if (sink)
+ sink->setSource(q);
+ if (captureSession)
captureSession->setVideoPreview(sink);
- emit q->videoOutputChanged();
- }
-
-};
+ emit q->videoOutputChanged();
+}
/*!
\class QMediaCaptureSession
@@ -92,14 +43,16 @@ public:
The QMediaCaptureSession is the central class that manages capturing of media on the local device.
- You can connect a camera and a microphone to QMediaCaptureSession using setCamera() and setAudioInput().
- A preview of the captured media can be seen by setting a QVideoSink of QVideoWidget using setVideoOutput()
- and heard by routing the audio to an output device using setAudioOutput().
+ You can connect a video input to QMediaCaptureSession using setCamera(), setScreenCapture() or setWindowCapture().
+ A preview of the captured media can be seen by setting a QVideoWidget or QGraphicsVideoItem using setVideoOutput().
+
+ You can connect a microphone to QMediaCaptureSession using setAudioInput().
+ The captured sound can be heard by routing the audio to an output device using setAudioOutput().
You can capture still images from a camera by setting a QImageCapture object on the capture session,
and record audio/video using a QMediaRecorder.
- \sa QCamera, QAudioDevice, QMediaRecorder, QImageCapture, QMediaRecorder
+ \sa QCamera, QAudioDevice, QMediaRecorder, QImageCapture, QScreenCapture, QWindowCapture, QMediaRecorder, QGraphicsVideoItem
*/
/*!
@@ -118,6 +71,12 @@ public:
Connect a camera and a microphone to a CaptureSession by assigning Camera
and AudioInput objects to the relevant properties.
+ Capture a screen by connecting a ScreenCapture object to
+ the screenCapture property.
+
+ Capture a window by connecting a WindowCapture object to
+ the windowCapture property.
+
Enable a preview of the captured media by assigning a VideoOutput element to
the videoOutput property.
@@ -146,7 +105,7 @@ public:
}
\endqml
- \sa Camera, MediaDevices, MediaRecorder, ImageCapture, AudioInput, VideoOutput
+ \sa Camera, MediaDevices, MediaRecorder, ImageCapture, ScreenCapture, WindowCapture, AudioInput, VideoOutput
*/
/*!
@@ -157,9 +116,13 @@ QMediaCaptureSession::QMediaCaptureSession(QObject *parent)
d_ptr(new QMediaCaptureSessionPrivate)
{
d_ptr->q = this;
- d_ptr->captureSession = QPlatformMediaIntegration::instance()->createCaptureSession();
- d_ptr->captureSession->setCaptureSession(this);
- Q_ASSERT(d_ptr->captureSession);
+ auto maybeCaptureSession = QPlatformMediaIntegration::instance()->createCaptureSession();
+ if (maybeCaptureSession) {
+ d_ptr->captureSession = maybeCaptureSession.value();
+ d_ptr->captureSession->setCaptureSession(this);
+ } else {
+ qWarning() << "Failed to initialize QMediaCaptureSession" << maybeCaptureSession.error();
+ }
}
/*!
@@ -170,6 +133,8 @@ QMediaCaptureSession::~QMediaCaptureSession()
setCamera(nullptr);
setRecorder(nullptr);
setImageCapture(nullptr);
+ setScreenCapture(nullptr);
+ setWindowCapture(nullptr);
setAudioInput(nullptr);
setAudioOutput(nullptr);
d_ptr->setVideoSink(nullptr);
@@ -183,6 +148,8 @@ QMediaCaptureSession::~QMediaCaptureSession()
*/
/*!
+ \property QMediaCaptureSession::audioInput
+
Returns the device that is being used to capture audio.
*/
QAudioInput *QMediaCaptureSession::audioInput() const
@@ -200,14 +167,21 @@ void QMediaCaptureSession::setAudioInput(QAudioInput *input)
QAudioInput *oldInput = d_ptr->audioInput;
if (oldInput == input)
return;
- d_ptr->audioInput = input;
- d_ptr->captureSession->setAudioInput(nullptr);
+
+ // To avoid double emit of audioInputChanged
+ // from recursive setAudioInput(nullptr) call.
+ d_ptr->audioInput = nullptr;
+
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setAudioInput(nullptr);
if (oldInput)
oldInput->setDisconnectFunction({});
if (input) {
input->setDisconnectFunction([this](){ setAudioInput(nullptr); });
- d_ptr->captureSession->setAudioInput(input->handle());
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setAudioInput(input->handle());
}
+ d_ptr->audioInput = input;
emit audioInputChanged();
}
@@ -226,7 +200,7 @@ void QMediaCaptureSession::setAudioInput(QAudioInput *input)
\brief The camera used to capture video.
Record video or take images by adding a camera to the capture session
- using this property,
+ using this property.
*/
QCamera *QMediaCaptureSession::camera() const
{
@@ -235,11 +209,13 @@ QCamera *QMediaCaptureSession::camera() const
void QMediaCaptureSession::setCamera(QCamera *camera)
{
+ // TODO: come up with an unification of the captures setup
QCamera *oldCamera = d_ptr->camera;
if (oldCamera == camera)
return;
d_ptr->camera = camera;
- d_ptr->captureSession->setCamera(nullptr);
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setCamera(nullptr);
if (oldCamera) {
if (oldCamera->captureSession() && oldCamera->captureSession() != this)
oldCamera->captureSession()->setCamera(nullptr);
@@ -248,11 +224,108 @@ void QMediaCaptureSession::setCamera(QCamera *camera)
if (camera) {
if (camera->captureSession())
camera->captureSession()->setCamera(nullptr);
- d_ptr->captureSession->setCamera(camera->platformCamera());
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setCamera(camera->platformCamera());
camera->setCaptureSession(this);
}
emit cameraChanged();
}
+
+/*!
+ \qmlproperty ScreenCapture QtMultimedia::CaptureSession::screenCapture
+ \since 6.5
+
+ \brief The object used to capture a screen.
+
+ Record a screen by adding a screen capture objet
+ to the capture session using this property.
+*/
+
+/*!
+ \property QMediaCaptureSession::screenCapture
+ \since 6.5
+
+ \brief The object used to capture a screen.
+
+ Record a screen by adding a screen capture object
+ to the capture session using this property.
+*/
+QScreenCapture *QMediaCaptureSession::screenCapture()
+{
+ return d_ptr ? d_ptr->screenCapture : nullptr;
+}
+
+void QMediaCaptureSession::setScreenCapture(QScreenCapture *screenCapture)
+{
+ // TODO: come up with an unification of the captures setup
+ QScreenCapture *oldScreenCapture = d_ptr->screenCapture;
+ if (oldScreenCapture == screenCapture)
+ return;
+ d_ptr->screenCapture = screenCapture;
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setScreenCapture(nullptr);
+ if (oldScreenCapture) {
+ if (oldScreenCapture->captureSession() && oldScreenCapture->captureSession() != this)
+ oldScreenCapture->captureSession()->setScreenCapture(nullptr);
+ oldScreenCapture->setCaptureSession(nullptr);
+ }
+ if (screenCapture) {
+ if (screenCapture->captureSession())
+ screenCapture->captureSession()->setScreenCapture(nullptr);
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setScreenCapture(screenCapture->platformScreenCapture());
+ screenCapture->setCaptureSession(this);
+ }
+ emit screenCaptureChanged();
+}
+
+/*!
+ \qmlproperty WindowCapture QtMultimedia::CaptureSession::windowCapture
+ \since 6.6
+
+ \brief The object used to capture a window.
+
+ Record a window by adding a window capture object
+ to the capture session using this property.
+*/
+
+/*!
+ \property QMediaCaptureSession::windowCapture
+ \since 6.6
+
+ \brief The object used to capture a window.
+
+ Record a window by adding a window capture objet
+ to the capture session using this property.
+*/
+QWindowCapture *QMediaCaptureSession::windowCapture() {
+ return d_ptr ? d_ptr->windowCapture : nullptr;
+}
+
+void QMediaCaptureSession::setWindowCapture(QWindowCapture *windowCapture)
+{
+ // TODO: come up with an unification of the captures setup
+ QWindowCapture *oldCapture = d_ptr->windowCapture;
+ if (oldCapture == windowCapture)
+ return;
+ d_ptr->windowCapture = windowCapture;
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setWindowCapture(nullptr);
+ if (oldCapture) {
+ if (oldCapture->captureSession() && oldCapture->captureSession() != this)
+ oldCapture->captureSession()->setWindowCapture(nullptr);
+ oldCapture->setCaptureSession(nullptr);
+ }
+ if (windowCapture) {
+ if (windowCapture->captureSession())
+ windowCapture->captureSession()->setWindowCapture(nullptr);
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setWindowCapture(windowCapture->platformWindowCapture());
+ windowCapture->setCaptureSession(this);
+ }
+ emit windowCaptureChanged();
+}
+
/*!
\qmlproperty ImageCapture QtMultimedia::CaptureSession::imageCapture
@@ -276,11 +349,13 @@ QImageCapture *QMediaCaptureSession::imageCapture()
void QMediaCaptureSession::setImageCapture(QImageCapture *imageCapture)
{
+ // TODO: come up with an unification of the captures setup
QImageCapture *oldImageCapture = d_ptr->imageCapture;
if (oldImageCapture == imageCapture)
return;
d_ptr->imageCapture = imageCapture;
- d_ptr->captureSession->setImageCapture(nullptr);
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setImageCapture(nullptr);
if (oldImageCapture) {
if (oldImageCapture->captureSession() && oldImageCapture->captureSession() != this)
oldImageCapture->captureSession()->setImageCapture(nullptr);
@@ -289,7 +364,8 @@ void QMediaCaptureSession::setImageCapture(QImageCapture *imageCapture)
if (imageCapture) {
if (imageCapture->captureSession())
imageCapture->captureSession()->setImageCapture(nullptr);
- d_ptr->captureSession->setImageCapture(imageCapture->platformImageCapture());
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setImageCapture(imageCapture->platformImageCapture());
imageCapture->setCaptureSession(this);
}
emit imageCaptureChanged();
@@ -322,7 +398,8 @@ void QMediaCaptureSession::setRecorder(QMediaRecorder *recorder)
if (oldRecorder == recorder)
return;
d_ptr->recorder = recorder;
- d_ptr->captureSession->setMediaRecorder(nullptr);
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setMediaRecorder(nullptr);
if (oldRecorder) {
if (oldRecorder->captureSession() && oldRecorder->captureSession() != this)
oldRecorder->captureSession()->setRecorder(nullptr);
@@ -331,7 +408,8 @@ void QMediaCaptureSession::setRecorder(QMediaRecorder *recorder)
if (recorder) {
if (recorder->captureSession())
recorder->captureSession()->setRecorder(nullptr);
- d_ptr->captureSession->setMediaRecorder(recorder->platformRecoder());
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setMediaRecorder(recorder->platformRecoder());
recorder->setCaptureSession(this);
}
emit recorderChanged();
@@ -347,6 +425,11 @@ void QMediaCaptureSession::setRecorder(QMediaRecorder *recorder)
The previously set preview is detached.
*/
+/*!
+ \property QMediaCaptureSession::videoOutput
+
+ Returns the video output for the session.
+*/
QObject *QMediaCaptureSession::videoOutput() const
{
Q_D(const QMediaCaptureSession);
@@ -388,6 +471,10 @@ void QMediaCaptureSession::setVideoSink(QVideoSink *sink)
d->videoOutput = nullptr;
d->setVideoSink(sink);
}
+
+/*!
+ Returns the QVideoSink for the session.
+*/
QVideoSink *QMediaCaptureSession::videoSink() const
{
Q_D(const QMediaCaptureSession);
@@ -395,25 +482,43 @@ QVideoSink *QMediaCaptureSession::videoSink() const
}
/*!
Sets the audio output device to \a{output}.
+
+ Setting an audio output device enables audio routing from an audio input device.
*/
void QMediaCaptureSession::setAudioOutput(QAudioOutput *output)
{
QAudioOutput *oldOutput = d_ptr->audioOutput;
if (oldOutput == output)
return;
- d_ptr->audioOutput = output;
- d_ptr->captureSession->setAudioOutput(nullptr);
+
+ // We don't want to end up with signal emitted
+ // twice (from recursive call setAudioInput(nullptr)
+ // from oldOutput->setDisconnectFunction():
+ d_ptr->audioOutput = nullptr;
+
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setAudioOutput(nullptr);
if (oldOutput)
oldOutput->setDisconnectFunction({});
if (output) {
output->setDisconnectFunction([this](){ setAudioOutput(nullptr); });
- d_ptr->captureSession->setAudioOutput(output->handle());
+ if (d_ptr->captureSession)
+ d_ptr->captureSession->setAudioOutput(output->handle());
}
+ d_ptr->audioOutput = output;
emit audioOutputChanged();
}
/*!
\qmlproperty AudioOutput QtMultimedia::CaptureSession::audioOutput
\brief The audio output device for the capture session.
+
+ Add an AudioOutput device to the capture session to enable
+ audio routing from an AudioInput device.
+*/
+/*!
+ \property QMediaCaptureSession::audioOutput
+
+ Returns the audio output for the session.
*/
QAudioOutput *QMediaCaptureSession::audioOutput() const
{
diff --git a/src/multimedia/recording/qmediacapturesession.h b/src/multimedia/recording/qmediacapturesession.h
index 77a16395d..1333af7eb 100644
--- a/src/multimedia/recording/qmediacapturesession.h
+++ b/src/multimedia/recording/qmediacapturesession.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEDIACAPTURESESSION_H
#define QMEDIACAPTURESESSION_H
@@ -53,6 +17,8 @@ class QImageCapture;
class QMediaRecorder;
class QPlatformMediaCaptureSession;
class QVideoSink;
+class QScreenCapture;
+class QWindowCapture;
class QMediaCaptureSessionPrivate;
class Q_MULTIMEDIA_EXPORT QMediaCaptureSession : public QObject
@@ -61,6 +27,10 @@ class Q_MULTIMEDIA_EXPORT QMediaCaptureSession : public QObject
Q_PROPERTY(QAudioInput *audioInput READ audioInput WRITE setAudioInput NOTIFY audioInputChanged)
Q_PROPERTY(QAudioOutput *audioOutput READ audioOutput WRITE setAudioOutput NOTIFY audioOutputChanged)
Q_PROPERTY(QCamera *camera READ camera WRITE setCamera NOTIFY cameraChanged)
+ Q_PROPERTY(
+ QScreenCapture *screenCapture READ screenCapture WRITE setScreenCapture NOTIFY screenCaptureChanged)
+ Q_PROPERTY(
+ QWindowCapture *windowCapture READ windowCapture WRITE setWindowCapture NOTIFY windowCaptureChanged)
Q_PROPERTY(QImageCapture *imageCapture READ imageCapture WRITE setImageCapture NOTIFY imageCaptureChanged)
Q_PROPERTY(QMediaRecorder *recorder READ recorder WRITE setRecorder NOTIFY recorderChanged)
Q_PROPERTY(QObject *videoOutput READ videoOutput WRITE setVideoOutput NOTIFY videoOutputChanged)
@@ -77,6 +47,12 @@ public:
QImageCapture *imageCapture();
void setImageCapture(QImageCapture *imageCapture);
+ QScreenCapture *screenCapture();
+ void setScreenCapture(QScreenCapture *screenCapture);
+
+ QWindowCapture *windowCapture();
+ void setWindowCapture(QWindowCapture *windowCapture);
+
QMediaRecorder *recorder();
void setRecorder(QMediaRecorder *recorder);
@@ -94,12 +70,16 @@ public:
Q_SIGNALS:
void audioInputChanged();
void cameraChanged();
+ void screenCaptureChanged();
+ void windowCaptureChanged();
void imageCaptureChanged();
void recorderChanged();
void videoOutputChanged();
void audioOutputChanged();
private:
+ friend class QPlatformMediaCaptureSession;
+
QMediaCaptureSessionPrivate *d_ptr;
Q_DISABLE_COPY(QMediaCaptureSession)
Q_DECLARE_PRIVATE(QMediaCaptureSession)
diff --git a/src/multimedia/recording/qmediacapturesession_p.h b/src/multimedia/recording/qmediacapturesession_p.h
new file mode 100644
index 000000000..8702c8d2b
--- /dev/null
+++ b/src/multimedia/recording/qmediacapturesession_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMEDIACAPTURESESSION_P_H
+#define QMEDIACAPTURESESSION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/qmediacapturesession.h>
+
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMediaCaptureSessionPrivate
+{
+public:
+ QMediaCaptureSession *q = nullptr;
+ QPlatformMediaCaptureSession *captureSession = nullptr;
+ QAudioInput *audioInput = nullptr;
+ QAudioOutput *audioOutput = nullptr;
+ QPointer<QCamera> camera;
+ QPointer<QScreenCapture> screenCapture;
+ QPointer<QWindowCapture> windowCapture;
+ QPointer<QImageCapture> imageCapture;
+ QPointer<QMediaRecorder> recorder;
+ QPointer<QVideoSink> videoSink;
+ QPointer<QObject> videoOutput;
+
+ void setVideoSink(QVideoSink *sink);
+};
+
+QT_END_NAMESPACE
+
+#endif // QMEDIACAPTURESESSION_P_H
diff --git a/src/multimedia/recording/qmediarecorder.cpp b/src/multimedia/recording/qmediarecorder.cpp
index 2921d7f4f..d4237c87d 100644
--- a/src/multimedia/recording/qmediarecorder.cpp
+++ b/src/multimedia/recording/qmediarecorder.cpp
@@ -1,49 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmediarecorder_p.h"
#include <private/qplatformmediarecorder_p.h>
#include <qaudiodevice.h>
#include <qcamera.h>
+#include <qscreencapture.h>
+#include <qwindowcapture.h>
#include <qmediacapturesession.h>
#include <private/qplatformcamera_p.h>
+#include <private/qplatformsurfacecapture_p.h>
#include <private/qplatformmediaintegration_p.h>
#include <private/qplatformmediacapture_p.h>
@@ -94,6 +61,7 @@ QT_BEGIN_NAMESPACE
id: captureSession
camera: Camera {
id: camera
+ active: true
}
audioInput: AudioInput {}
recorder: MediaRecorder {
@@ -106,14 +74,14 @@ QT_BEGIN_NAMESPACE
\qml
CameraButton {
text: "Record"
- visible: recorder.status !== MediaRecorder.RecordingStatus
+ visible: recorder.recorderState !== MediaRecorder.RecordingState
onClicked: recorder.record()
}
CameraButton {
id: stopButton
text: "Stop"
- visible: recorder.status === MediaRecorder.RecordingStatus
+ visible: recorder.recorderState === MediaRecorder.RecordingState
onClicked: recorder.stop()
}
\endqml
@@ -142,8 +110,22 @@ QMediaRecorder::QMediaRecorder(QObject *parent)
d_ptr(new QMediaRecorderPrivate)
{
Q_D(QMediaRecorder);
+
+ auto &mediaIntegration = *QPlatformMediaIntegration::instance();
+
d->q_ptr = this;
- d->control = QPlatformMediaIntegration::instance()->createRecorder(this);
+ auto maybeControl = mediaIntegration.createRecorder(this);
+ if (maybeControl) {
+ // The first format info initialization may take some time,
+ // for users it seems to be more suitable to have a delay on the object construction
+ // rather than on QMediaRecorder::record
+ mediaIntegration.formatInfo();
+
+ d->control = maybeControl.value();
+ } else {
+ d->initErrorMessage = maybeControl.error();
+ qWarning() << "Failed to initialize QMediaRecorder" << maybeControl.error();
+ }
}
/*!
@@ -217,17 +199,11 @@ void QMediaRecorder::setCaptureSession(QMediaCaptureSession *session)
*/
/*!
- \qmlproperty bool QtMultimedia::MediaRecorder::isAvailable
- \brief This property holds whether the recorder service is ready to use.
-
- Returns \c true if media recorder service ready to use.
-*/
-/*!
Returns \c true if media recorder service ready to use.
*/
bool QMediaRecorder::isAvailable() const
{
- return d_func()->control != nullptr && d_func()->captureSession;
+ return d_func()->control && d_func()->captureSession;
}
QUrl QMediaRecorder::outputLocation() const
@@ -239,7 +215,7 @@ void QMediaRecorder::setOutputLocation(const QUrl &location)
{
Q_D(QMediaRecorder);
if (!d->control) {
- emit errorOccurred(QMediaRecorder::ResourceError, tr("Not available"));
+ emit errorOccurred(QMediaRecorder::ResourceError, d->initErrorMessage);
return;
}
d->control->setOutputLocation(location);
@@ -248,6 +224,35 @@ void QMediaRecorder::setOutputLocation(const QUrl &location)
emit errorOccurred(QMediaRecorder::LocationNotWritable, tr("Output location not writable"));
}
+/*!
+ Set the output IO device for media content.
+
+ The \a device must have been opened in the \l{QIODevice::WriteOnly}{WriteOnly} or
+ \l{QIODevice::ReadWrite}{ReadWrite} modes before the recording starts.
+
+ The media recorder doesn't take ownership of the specified \a device.
+ If the recording has been started, the device must be kept alive and open until
+ the signal \c recorderStateChanged(StoppedState) is emitted.
+
+ \sa outputDevice()
+*/
+void QMediaRecorder::setOutputDevice(QIODevice *device)
+{
+ Q_D(QMediaRecorder);
+ d->control->setOutputDevice(device);
+}
+
+/*!
+ Returns the output IO device for media content.
+
+ \sa setOutputDevice()
+*/
+QIODevice *QMediaRecorder::outputDevice() const
+{
+ Q_D(const QMediaRecorder);
+ return d->control->outputDevice();
+}
+
QUrl QMediaRecorder::actualLocation() const
{
Q_D(const QMediaRecorder);
@@ -266,6 +271,8 @@ QMediaRecorder::RecorderState QMediaRecorder::recorderState() const
}
/*!
+ \property QMediaRecorder::error
+
Returns the current error state.
\sa errorString()
@@ -284,6 +291,8 @@ QMediaRecorder::Error QMediaRecorder::error() const
\sa error
*/
/*!
+ \property QMediaRecorder::errorString
+
Returns a string describing the current error state.
\sa error()
@@ -293,7 +302,7 @@ QString QMediaRecorder::errorString() const
{
Q_D(const QMediaRecorder);
- return d->control ? d->control->errorString() : tr("QMediaRecorder not supported on this platform");
+ return d->control ? d->control->errorString() : d->initErrorMessage;
}
/*!
\qmlproperty qint64 QtMultimedia::MediaRecorder::duration
@@ -312,6 +321,11 @@ qint64 QMediaRecorder::duration() const
return d_func()->control ? d_func()->control->duration() : 0;
}
/*!
+ \fn void QMediaRecorder::encoderSettingsChanged()
+
+ Signals when the encoder settings change.
+*/
+/*!
\qmlmethod QtMultimedia::MediaRecorder::record()
\brief Starts recording.
@@ -349,17 +363,18 @@ void QMediaRecorder::record()
{
Q_D(QMediaRecorder);
- if (!d->control || ! d->captureSession)
+ if (!d->control || !d->captureSession)
return;
if (d->control->state() == QMediaRecorder::PausedState) {
d->control->resume();
} else {
auto oldMediaFormat = d->encoderSettings.mediaFormat();
- auto camera = d->captureSession->camera();
- auto flags = camera && camera->isActive() ? QMediaFormat::RequiresVideo
- : QMediaFormat::NoFlags;
- d->encoderSettings.resolveFormat(flags);
+
+ auto platformSession = d->captureSession->platformSession();
+ const bool hasVideo = platformSession && !platformSession->activeVideoSources().empty();
+
+ d->encoderSettings.resolveFormat(hasVideo ? QMediaFormat::RequiresVideo : QMediaFormat::NoFlags);
d->control->clearActualLocation();
d->control->clearError();
@@ -526,6 +541,8 @@ void QMediaRecorder::stop()
*/
/*!
+ \property QMediaRecorder::metaData
+
Returns the metaData associated with the recording.
*/
QMediaMetaData QMediaRecorder::metaData() const
@@ -549,6 +566,9 @@ void QMediaRecorder::setMetaData(const QMediaMetaData &metaData)
d->control->setMetaData(metaData);
}
+/*!
+ Adds \a metaData to the recorded media.
+*/
void QMediaRecorder::addMetaData(const QMediaMetaData &metaData)
{
auto data = this->metaData();
@@ -574,6 +594,9 @@ void QMediaRecorder::addMetaData(const QMediaMetaData &metaData)
once.
*/
+/*!
+ Returns the media capture session.
+*/
QMediaCaptureSession *QMediaRecorder::captureSession() const
{
Q_D(const QMediaRecorder);
@@ -622,7 +645,11 @@ QMediaCaptureSession *QMediaRecorder::captureSession() const
\brief This property holds the current MediaFormat of the recorder.
*/
+/*!
+ \property QMediaRecorder::mediaFormat
+ Returns the recording media format.
+*/
QMediaFormat QMediaRecorder::mediaFormat() const
{
Q_D(const QMediaRecorder);
@@ -639,6 +666,14 @@ void QMediaRecorder::setMediaFormat(const QMediaFormat &format)
}
/*!
+
+ \qmlproperty enumeration QtMultimedia::MediaRecorder::encodingMode
+ \since 6.6
+ \brief This property holds the encoding mode.
+ \sa QMediaRecorder::EncodingMode
+*/
+
+/*!
Returns the encoding mode.
\sa EncodingMode
@@ -650,6 +685,11 @@ QMediaRecorder::EncodingMode QMediaRecorder::encodingMode() const
}
/*!
+ \fn void QMediaRecorder::encodingModeChanged()
+
+ Signals when the encoding mode changes.
+*/
+/*!
Sets the encoding \a mode setting.
If ConstantQualityEncoding is set, the quality
@@ -667,12 +707,22 @@ void QMediaRecorder::setEncodingMode(EncodingMode mode)
emit encodingModeChanged();
}
+/*!
+ \property QMediaRecorder::quality
+
+ Returns the recording quality.
+*/
QMediaRecorder::Quality QMediaRecorder::quality() const
{
Q_D(const QMediaRecorder);
return d->encoderSettings.quality();
}
+/*!
+ \fn void QMediaRecorder::qualityChanged()
+
+ Signals when the recording quality changes.
+*/
void QMediaRecorder::setQuality(Quality quality)
{
Q_D(QMediaRecorder);
@@ -682,6 +732,15 @@ void QMediaRecorder::setQuality(Quality quality)
emit qualityChanged();
}
+/*!
+ \qmlproperty Size QtMultimedia::MediaRecorder::videoResolution
+ \since 6.6
+ \brief This property holds the resolution of the encoded video.
+
+ Set an empty Size to make the recorder choose an optimal resolution based
+ on what is available from the video source and the limitations of the codec.
+*/
+
/*!
Returns the resolution of the encoded video.
@@ -693,6 +752,11 @@ QSize QMediaRecorder::videoResolution() const
}
/*!
+ \fn void QMediaRecorder::videoResolutionChanged()
+
+ Signals when the video recording resolution changes.
+*/
+/*!
Sets the resolution of the encoded video to \a{size}.
Pass an empty QSize to make the recorder choose an optimal resolution based
@@ -715,6 +779,15 @@ void QMediaRecorder::setVideoResolution(const QSize &size)
*/
/*!
+ \qmlproperty real QtMultimedia::MediaRecorder::videoFrameRate
+ \since 6.6
+ \brief This property holds the video frame rate.
+
+ A value of 0 indicates the recorder should make an optimal choice based on what is available
+ from the video source and the limitations of the codec.
+*/
+
+/*!
Returns the video frame rate.
*/
qreal QMediaRecorder::videoFrameRate() const
@@ -724,6 +797,11 @@ qreal QMediaRecorder::videoFrameRate() const
}
/*!
+ \fn void QMediaRecorder::videoFrameRateChanged()
+
+ Signals when the recording video frame rate changes.
+*/
+/*!
Sets the video \a frameRate.
A value of 0 indicates the recorder should make an optimal choice based on what is available
@@ -739,6 +817,12 @@ void QMediaRecorder::setVideoFrameRate(qreal frameRate)
}
/*!
+ \qmlproperty int QtMultimedia::MediaRecorder::videoBitRate
+ \since 6.6
+ \brief This property holds the bit rate of the compressed video stream in bits per second.
+*/
+
+/*!
Returns the bit rate of the compressed video stream in bits per second.
*/
int QMediaRecorder::videoBitRate() const
@@ -748,6 +832,11 @@ int QMediaRecorder::videoBitRate() const
}
/*!
+ \fn void QMediaRecorder::videoBitRateChanged()
+
+ Signals when the recording video bit rate changes.
+*/
+/*!
Sets the video \a bitRate in bits per second.
*/
void QMediaRecorder::setVideoBitRate(int bitRate)
@@ -760,6 +849,12 @@ void QMediaRecorder::setVideoBitRate(int bitRate)
}
/*!
+ \qmlproperty int QtMultimedia::MediaRecorder::audioBitRate
+ \since 6.6
+ \brief This property holds the bit rate of the compressed audio stream in bits per second.
+*/
+
+/*!
Returns the bit rate of the compressed audio stream in bits per second.
*/
int QMediaRecorder::audioBitRate() const
@@ -769,6 +864,11 @@ int QMediaRecorder::audioBitRate() const
}
/*!
+ \fn void QMediaRecorder::audioBitRateChanged()
+
+ Signals when the recording audio bit rate changes.
+*/
+/*!
Sets the audio \a bitRate in bits per second.
*/
void QMediaRecorder::setAudioBitRate(int bitRate)
@@ -781,6 +881,12 @@ void QMediaRecorder::setAudioBitRate(int bitRate)
}
/*!
+ \qmlproperty int QtMultimedia::MediaRecorder::audioChannelCount
+ \since 6.6
+ \brief This property holds the number of audio channels.
+*/
+
+/*!
Returns the number of audio channels.
*/
int QMediaRecorder::audioChannelCount() const
@@ -790,6 +896,11 @@ int QMediaRecorder::audioChannelCount() const
}
/*!
+ \fn void QMediaRecorder::audioChannelCountChanged()
+
+ Signals when the recording audio channel count changes.
+*/
+/*!
Sets the number of audio \a channels.
A value of -1 indicates the recorder should make an optimal choice based on
@@ -805,6 +916,12 @@ void QMediaRecorder::setAudioChannelCount(int channels)
}
/*!
+ \qmlproperty int QtMultimedia::MediaRecorder::audioSampleRate
+ \since 6.6
+ \brief This property holds the audio sample rate in Hz.
+*/
+
+/*!
Returns the audio sample rate in Hz.
*/
int QMediaRecorder::audioSampleRate() const
@@ -812,7 +929,11 @@ int QMediaRecorder::audioSampleRate() const
Q_D(const QMediaRecorder);
return d->encoderSettings.audioSampleRate();
}
+/*!
+ \fn void QMediaRecorder::audioSampleRateChanged()
+ Signals when the recording audio sample rate changes.
+*/
/*!
Sets the audio \a sampleRate in Hz.
diff --git a/src/multimedia/recording/qmediarecorder.h b/src/multimedia/recording/qmediarecorder.h
index baabb35e2..fed276baf 100644
--- a/src/multimedia/recording/qmediarecorder.h
+++ b/src/multimedia/recording/qmediarecorder.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMediaRecorder_H
#define QMediaRecorder_H
@@ -72,7 +36,14 @@ class Q_MULTIMEDIA_EXPORT QMediaRecorder : public QObject
Q_PROPERTY(QMediaRecorder::Error error READ error NOTIFY errorChanged)
Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged)
Q_PROPERTY(QMediaFormat mediaFormat READ mediaFormat WRITE setMediaFormat NOTIFY mediaFormatChanged)
- Q_PROPERTY(Quality quality READ quality WRITE setQuality)
+ Q_PROPERTY(Quality quality READ quality WRITE setQuality NOTIFY qualityChanged)
+ Q_PROPERTY(QMediaRecorder::EncodingMode encodingMode READ encodingMode WRITE setEncodingMode NOTIFY encodingModeChanged)
+ Q_PROPERTY(QSize videoResolution READ videoResolution WRITE setVideoResolution NOTIFY videoResolutionChanged)
+ Q_PROPERTY(qreal videoFrameRate READ videoFrameRate WRITE setVideoFrameRate NOTIFY videoFrameRateChanged)
+ Q_PROPERTY(int videoBitRate READ videoBitRate WRITE setVideoBitRate NOTIFY videoBitRateChanged)
+ Q_PROPERTY(int audioBitRate READ audioBitRate WRITE setAudioBitRate NOTIFY audioBitRateChanged)
+ Q_PROPERTY(int audioChannelCount READ audioChannelCount WRITE setAudioChannelCount NOTIFY audioChannelCountChanged)
+ Q_PROPERTY(int audioSampleRate READ audioSampleRate WRITE setAudioSampleRate NOTIFY audioSampleRateChanged)
public:
enum Quality
{
@@ -119,6 +90,9 @@ public:
QUrl outputLocation() const;
void setOutputLocation(const QUrl &location);
+ void setOutputDevice(QIODevice *device);
+ QIODevice *outputDevice() const;
+
QUrl actualLocation() const;
RecorderState recorderState() const;
diff --git a/src/multimedia/recording/qmediarecorder_p.h b/src/multimedia/recording/qmediarecorder_p.h
index ed46c5cba..193aa5f00 100644
--- a/src/multimedia/recording/qmediarecorder_p.h
+++ b/src/multimedia/recording/qmediarecorder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMediaRecorder_P_H
#define QMediaRecorder_P_H
@@ -73,6 +37,7 @@ public:
QMediaCaptureSession *captureSession = nullptr;
QPlatformMediaRecorder *control = nullptr;
+ QString initErrorMessage;
bool settingsChanged = false;
diff --git a/src/multimedia/recording/qscreencapture-limitations.qdocinc b/src/multimedia/recording/qscreencapture-limitations.qdocinc
new file mode 100644
index 000000000..cac51df02
--- /dev/null
+++ b/src/multimedia/recording/qscreencapture-limitations.qdocinc
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ //! [content]
+ \section1 Screen Capture Limitations
+ On Qt 6.5.2 and 6.5.3, the following limitations apply to using \1ScreenCapture:
+ \list
+ \li It is only supported with the FFmpeg backend.
+ \li It is supported on all desktop platforms, except Linux with Wayland
+ compositor, due to Wayland protocol restrictions and limitations.
+ \li It is not supported on mobile operating systems, except on Android.
+ There, you might run into performance issues as the class is currently
+ implemented via QScreen::grabWindow, which is not optimal for the use case.
+ \li On Linux, it works with X11, but it has not been tested on embedded.
+ \li In most cases, we set a screen capture frame rate that equals the screen
+ refresh rate, except on Windows, where the rate might be flexible.
+ Such a frame rate (75/120 FPS) might cause performance issues on weak
+ CPUs if the captured screen is of 4K resolution.
+ \endlist
+ //! [content]
+*/
diff --git a/src/multimedia/recording/qscreencapture.cpp b/src/multimedia/recording/qscreencapture.cpp
new file mode 100644
index 000000000..c178af1c1
--- /dev/null
+++ b/src/multimedia/recording/qscreencapture.cpp
@@ -0,0 +1,261 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qscreencapture.h"
+#include "qmediacapturesession.h"
+#include <private/qplatformmediaintegration_p.h>
+#include <private/qplatformsurfacecapture_p.h>
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static QScreenCapture::Error toScreenCaptureError(QPlatformSurfaceCapture::Error error)
+{
+ return static_cast<QScreenCapture::Error>(error);
+}
+
+class QScreenCapturePrivate : public QObjectPrivate
+{
+public:
+ QMediaCaptureSession *captureSession = nullptr;
+ std::unique_ptr<QPlatformSurfaceCapture> platformScreenCapture;
+};
+
+/*!
+ \class QScreenCapture
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_video
+ \since 6.5
+
+ \brief This class is used for capturing a screen.
+
+ The class captures a screen. It is managed by
+ the QMediaCaptureSession class where the captured screen can be displayed
+ in a video preview object or recorded to a file.
+
+ \snippet multimedia-snippets/media.cpp Media recorder
+
+ \include qscreencapture-limitations.qdocinc {content} {Q}
+
+ \sa QWindowCapture, QMediaCaptureSession
+*/
+/*!
+ \qmltype ScreenCapture
+ \instantiates QScreenCapture
+ \brief This type is used for capturing a screen.
+
+ \inqmlmodule QtMultimedia
+ \ingroup multimedia_qml
+ \ingroup multimedia_video_qml
+
+ ScreenCapture captures a screen. It is managed by
+ MediaCaptureSession where the captured screen can be displayed
+ in a video preview object or recorded to a file.
+
+ \since 6.5
+ The code below shows a simple capture session with ScreenCapture playing
+ back the captured primary screen view in VideoOutput.
+
+\qml
+ CaptureSession {
+ id: captureSession
+ screenCapture: ScreenCapture {
+ id: screenCapture
+ active: true
+ }
+ videoOutput: VideoOutput {
+ id: videoOutput
+ }
+ }
+\endqml
+
+ \include qscreencapture-limitations.qdocinc {content} {}
+
+ \sa WindowCapture, CaptureSession
+*/
+
+QScreenCapture::QScreenCapture(QObject *parent)
+ : QObject(*new QScreenCapturePrivate, parent)
+{
+ Q_D(QScreenCapture);
+
+ auto platformCapture = QPlatformMediaIntegration::instance()->createScreenCapture(this);
+ if (platformCapture) {
+ connect(platformCapture, &QPlatformSurfaceCapture::activeChanged, this,
+ &QScreenCapture::activeChanged);
+ connect(platformCapture, &QPlatformSurfaceCapture::errorChanged, this,
+ &QScreenCapture::errorChanged);
+ connect(platformCapture, &QPlatformSurfaceCapture::errorOccurred, this,
+ [this](QPlatformSurfaceCapture::Error error, QString errorString) {
+ emit errorOccurred(toScreenCaptureError(error), errorString);
+ });
+
+ connect(platformCapture,
+ qOverload<QPlatformSurfaceCapture::ScreenSource>(
+ &QPlatformSurfaceCapture::sourceChanged),
+ this, &QScreenCapture::screenChanged);
+
+ d->platformScreenCapture.reset(platformCapture);
+ }
+}
+
+QScreenCapture::~QScreenCapture()
+{
+ Q_D(QScreenCapture);
+
+ // Reset platformScreenCapture in the destructor to avoid having broken ref in the object.
+ d->platformScreenCapture.reset();
+
+ if (d->captureSession)
+ d->captureSession->setScreenCapture(nullptr);
+}
+
+/*!
+ \enum QScreenCapture::Error
+
+ Enumerates error codes that can be signaled by the QScreenCapture class.
+ errorString() provides detailed information about the error cause.
+
+ \value NoError No error
+ \value InternalError Internal screen capturing driver error
+ \value CapturingNotSupported Capturing is not supported
+ \value CaptureFailed Capturing screen failed
+ \value NotFound Selected screen not found
+*/
+
+/*!
+ Returns the capture session this QScreenCapture is connected to.
+
+ Use QMediaCaptureSession::setScreenCapture() to connect the camera to
+ a session.
+*/
+QMediaCaptureSession *QScreenCapture::captureSession() const
+{
+ Q_D(const QScreenCapture);
+
+ return d->captureSession;
+}
+
+/*!
+ \qmlproperty bool QtMultimedia::ScreenCapture::active
+ Describes whether the capturing is currently active.
+*/
+
+/*!
+ \property QScreenCapture::active
+ \brief whether the capturing is currently active.
+*/
+void QScreenCapture::setActive(bool active)
+{
+ Q_D(QScreenCapture);
+
+ if (d->platformScreenCapture)
+ d->platformScreenCapture->setActive(active);
+}
+
+bool QScreenCapture::isActive() const
+{
+ Q_D(const QScreenCapture);
+
+ return d->platformScreenCapture && d->platformScreenCapture->isActive();
+}
+
+/*!
+ \qmlproperty Screen QtMultimedia::ScreenCapture::screen
+ Describes the screen for capturing.
+*/
+
+/*!
+ \property QScreenCapture::screen
+ \brief the screen for capturing.
+*/
+
+void QScreenCapture::setScreen(QScreen *screen)
+{
+ Q_D(QScreenCapture);
+
+ if (d->platformScreenCapture)
+ d->platformScreenCapture->setSource(QPlatformSurfaceCapture::ScreenSource(screen));
+}
+
+QScreen *QScreenCapture::screen() const
+{
+ Q_D(const QScreenCapture);
+
+ return d->platformScreenCapture
+ ? d->platformScreenCapture->source<QPlatformSurfaceCapture::ScreenSource>()
+ : nullptr;
+}
+
+/*!
+ \qmlproperty enumeration QtMultimedia::ScreenCapture::error
+ Returns a code of the last error.
+*/
+
+/*!
+ \property QScreenCapture::error
+ \brief the code of the last error.
+*/
+QScreenCapture::Error QScreenCapture::error() const
+{
+ Q_D(const QScreenCapture);
+
+ return d->platformScreenCapture ? toScreenCaptureError(d->platformScreenCapture->error())
+ : CapturingNotSupported;
+}
+
+/*!
+ \fn void QScreenCapture::errorOccurred(QScreenCapture::Error error, const QString &errorString)
+
+ Signals when an \a error occurs, along with the \a errorString.
+*/
+/*!
+ \qmlproperty string QtMultimedia::ScreenCapture::errorString
+ Returns a human readable string describing the cause of error.
+*/
+
+/*!
+ \property QScreenCapture::errorString
+ \brief a human readable string describing the cause of error.
+*/
+QString QScreenCapture::errorString() const
+{
+ Q_D(const QScreenCapture);
+
+ return d->platformScreenCapture ? d->platformScreenCapture->errorString()
+ : QLatin1StringView("Capturing is not support on this platform");
+}
+/*!
+ \fn void QScreenCapture::start()
+
+ Starts screen capture.
+*/
+/*!
+ \fn void QScreenCapture::stop()
+
+ Stops screen capture.
+*/
+/*!
+ \internal
+*/
+void QScreenCapture::setCaptureSession(QMediaCaptureSession *captureSession)
+{
+ Q_D(QScreenCapture);
+
+ d->captureSession = captureSession;
+}
+
+/*!
+ \internal
+*/
+class QPlatformSurfaceCapture *QScreenCapture::platformScreenCapture() const
+{
+ Q_D(const QScreenCapture);
+
+ return d->platformScreenCapture.get();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qscreencapture.cpp"
diff --git a/src/multimedia/recording/qscreencapture.h b/src/multimedia/recording/qscreencapture.h
new file mode 100644
index 000000000..b46925bc0
--- /dev/null
+++ b/src/multimedia/recording/qscreencapture.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSCREENCAPTURE_H
+#define QSCREENCAPTURE_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qnamespace.h>
+#include <QtGui/qscreen.h>
+#include <QtGui/qwindow.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtMultimedia/qtmultimediaglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMediaCaptureSession;
+class QPlatformSurfaceCapture;
+class QScreenCapturePrivate;
+
+class Q_MULTIMEDIA_EXPORT QScreenCapture : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
+ Q_PROPERTY(QScreen *screen READ screen WRITE setScreen NOTIFY screenChanged)
+ Q_PROPERTY(Error error READ error NOTIFY errorChanged)
+ Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged)
+
+public:
+ enum Error {
+ NoError = 0,
+ InternalError = 1,
+ CapturingNotSupported = 2,
+ CaptureFailed = 4,
+ NotFound = 5,
+ };
+ Q_ENUM(Error)
+
+ explicit QScreenCapture(QObject *parent = nullptr);
+ ~QScreenCapture() override;
+
+ QMediaCaptureSession *captureSession() const;
+
+ void setScreen(QScreen *screen);
+ QScreen *screen() const;
+
+ bool isActive() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void setActive(bool active);
+ void start() { setActive(true); }
+ void stop() { setActive(false); }
+
+Q_SIGNALS:
+ void activeChanged(bool);
+ void errorChanged();
+ void screenChanged(QScreen *);
+ void errorOccurred(QScreenCapture::Error error, const QString &errorString);
+
+private:
+ void setCaptureSession(QMediaCaptureSession *captureSession);
+ QPlatformSurfaceCapture *platformScreenCapture() const;
+ friend class QMediaCaptureSession;
+ Q_DISABLE_COPY(QScreenCapture)
+ Q_DECLARE_PRIVATE(QScreenCapture)
+};
+
+QT_END_NAMESPACE
+
+#endif // QSCREENCAPTURE_H
diff --git a/src/multimedia/recording/qwindowcapture.cpp b/src/multimedia/recording/qwindowcapture.cpp
new file mode 100644
index 000000000..69ad60100
--- /dev/null
+++ b/src/multimedia/recording/qwindowcapture.cpp
@@ -0,0 +1,268 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwindowcapture.h"
+#include "qplatformmediaintegration_p.h"
+#include "qmediacapturesession.h"
+#include "private/qobject_p.h"
+#include "private/qplatformsurfacecapture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static QWindowCapture::Error toWindowCaptureError(QPlatformSurfaceCapture::Error error)
+{
+ return static_cast<QWindowCapture::Error>(error);
+}
+
+class QWindowCapturePrivate : public QObjectPrivate
+{
+public:
+ QMediaCaptureSession *captureSession = nullptr;
+ std::unique_ptr<QPlatformSurfaceCapture> platformWindowCapture;
+};
+
+/*!
+ \class QWindowCapture
+ \inmodule QtMultimedia
+ \ingroup multimedia
+ \ingroup multimedia_video
+ \since 6.6
+
+ \brief This class is used for capturing a window.
+
+ The class captures a window. It is managed by
+ the QMediaCaptureSession class where the captured window can be displayed
+ in a video preview object or recorded to a file.
+
+ \sa QMediaCaptureSession, QCapturableWindow
+*/
+/*!
+ \qmltype WindowCapture
+ \instantiates QWindowCapture
+ \inqmlmodule QtMultimedia
+ \ingroup multimedia_qml
+ \ingroup multimedia_video_qml
+ \since 6.6
+
+ \brief This type is used for capturing a window.
+
+ WindowCapture captures a window. It is managed by
+ MediaCaptureSession where the captured window can be displayed
+ in a video preview object or recorded to a file.
+
+ \sa CaptureSession, CapturableWindow
+*/
+
+/*!
+ \enum QWindowCapture::Error
+
+ Enumerates error codes that can be signaled by the QWindowCapture class.
+ errorString() provides detailed information about the error cause.
+
+ \value NoError No error
+ \value InternalError Internal window capturing driver error
+ \value CapturingNotSupported Window capturing is not supported
+ \value CaptureFailed Capturing window failed
+ \value NotFound Selected window not found
+*/
+
+/*!
+ Constructs a new QWindowCapture object with \a parent.
+*/
+QWindowCapture::QWindowCapture(QObject *parent) : QObject(*new QWindowCapturePrivate, parent)
+{
+ Q_D(QWindowCapture);
+
+ qRegisterMetaType<QCapturableWindow>();
+
+ auto platformCapture = QPlatformMediaIntegration::instance()->createWindowCapture(this);
+
+ if (platformCapture) {
+ connect(platformCapture, &QPlatformSurfaceCapture::activeChanged, this,
+ &QWindowCapture::activeChanged);
+ connect(platformCapture, &QPlatformSurfaceCapture::errorChanged, this,
+ &QWindowCapture::errorChanged);
+ connect(platformCapture, &QPlatformSurfaceCapture::errorOccurred, this,
+ [this](QPlatformSurfaceCapture::Error error, QString errorString) {
+ emit errorOccurred(toWindowCaptureError(error), errorString);
+ });
+ connect(platformCapture,
+ qOverload<QCapturableWindow>(&QPlatformSurfaceCapture::sourceChanged), this,
+ &QWindowCapture::windowChanged);
+
+ d->platformWindowCapture.reset(platformCapture);
+ }
+}
+
+/*!
+ Destroys the object.
+ */
+QWindowCapture::~QWindowCapture()
+{
+ Q_D(QWindowCapture);
+
+ d->platformWindowCapture.reset();
+
+ if (d->captureSession)
+ d->captureSession->setWindowCapture(nullptr);
+}
+
+/*!
+ \qmlmethod list<CapturableWindow> QtMultimedia::WindowCapture::capturableWindows()
+
+ Returns a list of CapturableWindow objects that is available for capturing.
+*/
+/*!
+ \fn QList<QCapturableWindow> QWindowCapture::capturableWindows()
+
+ Returns a list of QCapturableWindow objects that is available for capturing.
+ */
+QList<QCapturableWindow> QWindowCapture::capturableWindows()
+{
+ return QPlatformMediaIntegration::instance()->capturableWindowsList();
+}
+
+QMediaCaptureSession *QWindowCapture::captureSession() const
+{
+ Q_D(const QWindowCapture);
+
+ return d->captureSession;
+}
+
+/*!
+ \qmlproperty Window QtMultimedia::WindowCapture::window
+ Describes the window for capturing.
+
+ \sa QtMultimedia::WindowCapture::capturableWindows
+*/
+
+/*!
+ \property QWindowCapture::window
+ \brief the window for capturing.
+
+ \sa QWindowCapture::capturableWindows
+*/
+QCapturableWindow QWindowCapture::window() const
+{
+ Q_D(const QWindowCapture);
+
+ return d->platformWindowCapture ? d->platformWindowCapture->source<QCapturableWindow>()
+ : QCapturableWindow();
+}
+
+void QWindowCapture::setWindow(QCapturableWindow window)
+{
+ Q_D(QWindowCapture);
+
+ if (d->platformWindowCapture)
+ d->platformWindowCapture->setSource(window);
+}
+
+/*!
+ \qmlproperty bool QtMultimedia::WindowCapture::active
+ Describes whether the capturing is currently active.
+*/
+
+/*!
+ \property QWindowCapture::active
+ \brief whether the capturing is currently active.
+
+ \sa start(), stop()
+*/
+bool QWindowCapture::isActive() const
+{
+ Q_D(const QWindowCapture);
+
+ return d->platformWindowCapture && d->platformWindowCapture->isActive();
+}
+
+void QWindowCapture::setActive(bool active)
+{
+ Q_D(QWindowCapture);
+
+ if (d->platformWindowCapture)
+ d->platformWindowCapture->setActive(active);
+}
+
+/*!
+ \qmlmethod QtMultimedia::WindowCapture::start
+*/
+
+/*!
+ \fn void QWindowCapture::start()
+
+ Starts capturing the \l window.
+
+ This is equivalent to setting the \l active property to true.
+*/
+
+/*!
+ \qmlmethod QtMultimedia::WindowCapture::stop
+*/
+
+/*!
+ \fn void QWindowCapture::stop()
+
+ Stops capturing.
+
+ This is equivalent to setting the \l active property to false.
+*/
+
+
+/*!
+ \qmlproperty enumeration QtMultimedia::WindowCapture::error
+ Returns a code of the last error.
+*/
+
+/*!
+ \property QWindowCapture::error
+ \brief the code of the last error.
+*/
+QWindowCapture::Error QWindowCapture::error() const
+{
+ Q_D(const QWindowCapture);
+
+ return d->platformWindowCapture ? toWindowCaptureError(d->platformWindowCapture->error())
+ : CapturingNotSupported;
+}
+
+/*!
+ \fn void QWindowCapture::errorOccurred(QWindowCapture::Error error, const QString &errorString)
+
+ Signals when an \a error occurs, along with the \a errorString.
+*/
+/*!
+ \qmlproperty string QtMultimedia::WindowCapture::errorString
+ Returns a human readable string describing the cause of error.
+*/
+
+/*!
+ \property QWindowCapture::errorString
+ \brief a human readable string describing the cause of error.
+*/
+QString QWindowCapture::errorString() const
+{
+ Q_D(const QWindowCapture);
+
+ return d->platformWindowCapture
+ ? d->platformWindowCapture->errorString()
+ : QLatin1StringView("Capturing is not support on this platform");
+}
+
+void QWindowCapture::setCaptureSession(QMediaCaptureSession *captureSession)
+{
+ Q_D(QWindowCapture);
+
+ d->captureSession = captureSession;
+}
+
+QPlatformSurfaceCapture *QWindowCapture::platformWindowCapture() const
+{
+ Q_D(const QWindowCapture);
+
+ return d->platformWindowCapture.get();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwindowcapture.cpp"
diff --git a/src/multimedia/recording/qwindowcapture.h b/src/multimedia/recording/qwindowcapture.h
new file mode 100644
index 000000000..4c8b2eac3
--- /dev/null
+++ b/src/multimedia/recording/qwindowcapture.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWINDOWCAPTURE_H
+#define QWINDOWCAPTURE_H
+
+#include <QtMultimedia/qtmultimediaexports.h>
+#include <QtMultimedia/qcapturablewindow.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPlatformSurfaceCapture;
+
+class Q_MULTIMEDIA_EXPORT QWindowCapture : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
+ Q_PROPERTY(QCapturableWindow window READ window WRITE setWindow NOTIFY windowChanged)
+ Q_PROPERTY(Error error READ error NOTIFY errorChanged)
+ Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged)
+public:
+ enum Error {
+ NoError = 0,
+ InternalError = 1,
+ CapturingNotSupported = 2,
+ CaptureFailed = 4,
+ NotFound = 5,
+ };
+ Q_ENUM(Error)
+
+ explicit QWindowCapture(QObject *parent = nullptr);
+ ~QWindowCapture() override;
+
+ Q_INVOKABLE static QList<QCapturableWindow> capturableWindows();
+
+ QMediaCaptureSession *captureSession() const;
+
+ void setWindow(QCapturableWindow window);
+
+ QCapturableWindow window() const;
+
+ bool isActive() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void setActive(bool active);
+ void start() { setActive(true); }
+ void stop() { setActive(false); }
+
+Q_SIGNALS:
+ void activeChanged(bool);
+ void windowChanged(QCapturableWindow window);
+ void errorChanged();
+ void errorOccurred(QWindowCapture::Error error, const QString &errorString);
+
+private:
+ void setCaptureSession(QMediaCaptureSession *captureSession);
+ QPlatformSurfaceCapture *platformWindowCapture() const;
+
+ friend class QMediaCaptureSession;
+ Q_DISABLE_COPY(QWindowCapture)
+ Q_DECLARE_PRIVATE(QWindowCapture)
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWCAPTURE_H
diff --git a/src/multimedia/shaders/ayuv.frag b/src/multimedia/shaders/ayuv.frag
index 137933cd2..92c3f71fe 100644
--- a/src/multimedia/shaders/ayuv.frag
+++ b/src/multimedia/shaders/ayuv.frag
@@ -18,4 +18,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/colortransfer.glsl b/src/multimedia/shaders/colortransfer.glsl
index 76af51b6b..1d70ae9eb 100644
--- a/src/multimedia/shaders/colortransfer.glsl
+++ b/src/multimedia/shaders/colortransfer.glsl
@@ -12,14 +12,24 @@ vec4 convertRec709ToLinear(vec4 rgba)
return mix(high, low, cutoff);
}
-vec4 convertSRGBToLinear(vec4 rgba)
+vec4 convertSRGBToLinear(vec4 sRGB)
{
- return pow(rgba, vec4(2.2));
+ // https://en.wikipedia.org/wiki/SRGB
+ const bvec3 cutoff = lessThanEqual(sRGB.rgb, vec3(0.04045));
+ const vec3 low = sRGB.rgb / 12.92;
+ const vec3 high = pow((sRGB.rgb + 0.055) / 1.055, vec3(2.4));
+ vec3 linear = mix(high, low, cutoff);
+ return vec4(linear, sRGB.a);
}
-vec4 convertSRGBFromLinear(vec4 rgba)
+vec4 convertSRGBFromLinear(vec4 linear)
{
- return pow(rgba, vec4(1./2.2));
+ // https://en.wikipedia.org/wiki/SRGB
+ const bvec3 cutoff = lessThanEqual(linear.rgb, vec3(0.0031308));
+ const vec3 low = linear.rgb * 12.92;
+ const vec3 high = 1.055 * pow(linear.rgb, vec3(1.0 / 2.4f)) - 0.055;
+ vec3 sRGB = mix(high, low, cutoff);
+ return vec4(sRGB, linear.a);
}
// This uses the PQ transfer function, see also https://en.wikipedia.org/wiki/Perceptual_quantizer
diff --git a/src/multimedia/shaders/compile.bat b/src/multimedia/shaders/compile.bat
index f8f3f194b..8a012d265 100755
--- a/src/multimedia/shaders/compile.bat
+++ b/src/multimedia/shaders/compile.bat
@@ -1,41 +1,5 @@
-:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-::
:: Copyright (C) 2019 The Qt Company Ltd.
-:: Contact: https://www.qt.io/licensing/
-::
-:: This file is part of the QtQuick module of the Qt Toolkit.
-::
-:: $QT_BEGIN_LICENSE:LGPL$
-:: Commercial License Usage
-:: Licensees holding valid commercial Qt licenses may use this file in
-:: accordance with the commercial license agreement provided with the
-:: Software or, alternatively, in accordance with the terms contained in
-:: a written agreement between you and The Qt Company. For licensing terms
-:: and conditions see https://www.qt.io/terms-conditions. For further
-:: information use the contact form at https://www.qt.io/contact-us.
-::
-:: GNU Lesser General Public License Usage
-:: Alternatively, this file may be used under the terms of the GNU Lesser
-:: General Public License version 3 as published by the Free Software
-:: Foundation and appearing in the file LICENSE.LGPL3 included in the
-:: packaging of this file. Please review the following information to
-:: ensure the GNU Lesser General Public License version 3 requirements
-:: will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-::
-:: GNU General Public License Usage
-:: Alternatively, this file may be used under the terms of the GNU
-:: General Public License version 2.0 or (at your option) the GNU General
-:: Public license version 3 or any later version approved by the KDE Free
-:: Qt Foundation. The licenses are as published by the Free Software
-:: Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-:: included in the packaging of this file. Please review the following
-:: information to ensure the GNU General Public License requirements will
-:: be met: https://www.gnu.org/licenses/gpl-2.0.html and
-:: https://www.gnu.org/licenses/gpl-3.0.html.
-::
-:: $QT_END_LICENSE$
-::
-:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o rgba.vert.qsb rgba.vert
qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o rgba.frag.qsb rgba.frag
diff --git a/src/multimedia/shaders/imc2.frag b/src/multimedia/shaders/imc2.frag
index b6b9e7360..18401d420 100644
--- a/src/multimedia/shaders/imc2.frag
+++ b/src/multimedia/shaders/imc2.frag
@@ -14,12 +14,16 @@ void main()
{
float Y = texture(plane1Texture, texCoord).r;
float x = texCoord.x/2.;
- float U = texture(plane2Texture, vec2(x, texCoord.y)).r;
- float V = texture(plane2Texture, vec2(x + .5, texCoord.y)).r;
+ float U = texture(plane2Texture, vec2(x + .5, texCoord.y)).r;
+ float V = texture(plane2Texture, vec2(x, texCoord.y)).r;
vec4 color = vec4(Y, U, V, 1.);
fragColor = ubuf.colorMatrix * color * ubuf.opacity;
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/imc4.frag b/src/multimedia/shaders/imc4.frag
index b6b9e7360..574a5f49b 100644
--- a/src/multimedia/shaders/imc4.frag
+++ b/src/multimedia/shaders/imc4.frag
@@ -22,4 +22,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/nv12.frag b/src/multimedia/shaders/nv12.frag
index 2e36882be..20399d9f0 100644
--- a/src/multimedia/shaders/nv12.frag
+++ b/src/multimedia/shaders/nv12.frag
@@ -20,4 +20,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/nv21.frag b/src/multimedia/shaders/nv21.frag
index a8f94c8fc..c3f641be5 100644
--- a/src/multimedia/shaders/nv21.frag
+++ b/src/multimedia/shaders/nv21.frag
@@ -20,4 +20,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/uyvy.frag b/src/multimedia/shaders/uyvy.frag
index 003d1caba..26a83da3f 100644
--- a/src/multimedia/shaders/uyvy.frag
+++ b/src/multimedia/shaders/uyvy.frag
@@ -20,4 +20,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/y.frag b/src/multimedia/shaders/y.frag
index bec20c788..8c25fc762 100644
--- a/src/multimedia/shaders/y.frag
+++ b/src/multimedia/shaders/y.frag
@@ -18,4 +18,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/yuv_triplanar.frag b/src/multimedia/shaders/yuv_triplanar.frag
index 5b83b05b5..228147943 100644
--- a/src/multimedia/shaders/yuv_triplanar.frag
+++ b/src/multimedia/shaders/yuv_triplanar.frag
@@ -22,4 +22,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/yuv_triplanar_p10.frag b/src/multimedia/shaders/yuv_triplanar_p10.frag
index bac34c72c..5da0ed339 100644
--- a/src/multimedia/shaders/yuv_triplanar_p10.frag
+++ b/src/multimedia/shaders/yuv_triplanar_p10.frag
@@ -22,4 +22,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/yuyv.frag b/src/multimedia/shaders/yuyv.frag
index 8f1c31e64..0f6e65b6d 100644
--- a/src/multimedia/shaders/yuyv.frag
+++ b/src/multimedia/shaders/yuyv.frag
@@ -20,4 +20,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/shaders/yvu_triplanar.frag b/src/multimedia/shaders/yvu_triplanar.frag
index 37fc3a18d..ac2cbdf63 100644
--- a/src/multimedia/shaders/yvu_triplanar.frag
+++ b/src/multimedia/shaders/yvu_triplanar.frag
@@ -22,4 +22,8 @@ void main()
#ifdef QMM_OUTPUTSURFACE_LINEAR
fragColor = convertSRGBToLinear(fragColor);
#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
}
diff --git a/src/multimedia/spatial/qambisonicdecoder.cpp b/src/multimedia/spatial/qambisonicdecoder.cpp
deleted file mode 100644
index a25754ef0..000000000
--- a/src/multimedia/spatial/qambisonicdecoder.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "qambisonicdecoder_p.h"
-
-#include "qambisonicdecoderdata_p.h"
-#include <cmath>
-
-QT_BEGIN_NAMESPACE
-
-// Ambisonic decoding is described in detail in https://ambisonics.dreamhosters.com/BLaH3.pdf.
-// We're using a phase matched band splitting filter to split the ambisonic signal into a low
-// and high frequency component and apply matrix conversions to those components individually
-// as described in the document.
-//
-// We are currently not using a near field compensation filter, something that could potentially
-// improve sound quality further.
-
-
-struct QAmbisonicDecoderData
-{
- QAudioFormat::ChannelConfig config;
- const float *lf[3];
- const float *hf[3];
-};
-
-static const QAmbisonicDecoderData decoderMap[] =
-{
- { QAudioFormat::ChannelConfigMono,
- { decoderMatrix_mono_1_lf, decoderMatrix_mono_2_lf, decoderMatrix_mono_3_lf },
- { decoderMatrix_mono_1_hf, decoderMatrix_mono_2_hf, decoderMatrix_mono_3_hf }
- },
- { QAudioFormat::ChannelConfigStereo,
- { decoderMatrix_stereo_1_lf, decoderMatrix_stereo_2_lf, decoderMatrix_stereo_3_lf },
- { decoderMatrix_stereo_1_hf, decoderMatrix_stereo_2_hf, decoderMatrix_stereo_3_hf }
- },
- { QAudioFormat::ChannelConfig2Dot1,
- { decoderMatrix_2dot1_1_lf, decoderMatrix_2dot1_2_lf, decoderMatrix_2dot1_3_lf },
- { decoderMatrix_2dot1_1_hf, decoderMatrix_2dot1_2_hf, decoderMatrix_2dot1_3_hf }
- },
- { QAudioFormat::ChannelConfigSurround5Dot0,
- { decoderMatrix_5dot0_1_lf, decoderMatrix_5dot0_2_lf, decoderMatrix_5dot0_3_lf },
- { decoderMatrix_5dot0_1_hf, decoderMatrix_5dot0_2_hf, decoderMatrix_5dot0_3_hf }
- },
- { QAudioFormat::ChannelConfigSurround5Dot1,
- { decoderMatrix_5dot1_1_lf, decoderMatrix_5dot1_2_lf, decoderMatrix_5dot1_3_lf },
- { decoderMatrix_5dot1_1_hf, decoderMatrix_5dot1_2_hf, decoderMatrix_5dot1_3_hf }
- },
- { QAudioFormat::ChannelConfigSurround7Dot0,
- { decoderMatrix_7dot0_1_lf, decoderMatrix_7dot0_2_lf, decoderMatrix_7dot0_3_lf },
- { decoderMatrix_7dot0_1_hf, decoderMatrix_7dot0_2_hf, decoderMatrix_7dot0_3_hf }
- },
- { QAudioFormat::ChannelConfigSurround7Dot1,
- { decoderMatrix_7dot1_1_lf, decoderMatrix_7dot1_2_lf, decoderMatrix_7dot1_3_lf },
- { decoderMatrix_7dot1_1_hf, decoderMatrix_7dot1_2_hf, decoderMatrix_7dot1_3_hf }
- }
-};
-
-
-// Implements a split second order IIR filter
-// The audio data is split into a phase synced low and high frequency part
-// This allows us to apply different factors to both parts for better sound
-// localization when converting from ambisonic formats
-//
-// Details are described in https://ambisonics.dreamhosters.com/BLaH3.pdf, Appendix A.2.
-class QAmbisonicDecoderFilter
-{
-public:
- QAmbisonicDecoderFilter() = default;
- void configure(float sampleRate, float cutoffFrequency = 380)
- {
- double k = tan(M_PI*cutoffFrequency/sampleRate);
- a1 = float(2.*(k*k - 1.)/(k*k + 2*k + 1.));
- a2 = float((k*k - 2*k + 1.)/(k*k + 2*k + 1.));
-
- b0_lf = float(k*k/(k*k + 2*k + 1));
- b1_lf = 2.f*b0_lf;
-
- b0_hf = float(1./(k*k + 2*k + 1));
- b1_hf = -2.f*b0_hf;
- }
-
- struct Output
- {
- float lf;
- float hf;
- };
-
- Output next(float x)
- {
- float r_lf = x*b0_lf +
- prevX[0]*b1_lf +
- prevX[1]*b0_lf -
- prevR_lf[0]*a1 -
- prevR_lf[1]*a2;
- float r_hf = x*b0_hf +
- prevX[0]*b1_hf +
- prevX[1]*b0_hf -
- prevR_hf[0]*a1 -
- prevR_hf[1]*a2;
- prevX[1] = prevX[0];
- prevX[0] = x;
- prevR_lf[1] = prevR_lf[0];
- prevR_lf[0] = r_lf;
- prevR_hf[1] = prevR_hf[0];
- prevR_hf[0] = r_hf;
- return { r_lf, r_hf };
- }
-
-private:
- float a1 = 0.;
- float a2 = 0.;
-
- float b0_hf = 0.;
- float b1_hf = 0.;
-
- float b0_lf = 0.;
- float b1_lf = 0.;
-
- float prevX[2] = {};
- float prevR_lf[2] = {};
- float prevR_hf[2] = {};
-};
-
-
-QAmbisonicDecoder::QAmbisonicDecoder(AmbisonicLevel ambisonicLevel, const QAudioFormat &format)
- : level(ambisonicLevel)
-{
- auto outputConfiguration = format.channelConfig();
- if (outputConfiguration == QAudioFormat::ChannelConfigUnknown)
- outputConfiguration = format.defaultChannelConfigForChannelCount(format.channelCount());
- for (const auto &d : decoderMap) {
- if (d.config == outputConfiguration) {
- decoderData = &d;
- break;
- }
- }
- if (!decoderData) {
- // ### FIXME: use a stereo config, fill other channels with 0
- }
-
- inputChannels = (level+1)*(level+1);
- outputChannels = format.channelCount();
-
- filters = new QAmbisonicDecoderFilter[inputChannels];
- for (int i = 0; i < inputChannels; ++i)
- filters[i].configure(format.sampleRate());
-}
-
-void QAmbisonicDecoder::processBuffer(const float *input[], float *output, int nSamples)
-{
- float *o = output;
- memset(o, 0, nSamples*outputChannels*sizeof(float));
-
- const float *matrix_hi = decoderData->hf[level];
- const float *matrix_lo = decoderData->hf[level];
- for (int i = 0; i < nSamples; ++i) {
- QAmbisonicDecoderFilter::Output buf[maxAmbisonicChannels];
- for (int j = 0; j < inputChannels; ++j)
- buf[j] = filters[j].next(input[j][i]);
- for (int j = 0; j < inputChannels; ++j) {
- for (int k = 0; k < outputChannels; ++k)
- o[k] += matrix_lo[k*outputChannels + j]*buf[j].lf + matrix_hi[k*outputChannels + j]*buf[j].hf;
- }
- o += outputChannels;
- }
-}
-
-void QAmbisonicDecoder::processBuffer(const float *input[], short *output, int nSamples)
-{
- if (level == 0) {
- // ### copy W data
- return;
- }
- const float *matrix_hi = decoderData->hf[level - 1];
- const float *matrix_lo = decoderData->hf[level - 1];
- for (int i = 0; i < nSamples; ++i) {
- QAmbisonicDecoderFilter::Output buf[maxAmbisonicChannels];
- for (int j = 0; j < inputChannels; ++j)
- buf[j] = filters[j].next(input[j][i]);
- float o[32]; // we can't support more than 32 channels from our API
- memset(o, 0, 32*sizeof(short));
- for (int j = 0; j < inputChannels; ++j) {
- for (int k = 0; k < outputChannels; ++k)
- o[k] += matrix_lo[k*outputChannels + j]*buf[j].lf + matrix_hi[k*outputChannels + j]*buf[j].hf;
- }
- for (int k = 0; k < outputChannels; ++k)
- output[k] = static_cast<short>(o[k]*32768.);
- output += outputChannels;
- }
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/multimedia/spatial/qambisonicdecoder_p.h b/src/multimedia/spatial/qambisonicdecoder_p.h
deleted file mode 100644
index 3c6d87b60..000000000
--- a/src/multimedia/spatial/qambisonicdecoder_p.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QAMBISONICDECODER_P_H
-#define QAMBISONICDECODER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <qtmultimediaglobal_p.h>
-#include <qaudioformat.h>
-
-QT_BEGIN_NAMESPACE
-
-struct QAmbisonicDecoderData;
-class QAmbisonicDecoderFilter;
-
-class QAmbisonicDecoder
-{
-public:
- enum AmbisonicLevel
- {
- AmbisonicLevel1 = 1,
- LowQuality = AmbisonicLevel1,
- AmbisonicLevel2 = 2,
- MediumQuality = AmbisonicLevel2,
- AmbisonicLevel3 = 3,
- HighQuality = AmbisonicLevel3
- };
- QAmbisonicDecoder(AmbisonicLevel ambisonicLevel, const QAudioFormat &format);
-
- int nInputChannels() const { return inputChannels; }
- int nOutputChannels() const { return outputChannels; }
-
- int outputSize(int nSamples) const { return outputChannels * nSamples; }
-
- // input is planar, output interleaved
- void processBuffer(const float *input[], float *output, int nSamples);
- void processBuffer(const float *input[], short *output, int nSamples);
-
- static constexpr int maxAmbisonicChannels = 16;
- static constexpr int maxAmbisonicLevel = 3;
-private:
- QAudioFormat format;
- AmbisonicLevel level = AmbisonicLevel1;
- int inputChannels = 0;
- int outputChannels = 0;
- const QAmbisonicDecoderData *decoderData = nullptr;
- QAmbisonicDecoderFilter *filters = nullptr;
-
-};
-
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimedia/spatial/qambisonicdecoderdata_p.h b/src/multimedia/spatial/qambisonicdecoderdata_p.h
deleted file mode 100644
index fa7b462f4..000000000
--- a/src/multimedia/spatial/qambisonicdecoderdata_p.h
+++ /dev/null
@@ -1,423 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QAMBISONICDECODERDATA_P_H
-#define QAMBISONICDECODERDATA_P_H
-
-#include <qtmultimediaglobal_p.h>
-
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-// This file is generated by the matlab/octave file adt_generate_qt.m
-// using the Ambisonic Decoder Toolbox (https://bitbucket.org/ambidecodertoolbox/adt/src/master/)
-
-
-QT_BEGIN_NAMESPACE
-
-// Decoder matrix for mono, ambisonic level 1
-static constexpr float decoderMatrix_mono_1_lf[1*4] = {
-0.471409f, 0.619827f, 0.000000f, 0.000000f, // C
-};
-
-// Decoder matrix for mono, ambisonic level 1
-static constexpr float decoderMatrix_mono_1_hf[1*4] = {
-0.666673f, 0.506086f, 0.000000f, 0.000000f, // C
-};
-
-// Decoder matrix for mono, ambisonic level 2
-static constexpr float decoderMatrix_mono_2_lf[1*9] = {
-0.471409f, 0.619827f, 0.000000f, 0.000000f, -0.138903f, 0.000000f, 0.000000f, 0.208318f, 0.000000f, // C
-};
-
-// Decoder matrix for mono, ambisonic level 2
-static constexpr float decoderMatrix_mono_2_hf[1*9] = {
-0.745364f, 0.759129f, 0.000000f, 0.000000f, -0.087850f, 0.000000f, 0.000000f, 0.131752f, 0.000000f, // C
-};
-
-// Decoder matrix for mono, ambisonic level 3
-static constexpr float decoderMatrix_mono_3_lf[1*16] = {
-0.471409f, 0.619827f, 0.000000f, 0.000000f, -0.138903f, 0.000000f, 0.000000f, 0.208318f, 0.000000f, 0.000000f, 0.080888f, 0.000000f, 0.000000f, 0.000000f, -0.097901f, 0.000000f, // C
-};
-
-// Decoder matrix for mono, ambisonic level 3
-static constexpr float decoderMatrix_mono_3_hf[1*16] = {
-0.786398f, 0.890402f, 0.000000f, 0.000000f, -0.141888f, 0.000000f, 0.000000f, 0.212793f, 0.000000f, 0.000000f, 0.041121f, 0.000000f, 0.000000f, 0.000000f, -0.049771f, 0.000000f, // C
-};
-
-// Decoder matrix for stereo, ambisonic level 1
-static constexpr float decoderMatrix_stereo_1_lf[2*4] = {
-0.280670f, 0.372187f, 0.240963f, 0.000000f, // L
-0.280656f, 0.372181f, -0.240980f, 0.000000f, // R
-};
-
-// Decoder matrix for stereo, ambisonic level 1
-static constexpr float decoderMatrix_stereo_1_hf[2*4] = {
-0.396928f, 0.303889f, 0.196746f, 0.000000f, // L
-0.396908f, 0.303884f, -0.196759f, 0.000000f, // R
-};
-
-// Decoder matrix for stereo, ambisonic level 2
-static constexpr float decoderMatrix_stereo_2_lf[2*9] = {
-0.280670f, 0.372187f, 0.240963f, 0.000000f, -0.085490f, 0.000000f, 0.000000f, 0.135154f, 0.311013f, // L
-0.280656f, 0.372181f, -0.240980f, 0.000000f, -0.085579f, 0.000000f, 0.000000f, 0.135130f, -0.311015f, // R
-};
-
-// Decoder matrix for stereo, ambisonic level 2
-static constexpr float decoderMatrix_stereo_2_hf[2*9] = {
-0.443779f, 0.455834f, 0.295119f, 0.000000f, -0.054068f, 0.000000f, 0.000000f, 0.085479f, 0.196702f, // L
-0.443757f, 0.455826f, -0.295139f, 0.000000f, -0.054125f, 0.000000f, 0.000000f, 0.085464f, -0.196703f, // R
-};
-
-// Decoder matrix for stereo, ambisonic level 3
-static constexpr float decoderMatrix_stereo_3_lf[2*16] = {
-0.280670f, 0.372187f, 0.240963f, 0.000000f, -0.085490f, 0.000000f, 0.000000f, 0.135154f, 0.311013f, 0.000000f, 0.045437f, 0.021103f, 0.000000f, 0.000000f, -0.035873f, 0.169918f, // L
-0.280656f, 0.372181f, -0.240980f, 0.000000f, -0.085579f, 0.000000f, 0.000000f, 0.135130f, -0.311015f, 0.000000f, 0.045414f, -0.021065f, 0.000000f, 0.000000f, -0.035873f, -0.169892f, // R
-};
-
-// Decoder matrix for stereo, ambisonic level 3
-static constexpr float decoderMatrix_stereo_3_hf[2*16] = {
-0.468210f, 0.534659f, 0.346152f, 0.000000f, -0.087326f, 0.000000f, 0.000000f, 0.138058f, 0.317696f, 0.000000f, 0.023099f, 0.010728f, 0.000000f, 0.000000f, -0.018237f, 0.086382f, // L
-0.468186f, 0.534650f, -0.346176f, 0.000000f, -0.087418f, 0.000000f, 0.000000f, 0.138033f, -0.317697f, 0.000000f, 0.023087f, -0.010709f, 0.000000f, 0.000000f, -0.018237f, -0.086369f, // R
-};
-
-// Decoder matrix for 2dot1, ambisonic level 1
-static constexpr float decoderMatrix_2dot1_1_lf[3*4] = {
-0.280670f, 0.372187f, 0.240963f, 0.000000f, // L
-0.280656f, 0.372181f, -0.240980f, 0.000000f, // R
-0.5f, 0.0f, 0.0f, 0.0f, // LFE
-};
-
-// Decoder matrix for 2dot1, ambisonic level 1
-static constexpr float decoderMatrix_2dot1_1_hf[3*4] = {
-0.396928f, 0.303889f, 0.196746f, 0.000000f, // L
-0.396908f, 0.303884f, -0.196759f, 0.000000f, // R
-0.0f, 0.0f, 0.0f, 0.0f, // LFE
-};
-
-// Decoder matrix for 2dot1, ambisonic level 2
-static constexpr float decoderMatrix_2dot1_2_lf[3*9] = {
-0.280670f, 0.372187f, 0.240963f, 0.000000f, -0.085490f, 0.000000f, 0.000000f, 0.135154f, 0.311013f, // L
-0.280656f, 0.372181f, -0.240980f, 0.000000f, -0.085579f, 0.000000f, 0.000000f, 0.135130f, -0.311015f, // R
-0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-};
-
-// Decoder matrix for 2dot1, ambisonic level 2
-static constexpr float decoderMatrix_2dot1_2_hf[3*9] = {
-0.443779f, 0.455834f, 0.295119f, 0.000000f, -0.054068f, 0.000000f, 0.000000f, 0.085479f, 0.196702f, // L
-0.443757f, 0.455826f, -0.295139f, 0.000000f, -0.054125f, 0.000000f, 0.000000f, 0.085464f, -0.196703f, // R
-0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-};
-
-// Decoder matrix for 2dot1, ambisonic level 3
-static constexpr float decoderMatrix_2dot1_3_lf[3*16] = {
-0.280670f, 0.372187f, 0.240963f, 0.000000f, -0.085490f, 0.000000f, 0.000000f, 0.135154f, 0.311013f, 0.000000f, 0.045437f, 0.021103f, 0.000000f, 0.000000f, -0.035873f, 0.169918f, // L
-0.280656f, 0.372181f, -0.240980f, 0.000000f, -0.085579f, 0.000000f, 0.000000f, 0.135130f, -0.311015f, 0.000000f, 0.045414f, -0.021065f, 0.000000f, 0.000000f, -0.035873f, -0.169892f, // R
-0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-};
-
-// Decoder matrix for 2dot1, ambisonic level 3
-static constexpr float decoderMatrix_2dot1_3_hf[3*16] = {
-0.468210f, 0.534659f, 0.346152f, 0.000000f, -0.087326f, 0.000000f, 0.000000f, 0.138058f, 0.317696f, 0.000000f, 0.023099f, 0.010728f, 0.000000f, 0.000000f, -0.018237f, 0.086382f, // L
-0.468186f, 0.534650f, -0.346176f, 0.000000f, -0.087418f, 0.000000f, 0.000000f, 0.138033f, -0.317697f, 0.000000f, 0.023087f, -0.010709f, 0.000000f, 0.000000f, -0.018237f, -0.086369f, // R
-0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-};
-
-// Decoder matrix for 5dot0, ambisonic level 1
-static constexpr float decoderMatrix_5dot0_1_lf[5*4] = {
-0.240964f, 0.257639f, 0.287251f, 0.000000f, // L
-0.240957f, 0.257633f, -0.287251f, 0.000000f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, // C
-0.520591f, -0.419052f, 0.415955f, 0.000000f, // Ls
-0.520596f, -0.419047f, -0.415960f, 0.000000f, // Rs
-};
-
-// Decoder matrix for 5dot0, ambisonic level 1
-static constexpr float decoderMatrix_5dot0_1_hf[5*4] = {
-0.340774f, 0.210361f, 0.234540f, 0.000000f, // L
-0.340764f, 0.210357f, -0.234540f, 0.000000f, // R
-0.180812f, 0.179246f, 0.000000f, 0.000000f, // C
-0.736226f, -0.342155f, 0.339626f, 0.000000f, // Ls
-0.736233f, -0.342151f, -0.339630f, 0.000000f, // Rs
-};
-
-// Decoder matrix for 5dot0, ambisonic level 2
-static constexpr float decoderMatrix_5dot0_2_lf[5*9] = {
-0.240964f, 0.257639f, 0.287251f, 0.000000f, -0.063536f, 0.000000f, 0.000000f, -0.026015f, 0.315018f, // L
-0.240957f, 0.257633f, -0.287251f, 0.000000f, -0.063562f, 0.000000f, 0.000000f, -0.026018f, -0.315014f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, -0.037403f, 0.000000f, 0.000000f, 0.222021f, 0.000000f, // C
-0.520591f, -0.419052f, 0.415955f, 0.000000f, -0.092715f, 0.000000f, 0.000000f, -0.061595f, -0.083957f, // Ls
-0.520596f, -0.419047f, -0.415960f, 0.000000f, -0.092693f, 0.000000f, 0.000000f, -0.061591f, 0.083957f, // Rs
-};
-
-// Decoder matrix for 5dot0, ambisonic level 2
-static constexpr float decoderMatrix_5dot0_2_hf[5*9] = {
-0.380997f, 0.315542f, 0.351810f, 0.000000f, -0.040184f, 0.000000f, 0.000000f, -0.016453f, 0.199235f, // L
-0.380986f, 0.315535f, -0.351809f, 0.000000f, -0.040200f, 0.000000f, 0.000000f, -0.016455f, -0.199232f, // R
-0.202154f, 0.268870f, 0.000000f, 0.000000f, -0.023656f, 0.000000f, 0.000000f, 0.140418f, 0.000000f, // C
-0.823126f, -0.513232f, 0.509439f, 0.000000f, -0.058638f, 0.000000f, 0.000000f, -0.038956f, -0.053099f, // Ls
-0.823134f, -0.513226f, -0.509444f, 0.000000f, -0.058624f, 0.000000f, 0.000000f, -0.038954f, 0.053099f, // Rs
-};
-
-// Decoder matrix for 5dot0, ambisonic level 3
-static constexpr float decoderMatrix_5dot0_3_lf[5*16] = {
-0.240964f, 0.257639f, 0.287251f, 0.000000f, -0.063536f, 0.000000f, 0.000000f, -0.026015f, 0.315018f, 0.000000f, 0.035335f, 0.041639f, 0.000000f, 0.000000f, -0.142145f, 0.134220f, // L
-0.240957f, 0.257633f, -0.287251f, 0.000000f, -0.063562f, 0.000000f, 0.000000f, -0.026018f, -0.315014f, 0.000000f, 0.035320f, -0.041624f, 0.000000f, 0.000000f, -0.142141f, -0.134218f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, -0.037403f, 0.000000f, 0.000000f, 0.222021f, 0.000000f, 0.000000f, 0.026162f, 0.000000f, 0.000000f, 0.000000f, 0.204779f, 0.000000f, // C
-0.520591f, -0.419052f, 0.415955f, 0.000000f, -0.092715f, 0.000000f, 0.000000f, -0.061595f, -0.083957f, 0.000000f, -0.101526f, 0.068569f, 0.000000f, 0.000000f, -0.017680f, -0.126417f, // Ls
-0.520596f, -0.419047f, -0.415960f, 0.000000f, -0.092693f, 0.000000f, 0.000000f, -0.061591f, 0.083957f, 0.000000f, -0.101519f, -0.068568f, 0.000000f, 0.000000f, -0.017687f, 0.126417f, // Rs
-};
-
-// Decoder matrix for 5dot0, ambisonic level 3
-static constexpr float decoderMatrix_5dot0_3_hf[5*16] = {
-0.401972f, 0.370107f, 0.412646f, 0.000000f, -0.064901f, 0.000000f, 0.000000f, -0.026574f, 0.321786f, 0.000000f, 0.017964f, 0.021168f, 0.000000f, 0.000000f, -0.072263f, 0.068234f, // L
-0.401960f, 0.370099f, -0.412646f, 0.000000f, -0.064928f, 0.000000f, 0.000000f, -0.026577f, -0.321782f, 0.000000f, 0.017956f, -0.021161f, 0.000000f, 0.000000f, -0.072261f, -0.068233f, // R
-0.213283f, 0.315364f, 0.000000f, 0.000000f, -0.038207f, 0.000000f, 0.000000f, 0.226791f, 0.000000f, 0.000000f, 0.013300f, 0.000000f, 0.000000f, 0.000000f, 0.104104f, 0.000000f, // C
-0.868441f, -0.601983f, 0.597533f, 0.000000f, -0.094707f, 0.000000f, 0.000000f, -0.062919f, -0.085760f, 0.000000f, -0.051613f, 0.034859f, 0.000000f, 0.000000f, -0.008988f, -0.064267f, // Ls
-0.868449f, -0.601975f, -0.597540f, 0.000000f, -0.094684f, 0.000000f, 0.000000f, -0.062915f, 0.085761f, 0.000000f, -0.051609f, -0.034858f, 0.000000f, 0.000000f, -0.008992f, 0.064267f, // Rs
-};
-
-// Decoder matrix for 5dot1, ambisonic level 1
-static constexpr float decoderMatrix_5dot1_1_lf[6*4] = {
-0.240964f, 0.257639f, 0.287251f, 0.000000f, // L
-0.240957f, 0.257633f, -0.287251f, 0.000000f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, // C
-0.5f, 0.0f, 0.0f, 0.0f, // LFE
-0.520591f, -0.419052f, 0.415955f, 0.000000f, // Ls
-0.520596f, -0.419047f, -0.415960f, 0.000000f, // Rs
-};
-
-// Decoder matrix for 5dot1, ambisonic level 1
-static constexpr float decoderMatrix_5dot1_1_hf[6*4] = {
-0.340774f, 0.210361f, 0.234540f, 0.000000f, // L
-0.340764f, 0.210357f, -0.234540f, 0.000000f, // R
-0.180812f, 0.179246f, 0.000000f, 0.000000f, // C
-0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.736226f, -0.342155f, 0.339626f, 0.000000f, // Ls
-0.736233f, -0.342151f, -0.339630f, 0.000000f, // Rs
-};
-
-// Decoder matrix for 5dot1, ambisonic level 2
-static constexpr float decoderMatrix_5dot1_2_lf[6*9] = {
-0.240964f, 0.257639f, 0.287251f, 0.000000f, -0.063536f, 0.000000f, 0.000000f, -0.026015f, 0.315018f, // L
-0.240957f, 0.257633f, -0.287251f, 0.000000f, -0.063562f, 0.000000f, 0.000000f, -0.026018f, -0.315014f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, -0.037403f, 0.000000f, 0.000000f, 0.222021f, 0.000000f, // C
-0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.520591f, -0.419052f, 0.415955f, 0.000000f, -0.092715f, 0.000000f, 0.000000f, -0.061595f, -0.083957f, // Ls
-0.520596f, -0.419047f, -0.415960f, 0.000000f, -0.092693f, 0.000000f, 0.000000f, -0.061591f, 0.083957f, // Rs
-};
-
-// Decoder matrix for 5dot1, ambisonic level 2
-static constexpr float decoderMatrix_5dot1_2_hf[6*9] = {
-0.380997f, 0.315542f, 0.351810f, 0.000000f, -0.040184f, 0.000000f, 0.000000f, -0.016453f, 0.199235f, // L
-0.380986f, 0.315535f, -0.351809f, 0.000000f, -0.040200f, 0.000000f, 0.000000f, -0.016455f, -0.199232f, // R
-0.202154f, 0.268870f, 0.000000f, 0.000000f, -0.023656f, 0.000000f, 0.000000f, 0.140418f, 0.000000f, // C
-0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.823126f, -0.513232f, 0.509439f, 0.000000f, -0.058638f, 0.000000f, 0.000000f, -0.038956f, -0.053099f, // Ls
-0.823134f, -0.513226f, -0.509444f, 0.000000f, -0.058624f, 0.000000f, 0.000000f, -0.038954f, 0.053099f, // Rs
-};
-
-// Decoder matrix for 5dot1, ambisonic level 3
-static constexpr float decoderMatrix_5dot1_3_lf[6*16] = {
-0.240964f, 0.257639f, 0.287251f, 0.000000f, -0.063536f, 0.000000f, 0.000000f, -0.026015f, 0.315018f, 0.000000f, 0.035335f, 0.041639f, 0.000000f, 0.000000f, -0.142145f, 0.134220f, // L
-0.240957f, 0.257633f, -0.287251f, 0.000000f, -0.063562f, 0.000000f, 0.000000f, -0.026018f, -0.315014f, 0.000000f, 0.035320f, -0.041624f, 0.000000f, 0.000000f, -0.142141f, -0.134218f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, -0.037403f, 0.000000f, 0.000000f, 0.222021f, 0.000000f, 0.000000f, 0.026162f, 0.000000f, 0.000000f, 0.000000f, 0.204779f, 0.000000f, // C
-0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.520591f, -0.419052f, 0.415955f, 0.000000f, -0.092715f, 0.000000f, 0.000000f, -0.061595f, -0.083957f, 0.000000f, -0.101526f, 0.068569f, 0.000000f, 0.000000f, -0.017680f, -0.126417f, // Ls
-0.520596f, -0.419047f, -0.415960f, 0.000000f, -0.092693f, 0.000000f, 0.000000f, -0.061591f, 0.083957f, 0.000000f, -0.101519f, -0.068568f, 0.000000f, 0.000000f, -0.017687f, 0.126417f, // Rs
-};
-
-// Decoder matrix for 5dot1, ambisonic level 3
-static constexpr float decoderMatrix_5dot1_3_hf[6*16] = {
-0.401972f, 0.370107f, 0.412646f, 0.000000f, -0.064901f, 0.000000f, 0.000000f, -0.026574f, 0.321786f, 0.000000f, 0.017964f, 0.021168f, 0.000000f, 0.000000f, -0.072263f, 0.068234f, // L
-0.401960f, 0.370099f, -0.412646f, 0.000000f, -0.064928f, 0.000000f, 0.000000f, -0.026577f, -0.321782f, 0.000000f, 0.017956f, -0.021161f, 0.000000f, 0.000000f, -0.072261f, -0.068233f, // R
-0.213283f, 0.315364f, 0.000000f, 0.000000f, -0.038207f, 0.000000f, 0.000000f, 0.226791f, 0.000000f, 0.000000f, 0.013300f, 0.000000f, 0.000000f, 0.000000f, 0.104104f, 0.000000f, // C
-0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.868441f, -0.601983f, 0.597533f, 0.000000f, -0.094707f, 0.000000f, 0.000000f, -0.062919f, -0.085760f, 0.000000f, -0.051613f, 0.034859f, 0.000000f, 0.000000f, -0.008988f, -0.064267f, // Ls
-0.868449f, -0.601975f, -0.597540f, 0.000000f, -0.094684f, 0.000000f, 0.000000f, -0.062915f, 0.085761f, 0.000000f, -0.051609f, -0.034858f, 0.000000f, 0.000000f, -0.008992f, 0.064267f, // Rs
-};
-
-// Decoder matrix for 7dot0, ambisonic level 1
-static constexpr float decoderMatrix_7dot0_1_lf[7*4] = {
-0.194124f, 0.244088f, 0.209910f, 0.000000f, // L
-0.194108f, 0.244084f, -0.209914f, 0.000000f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, // C
-0.260404f, 0.000000f, 0.413172f, 0.000000f, // Ls
-0.260430f, 0.000000f, -0.413179f, 0.000000f, // Rs
-0.260425f, -0.357822f, 0.206589f, 0.000000f, // Lb
-0.260400f, -0.357815f, -0.206595f, 0.000000f, // Rb
-};
-
-// Decoder matrix for 7dot0, ambisonic level 1
-static constexpr float decoderMatrix_7dot0_1_hf[7*4] = {
-0.274533f, 0.199297f, 0.171391f, 0.000000f, // L
-0.274510f, 0.199294f, -0.171394f, 0.000000f, // R
-0.180812f, 0.179246f, 0.000000f, 0.000000f, // C
-0.368266f, 0.000000f, 0.337353f, 0.000000f, // Ls
-0.368304f, 0.000000f, -0.337359f, 0.000000f, // Rs
-0.368297f, -0.292160f, 0.168680f, 0.000000f, // Lb
-0.368261f, -0.292155f, -0.168684f, 0.000000f, // Rb
-};
-
-// Decoder matrix for 7dot0, ambisonic level 2
-static constexpr float decoderMatrix_7dot0_2_lf[7*9] = {
-0.194124f, 0.244088f, 0.209910f, 0.000000f, -0.054359f, 0.000000f, 0.000000f, 0.046706f, 0.289736f, // L
-0.194108f, 0.244084f, -0.209914f, 0.000000f, -0.054434f, 0.000000f, 0.000000f, 0.046694f, -0.289734f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, -0.037403f, 0.000000f, 0.000000f, 0.222021f, 0.000000f, // C
-0.260404f, 0.000000f, 0.413172f, 0.000000f, -0.071326f, 0.000000f, 0.000000f, -0.326534f, 0.000000f, // Ls
-0.260430f, 0.000000f, -0.413179f, 0.000000f, -0.071237f, 0.000000f, 0.000000f, -0.326534f, 0.000000f, // Rs
-0.260425f, -0.357822f, 0.206589f, 0.000000f, -0.071249f, 0.000000f, 0.000000f, 0.163267f, -0.282791f, // Lb
-0.260400f, -0.357815f, -0.206595f, 0.000000f, -0.071359f, 0.000000f, 0.000000f, 0.163256f, 0.282788f, // Rb
-};
-
-// Decoder matrix for 7dot0, ambisonic level 2
-static constexpr float decoderMatrix_7dot0_2_hf[7*9] = {
-0.306937f, 0.298946f, 0.257087f, 0.000000f, -0.034380f, 0.000000f, 0.000000f, 0.029540f, 0.183245f, // L
-0.306912f, 0.298940f, -0.257091f, 0.000000f, -0.034427f, 0.000000f, 0.000000f, 0.029532f, -0.183244f, // R
-0.202154f, 0.268870f, 0.000000f, 0.000000f, -0.023656f, 0.000000f, 0.000000f, 0.140418f, 0.000000f, // C
-0.411734f, 0.000000f, 0.506030f, 0.000000f, -0.045111f, 0.000000f, 0.000000f, -0.206518f, 0.000000f, // Ls
-0.411776f, 0.000000f, -0.506039f, 0.000000f, -0.045054f, 0.000000f, 0.000000f, -0.206518f, 0.000000f, // Rs
-0.411768f, -0.438240f, 0.253019f, 0.000000f, -0.045062f, 0.000000f, 0.000000f, 0.103259f, -0.178853f, // Lb
-0.411728f, -0.438232f, -0.253026f, 0.000000f, -0.045131f, 0.000000f, 0.000000f, 0.103252f, 0.178851f, // Rb
-};
-
-// Decoder matrix for 7dot0, ambisonic level 3
-static constexpr float decoderMatrix_7dot0_3_lf[7*16] = {
-0.194124f, 0.244088f, 0.209910f, 0.000000f, -0.054359f, 0.000000f, 0.000000f, 0.046706f, 0.289736f, 0.000000f, 0.031236f, 0.027345f, 0.000000f, 0.000000f, -0.110659f, 0.194894f, // L
-0.194108f, 0.244084f, -0.209914f, 0.000000f, -0.054434f, 0.000000f, 0.000000f, 0.046694f, -0.289734f, 0.000000f, 0.031217f, -0.027307f, 0.000000f, 0.000000f, -0.110659f, -0.194879f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, -0.037403f, 0.000000f, 0.000000f, 0.222021f, 0.000000f, 0.000000f, 0.026162f, 0.000000f, 0.000000f, 0.000000f, 0.204779f, 0.000000f, // C
-0.260404f, 0.000000f, 0.413172f, 0.000000f, -0.071326f, 0.000000f, 0.000000f, -0.326534f, 0.000000f, 0.000000f, 0.000000f, 0.055313f, 0.000000f, 0.000000f, 0.000000f, -0.185006f, // Ls
-0.260430f, 0.000000f, -0.413179f, 0.000000f, -0.071237f, 0.000000f, 0.000000f, -0.326534f, 0.000000f, 0.000000f, 0.000000f, -0.055349f, 0.000000f, 0.000000f, 0.000000f, 0.185008f, // Rs
-0.260425f, -0.357822f, 0.206589f, 0.000000f, -0.071249f, 0.000000f, 0.000000f, 0.163267f, -0.282791f, 0.000000f, -0.047935f, 0.027670f, 0.000000f, 0.000000f, 0.000000f, 0.185014f, // Lb
-0.260400f, -0.357815f, -0.206595f, 0.000000f, -0.071359f, 0.000000f, 0.000000f, 0.163256f, 0.282788f, 0.000000f, -0.047893f, -0.027649f, 0.000000f, 0.000000f, 0.000000f, -0.184996f, // Rb
-};
-
-// Decoder matrix for 7dot0, ambisonic level 3
-static constexpr float decoderMatrix_7dot0_3_hf[7*16] = {
-0.323835f, 0.350641f, 0.301543f, 0.000000f, -0.055527f, 0.000000f, 0.000000f, 0.047710f, 0.295961f, 0.000000f, 0.015880f, 0.013901f, 0.000000f, 0.000000f, -0.056256f, 0.099079f, // L
-0.323808f, 0.350635f, -0.301549f, 0.000000f, -0.055604f, 0.000000f, 0.000000f, 0.047697f, -0.295959f, 0.000000f, 0.015870f, -0.013882f, 0.000000f, 0.000000f, -0.056256f, -0.099072f, // R
-0.213283f, 0.315364f, 0.000000f, 0.000000f, -0.038207f, 0.000000f, 0.000000f, 0.226791f, 0.000000f, 0.000000f, 0.013300f, 0.000000f, 0.000000f, 0.000000f, 0.104104f, 0.000000f, // C
-0.434401f, 0.000000f, 0.593535f, 0.000000f, -0.072859f, 0.000000f, 0.000000f, -0.333550f, 0.000000f, 0.000000f, 0.000000f, 0.028120f, 0.000000f, 0.000000f, 0.000000f, -0.094052f, // Ls
-0.434446f, 0.000000f, -0.593546f, 0.000000f, -0.072768f, 0.000000f, 0.000000f, -0.333550f, 0.000000f, 0.000000f, 0.000000f, -0.028138f, 0.000000f, 0.000000f, 0.000000f, 0.094053f, // Rs
-0.434437f, -0.514023f, 0.296773f, 0.000000f, -0.072780f, 0.000000f, 0.000000f, 0.166775f, -0.288867f, 0.000000f, -0.024369f, 0.014067f, 0.000000f, 0.000000f, 0.000000f, 0.094056f, // Lb
-0.434395f, -0.514014f, -0.296781f, 0.000000f, -0.072892f, 0.000000f, 0.000000f, 0.166764f, 0.288864f, 0.000000f, -0.024348f, -0.014056f, 0.000000f, 0.000000f, 0.000000f, -0.094047f, // Rb
-};
-
-// Decoder matrix for 7dot1, ambisonic level 1
-static constexpr float decoderMatrix_7dot1_1_lf[8*4] = {
-0.194124f, 0.244088f, 0.209910f, 0.000000f, // L
-0.194108f, 0.244084f, -0.209914f, 0.000000f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, // C
-0.5f, 0.0f, 0.0f, 0.0f, // LFE
-0.260404f, 0.000000f, 0.413172f, 0.000000f, // Ls
-0.260430f, 0.000000f, -0.413179f, 0.000000f, // Rs
-0.260425f, -0.357822f, 0.206589f, 0.000000f, // Lb
-0.260400f, -0.357815f, -0.206595f, 0.000000f, // Rb
-};
-
-// Decoder matrix for 7dot1, ambisonic level 1
-static constexpr float decoderMatrix_7dot1_1_hf[8*4] = {
-0.274533f, 0.199297f, 0.171391f, 0.000000f, // L
-0.274510f, 0.199294f, -0.171394f, 0.000000f, // R
-0.180812f, 0.179246f, 0.000000f, 0.000000f, // C
-0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.368266f, 0.000000f, 0.337353f, 0.000000f, // Ls
-0.368304f, 0.000000f, -0.337359f, 0.000000f, // Rs
-0.368297f, -0.292160f, 0.168680f, 0.000000f, // Lb
-0.368261f, -0.292155f, -0.168684f, 0.000000f, // Rb
-};
-
-// Decoder matrix for 7dot1, ambisonic level 2
-static constexpr float decoderMatrix_7dot1_2_lf[8*9] = {
-0.194124f, 0.244088f, 0.209910f, 0.000000f, -0.054359f, 0.000000f, 0.000000f, 0.046706f, 0.289736f, // L
-0.194108f, 0.244084f, -0.209914f, 0.000000f, -0.054434f, 0.000000f, 0.000000f, 0.046694f, -0.289734f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, -0.037403f, 0.000000f, 0.000000f, 0.222021f, 0.000000f, // C
-0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.260404f, 0.000000f, 0.413172f, 0.000000f, -0.071326f, 0.000000f, 0.000000f, -0.326534f, 0.000000f, // Ls
-0.260430f, 0.000000f, -0.413179f, 0.000000f, -0.071237f, 0.000000f, 0.000000f, -0.326534f, 0.000000f, // Rs
-0.260425f, -0.357822f, 0.206589f, 0.000000f, -0.071249f, 0.000000f, 0.000000f, 0.163267f, -0.282791f, // Lb
-0.260400f, -0.357815f, -0.206595f, 0.000000f, -0.071359f, 0.000000f, 0.000000f, 0.163256f, 0.282788f, // Rb
-};
-
-// Decoder matrix for 7dot1, ambisonic level 2
-static constexpr float decoderMatrix_7dot1_2_hf[8*9] = {
-0.306937f, 0.298946f, 0.257087f, 0.000000f, -0.034380f, 0.000000f, 0.000000f, 0.029540f, 0.183245f, // L
-0.306912f, 0.298940f, -0.257091f, 0.000000f, -0.034427f, 0.000000f, 0.000000f, 0.029532f, -0.183244f, // R
-0.202154f, 0.268870f, 0.000000f, 0.000000f, -0.023656f, 0.000000f, 0.000000f, 0.140418f, 0.000000f, // C
-0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.411734f, 0.000000f, 0.506030f, 0.000000f, -0.045111f, 0.000000f, 0.000000f, -0.206518f, 0.000000f, // Ls
-0.411776f, 0.000000f, -0.506039f, 0.000000f, -0.045054f, 0.000000f, 0.000000f, -0.206518f, 0.000000f, // Rs
-0.411768f, -0.438240f, 0.253019f, 0.000000f, -0.045062f, 0.000000f, 0.000000f, 0.103259f, -0.178853f, // Lb
-0.411728f, -0.438232f, -0.253026f, 0.000000f, -0.045131f, 0.000000f, 0.000000f, 0.103252f, 0.178851f, // Rb
-};
-
-// Decoder matrix for 7dot1, ambisonic level 3
-static constexpr float decoderMatrix_7dot1_3_lf[8*16] = {
-0.194124f, 0.244088f, 0.209910f, 0.000000f, -0.054359f, 0.000000f, 0.000000f, 0.046706f, 0.289736f, 0.000000f, 0.031236f, 0.027345f, 0.000000f, 0.000000f, -0.110659f, 0.194894f, // L
-0.194108f, 0.244084f, -0.209914f, 0.000000f, -0.054434f, 0.000000f, 0.000000f, 0.046694f, -0.289734f, 0.000000f, 0.031217f, -0.027307f, 0.000000f, 0.000000f, -0.110659f, -0.194879f, // R
-0.127854f, 0.219531f, 0.000000f, 0.000000f, -0.037403f, 0.000000f, 0.000000f, 0.222021f, 0.000000f, 0.000000f, 0.026162f, 0.000000f, 0.000000f, 0.000000f, 0.204779f, 0.000000f, // C
-0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.260404f, 0.000000f, 0.413172f, 0.000000f, -0.071326f, 0.000000f, 0.000000f, -0.326534f, 0.000000f, 0.000000f, 0.000000f, 0.055313f, 0.000000f, 0.000000f, 0.000000f, -0.185006f, // Ls
-0.260430f, 0.000000f, -0.413179f, 0.000000f, -0.071237f, 0.000000f, 0.000000f, -0.326534f, 0.000000f, 0.000000f, 0.000000f, -0.055349f, 0.000000f, 0.000000f, 0.000000f, 0.185008f, // Rs
-0.260425f, -0.357822f, 0.206589f, 0.000000f, -0.071249f, 0.000000f, 0.000000f, 0.163267f, -0.282791f, 0.000000f, -0.047935f, 0.027670f, 0.000000f, 0.000000f, 0.000000f, 0.185014f, // Lb
-0.260400f, -0.357815f, -0.206595f, 0.000000f, -0.071359f, 0.000000f, 0.000000f, 0.163256f, 0.282788f, 0.000000f, -0.047893f, -0.027649f, 0.000000f, 0.000000f, 0.000000f, -0.184996f, // Rb
-};
-
-// Decoder matrix for 7dot1, ambisonic level 3
-static constexpr float decoderMatrix_7dot1_3_hf[8*16] = {
-0.323835f, 0.350641f, 0.301543f, 0.000000f, -0.055527f, 0.000000f, 0.000000f, 0.047710f, 0.295961f, 0.000000f, 0.015880f, 0.013901f, 0.000000f, 0.000000f, -0.056256f, 0.099079f, // L
-0.323808f, 0.350635f, -0.301549f, 0.000000f, -0.055604f, 0.000000f, 0.000000f, 0.047697f, -0.295959f, 0.000000f, 0.015870f, -0.013882f, 0.000000f, 0.000000f, -0.056256f, -0.099072f, // R
-0.213283f, 0.315364f, 0.000000f, 0.000000f, -0.038207f, 0.000000f, 0.000000f, 0.226791f, 0.000000f, 0.000000f, 0.013300f, 0.000000f, 0.000000f, 0.000000f, 0.104104f, 0.000000f, // C
-0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
-0.434401f, 0.000000f, 0.593535f, 0.000000f, -0.072859f, 0.000000f, 0.000000f, -0.333550f, 0.000000f, 0.000000f, 0.000000f, 0.028120f, 0.000000f, 0.000000f, 0.000000f, -0.094052f, // Ls
-0.434446f, 0.000000f, -0.593546f, 0.000000f, -0.072768f, 0.000000f, 0.000000f, -0.333550f, 0.000000f, 0.000000f, 0.000000f, -0.028138f, 0.000000f, 0.000000f, 0.000000f, 0.094053f, // Rs
-0.434437f, -0.514023f, 0.296773f, 0.000000f, -0.072780f, 0.000000f, 0.000000f, 0.166775f, -0.288867f, 0.000000f, -0.024369f, 0.014067f, 0.000000f, 0.000000f, 0.000000f, 0.094056f, // Lb
-0.434395f, -0.514014f, -0.296781f, 0.000000f, -0.072892f, 0.000000f, 0.000000f, 0.166764f, 0.288864f, 0.000000f, -0.024348f, -0.014056f, 0.000000f, 0.000000f, 0.000000f, -0.094047f, // Rb
-};
-
-QT_END_NAMESPACE
-
-#endif
-
diff --git a/src/multimedia/spatial/qspatialaudioengine.cpp b/src/multimedia/spatial/qspatialaudioengine.cpp
deleted file mode 100644
index 7ab89c1fa..000000000
--- a/src/multimedia/spatial/qspatialaudioengine.cpp
+++ /dev/null
@@ -1,627 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qspatialaudioengine_p.h>
-#include <qspatialaudiosoundsource_p.h>
-#include <qspatialaudiostereosource.h>
-#include <qspatialaudioroom_p.h>
-#include <qspatialaudiolistener.h>
-#include <resonance_audio_api_extensions.h>
-#include <qambisonicdecoder_p.h>
-#include <qaudiodecoder.h>
-#include <qmediadevices.h>
-#include <qiodevice.h>
-#include <qaudiosink.h>
-#include <qdebug.h>
-#include <qelapsedtimer.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAudioOutputStream : public QIODevice
-{
- Q_OBJECT
-public:
- explicit QAudioOutputStream(QSpatialAudioEnginePrivate *d)
- : d(d)
- {
- open(QIODevice::ReadOnly);
- }
- ~QAudioOutputStream();
-
- qint64 readData(char *data, qint64 len) override;
-
- qint64 writeData(const char *, qint64) override;
-
- qint64 size() const override { return 0; }
- qint64 bytesAvailable() const override {
- return std::numeric_limits<qint64>::max();
- }
- bool isSequential() const override {
- return true;
- }
- bool atEnd() const override {
- return false;
- }
- qint64 pos() const override {
- return m_pos;
- }
-
- Q_INVOKABLE void startOutput() {
- QMutexLocker l(&d->mutex);
- Q_ASSERT(!sink);
- d->ambisonicDecoder.reset(new QAmbisonicDecoder(QAmbisonicDecoder::HighQuality, d->format));
- sink.reset(new QAudioSink(d->device, d->format));
- sink->setBufferSize(16384);
- sink->start(this);
- }
-
- Q_INVOKABLE void stopOutput() {
- sink->stop();
- sink.reset();
- d->ambisonicDecoder.reset();
- delete this;
- }
-
- void setPaused(bool paused) {
- if (paused)
- sink->suspend();
- else
- sink->resume();
- }
-
-private:
- qint64 m_pos = 0;
- QSpatialAudioEnginePrivate *d = nullptr;
- std::unique_ptr<QAudioSink> sink;
-};
-
-
-QAudioOutputStream::~QAudioOutputStream()
-{
-}
-
-qint64 QAudioOutputStream::writeData(const char *, qint64)
-{
- return 0;
-}
-
-qint64 QAudioOutputStream::readData(char *data, qint64 len)
-{
- if (d->paused.loadRelaxed())
- return 0;
-
- d->updateRooms();
-
- int nChannels = d->ambisonicDecoder ? d->ambisonicDecoder->nOutputChannels() : 2;
- if (len < nChannels*int(sizeof(float))*QSpatialAudioEnginePrivate::bufferSize)
- return 0;
-
- short *fd = (short *)data;
- qint64 frames = len / nChannels / sizeof(short);
- bool ok = true;
- while (frames >= qint64(QSpatialAudioEnginePrivate::bufferSize)) {
- // Fill input buffers
- for (auto *source : qAsConst(d->sources)) {
- auto *sp = QSpatialAudioSoundSourcePrivate::get(source);
- float buf[QSpatialAudioEnginePrivate::bufferSize];
- sp->getBuffer(buf, QSpatialAudioEnginePrivate::bufferSize, 1);
- d->api->SetInterleavedBuffer(sp->sourceId, buf, 1, QSpatialAudioEnginePrivate::bufferSize);
- }
- for (auto *source : qAsConst(d->stereoSources)) {
- auto *sp = QSpatialAudioSound::get(source);
- float buf[2*QSpatialAudioEnginePrivate::bufferSize];
- sp->getBuffer(buf, QSpatialAudioEnginePrivate::bufferSize, 2);
- d->api->SetInterleavedBuffer(sp->sourceId, buf, 2, QSpatialAudioEnginePrivate::bufferSize);
- }
-
- if (d->ambisonicDecoder && d->outputMode == QSpatialAudioEngine::Normal && d->format.channelCount() != 2) {
- const float *channels[QAmbisonicDecoder::maxAmbisonicChannels];
- int nSamples = vraudio::getAmbisonicOutput(d->api, channels, d->ambisonicDecoder->nInputChannels());
- Q_ASSERT(d->ambisonicDecoder->nOutputChannels() <= 8);
- d->ambisonicDecoder->processBuffer(channels, fd, nSamples);
- } else {
- ok = d->api->FillInterleavedOutputBuffer(2, QSpatialAudioEnginePrivate::bufferSize, fd);
- if (!ok) {
- qWarning() << " Reading failed!";
- break;
- }
- }
- fd += nChannels*QSpatialAudioEnginePrivate::bufferSize;
- frames -= QSpatialAudioEnginePrivate::bufferSize;
- }
- const int bytesProcessed = ((char *)fd - data);
- m_pos += bytesProcessed;
- return bytesProcessed;
-}
-
-
-QSpatialAudioEnginePrivate::QSpatialAudioEnginePrivate()
-{
- device = QMediaDevices::defaultAudioOutput();
-}
-
-QSpatialAudioEnginePrivate::~QSpatialAudioEnginePrivate()
-{
- delete api;
-}
-
-void QSpatialAudioEnginePrivate::addSpatialSound(QSpatialAudioSoundSource *sound)
-{
- QSpatialAudioSound *sd = QSpatialAudioSound::get(sound);
-
- sd->sourceId = api->CreateSoundObjectSource(vraudio::kBinauralHighQuality);
- sources.append(sound);
-}
-
-void QSpatialAudioEnginePrivate::removeSpatialSound(QSpatialAudioSoundSource *sound)
-{
- QSpatialAudioSound *sd = QSpatialAudioSound::get(sound);
-
- api->DestroySource(sd->sourceId);
- sd->sourceId = vraudio::ResonanceAudioApi::kInvalidSourceId;
- sources.removeOne(sound);
-}
-
-void QSpatialAudioEnginePrivate::addStereoSound(QSpatialAudioStereoSource *sound)
-{
- QSpatialAudioSound *sd = QSpatialAudioSound::get(sound);
-
- sd->sourceId = api->CreateStereoSource(2);
- stereoSources.append(sound);
-}
-
-void QSpatialAudioEnginePrivate::removeStereoSound(QSpatialAudioStereoSource *sound)
-{
- QSpatialAudioSound *sd = QSpatialAudioSound::get(sound);
-
- api->DestroySource(sd->sourceId);
- sd->sourceId = vraudio::ResonanceAudioApi::kInvalidSourceId;
- stereoSources.removeOne(sound);
-}
-
-void QSpatialAudioEnginePrivate::addRoom(QSpatialAudioRoom *room)
-{
- rooms.append(room);
-}
-
-void QSpatialAudioEnginePrivate::removeRoom(QSpatialAudioRoom *room)
-{
- rooms.removeOne(room);
-}
-
-void QSpatialAudioEnginePrivate::updateRooms()
-{
- if (!roomEffectsEnabled)
- return;
-
- bool needUpdate = listenerPositionDirty;
- listenerPositionDirty = false;
-
- bool roomDirty = false;
- for (const auto &room : rooms) {
- auto *rd = QSpatialAudioRoomPrivate::get(room);
- if (rd->dirty) {
- roomDirty = true;
- rd->update();
- needUpdate = true;
- }
- }
-
- if (!needUpdate)
- return;
-
- QVector3D listenerPos = listenerPosition();
- float roomVolume = float(qInf());
- QSpatialAudioRoom *room = nullptr;
- // Find the smallest room that contains the listener and apply it's room effects
- for (auto *r : qAsConst(rooms)) {
- QVector3D dim2 = r->dimensions()/2.;
- float vol = dim2.x()*dim2.y()*dim2.z();
- if (vol > roomVolume)
- continue;
- QVector3D dist = r->position() - listenerPos;
- // transform into room coordinates
- dist = r->rotation().rotatedVector(dist);
- if (qAbs(dist.x()) <= dim2.x() &&
- qAbs(dist.y()) <= dim2.y() &&
- qAbs(dist.z()) <= dim2.z()) {
- room = r;
- roomVolume = vol;
- }
- }
- if (room != currentRoom)
- roomDirty = true;
- currentRoom = room;
-
- if (!roomDirty)
- return;
-
- // apply room to engine
- if (!currentRoom) {
- api->EnableRoomEffects(false);
- return;
- }
- QSpatialAudioRoomPrivate *rp = QSpatialAudioRoomPrivate::get(room);
- api->SetReflectionProperties(rp->reflections);
- api->SetReverbProperties(rp->reverb);
-
- // update room effects for all sound sources
- for (auto *s : qAsConst(sources)) {
- auto *sp = QSpatialAudioSoundSourcePrivate::get(s);
- sp->updateRoomEffects();
- }
-}
-
-QVector3D QSpatialAudioEnginePrivate::listenerPosition() const
-{
- return listener ? listener->position() : QVector3D();
-}
-
-
-/*!
- \class QSpatialAudioEngine
- \inmodule QtMultimedia
- \ingroup multimedia_spatialaudio
-
- \brief QSpatialAudioEngine manages a three dimensional sound field.
-
- You can use an instance of QSpatialAudioEngine to manage a sound field in
- three dimensions. A sound field is defined by several QSpatialAudioSoundSource
- objects that define a sound at a specified location in 3D space. You can also
- add stereo overlays using QSpatialAudioStereoSource.
-
- You can use QSpatialAudioListener to define the position of the person listening
- to the sound field relative to the sound sources. Sound sources will be less audible
- if the listener is further away from source. They will also get mapped to the corresponding
- loudspeakers depending on the direction between listener and source.
-
- QSpatialAudioEngine offers two output modes. The first mode renders the sound field to a set of
- speakers, either a stereo speaker pair or a surround configuration. The second mode provides
- an immersive 3D sound experience when using headphones.
-
- Perception of sound localization is driven mainly by two factors. The first factor is timing
- differences of the sound waves between left and right ear. The second factor comes from various
- ways how sounds coming from different direcations create different types of reflections from our
- ears and heads. See https://en.wikipedia.org/wiki/Sound_localization for more details.
-
- The spatial audio engine emulates those timing differences and reflections through
- Head related transfer functions (HRTF, see
- https://en.wikipedia.org/wiki/Head-related_transfer_function). The functions used emulates those
- effects for an average persons ears and head. It provides a good and immersive 3D sound localization
- experience for most persons when using headphones.
-
- The engine is rather versatile allowing you to define amd emulate room properties and reverb settings emulating
- different types of rooms.
-
- Sound sources can also be occluded dampening the sound coming from those sources.
-
-*/
-
-/*!
- Constructs a spatial audio engine with \a parent.
-
- The engine will operate with a sample rate given by \a sampleRate. Sound content that is
- not provided at that sample rate will automatically get resampled to \a sampleRate when
- being processed by the engine. The default sample rate is fine in most cases, but you can define
- a different rate if most of your sound files are sampled with a different rate, and avoid some
- CPU overhead for resampling.
- */
-QSpatialAudioEngine::QSpatialAudioEngine(QObject *parent, int sampleRate)
- : QObject(parent)
- , d(new QSpatialAudioEnginePrivate)
-{
- d->sampleRate = sampleRate;
- d->api = vraudio::CreateResonanceAudioApi(2, QSpatialAudioEnginePrivate::bufferSize, d->sampleRate);
-}
-
-/*!
- Destroys the spatial audio engine.
- */
-QSpatialAudioEngine::~QSpatialAudioEngine()
-{
- stop();
- delete d;
-}
-
-/*! \enum QSpatialAudioEngine::OutputMode
- \value Normal Map the sounds to the loudspeaker configuration of the output device.
- This is normally a stereo or surround speaker setup.
- \value Headphone Use Headphone spatialization to create a 3D audio effect when listening
- to the sound field through headphones
-*/
-
-/*!
- \property QSpatialAudioEngine::outputMode
-
- Sets or retrieves the current output mode of the engine.
-
- \sa QSpatialAudioEngine::OutputMode
- */
-void QSpatialAudioEngine::setOutputMode(OutputMode mode)
-{
- if (d->outputMode == mode)
- return;
- d->outputMode = mode;
- if (d->api) {
- d->api->SetStereoSpeakerMode(mode == Normal);
- }
- emit outputModeChanged();
-}
-
-QSpatialAudioEngine::OutputMode QSpatialAudioEngine::outputMode() const
-{
- return d->outputMode;
-}
-
-/*!
- Returns the sample rate the engine has been configured with.
- */
-int QSpatialAudioEngine::sampleRate() const
-{
- return d->sampleRate;
-}
-
-/*!
- \property QSpatialAudioEngine::outputDevice
-
- Sets or returns the device that is being used for playing the sound field.
- */
-void QSpatialAudioEngine::setOutputDevice(const QAudioDevice &device)
-{
- if (d->device == device)
- return;
- if (d->api) {
- qWarning() << "Changing device on a running engine not implemented";
- return;
- }
- d->device = device;
- emit outputDeviceChanged();
-}
-
-QAudioDevice QSpatialAudioEngine::outputDevice() const
-{
- return d->device;
-}
-
-/*!
- \property QSpatialAudioEngine::masterVolume
-
- Sets or returns volume being used to render the sound field.
- */
-void QSpatialAudioEngine::setMasterVolume(float volume)
-{
- if (d->masterVolume == volume)
- return;
- d->masterVolume = volume;
- d->api->SetMasterVolume(volume);
- emit masterVolumeChanged();
-}
-
-float QSpatialAudioEngine::masterVolume() const
-{
- return d->masterVolume;
-}
-
-/*!
- Starts the engine.
- */
-void QSpatialAudioEngine::start()
-{
- if (d->outputStream)
- // already started
- return;
-
- d->format.setChannelCount(2);
- d->format.setSampleRate(d->sampleRate);
- d->format.setSampleFormat(QAudioFormat::Int16);
-
- d->api->SetStereoSpeakerMode(d->outputMode == Normal);
- d->api->SetMasterVolume(d->masterVolume);
-
- d->outputStream.reset(new QAudioOutputStream(d));
- d->outputStream->moveToThread(&d->audioThread);
- d->audioThread.start();
-
- QMetaObject::invokeMethod(d->outputStream.get(), "startOutput");
-}
-
-/*!
- Stops the engine.
- */
-void QSpatialAudioEngine::stop()
-{
- QMetaObject::invokeMethod(d->outputStream.get(), "stopOutput", Qt::BlockingQueuedConnection);
- d->outputStream.reset();
- d->audioThread.exit(0);
- d->audioThread.wait();
- delete d->api;
- d->api = nullptr;
-}
-
-/*!
- \property QSpatialAudioEngine::paused
-
- Pauses the spatial audio engine.
- */
-void QSpatialAudioEngine::setPaused(bool paused)
-{
- bool old = d->paused.fetchAndStoreRelaxed(paused);
- if (old != paused) {
- if (d->outputStream)
- d->outputStream->setPaused(paused);
- emit pausedChanged();
- }
-}
-
-bool QSpatialAudioEngine::paused() const
-{
- return d->paused.loadRelaxed();
-}
-
-/*!
- Enables room effects such as echos and reverb.
-
- Enables room effects if \a enabled is true.
- Room effects will only apply if you create one or more \l QSpatialAudioRoom objects
- and the listener is inside at least one of the rooms. If the listener is inside
- multiple rooms, the room with the smallest volume will be used.
- */
-void QSpatialAudioEngine::setRoomEffectsEnabled(bool enabled)
-{
- if (d->roomEffectsEnabled == enabled)
- return;
- d->roomEffectsEnabled = enabled;
-}
-
-/*!
- Returns true if room effects are enabled.
- */
-bool QSpatialAudioEngine::roomEffectsEnabled() const
-{
- return d->roomEffectsEnabled;
-}
-
-/*!
- \property QSpatialAudioEngine::distanceScale
-
- Defines the scale of the coordinate system being used by the spatial audio engine.
- By default, all units are in centimeters, in line with the default units being
- used by Qt Quick 3D.
-
- Set the distance scale to QSpatialAudioEngine::DistanceScaleMeter to get units in meters.
-*/
-void QSpatialAudioEngine::setDistanceScale(float scale)
-{
- // multiply with 100, to get the conversion to meters that resonance audio uses
- scale /= 100.f;
- if (scale <= 0.0f) {
- qWarning() << "QSpatialAudioEngine: Invalid distance scale.";
- return;
- }
- if (scale == d->distanceScale)
- return;
- d->distanceScale = scale;
- emit distanceScaleChanged();
-}
-
-float QSpatialAudioEngine::distanceScale() const
-{
- return d->distanceScale*100.f;
-}
-
-
-/*! \class QSpatialAudioSound
- \internal
- */
-
-void QSpatialAudioSound::load()
-{
- decoder.reset(new QAudioDecoder);
- buffers.clear();
- currentBuffer = 0;
- bufPos = 0;
- m_playing = false;
- m_loading = true;
- auto *ep = QSpatialAudioEnginePrivate::get(engine);
- QAudioFormat f = ep->format;
- f.setSampleFormat(QAudioFormat::Float);
- f.setChannelConfig(nchannels == 2 ? QAudioFormat::ChannelConfigStereo : QAudioFormat::ChannelConfigMono);
- decoder->setAudioFormat(f);
- decoder->setSource(url);
-
- connect(decoder.get(), &QAudioDecoder::bufferReady, this, &QSpatialAudioSound::bufferReady);
- connect(decoder.get(), &QAudioDecoder::finished, this, &QSpatialAudioSound::finished);
- decoder->start();
-}
-
-void QSpatialAudioSound::getBuffer(float *buf, int nframes, int channels)
-{
- Q_ASSERT(channels == nchannels);
- QMutexLocker l(&mutex);
- if (!m_playing || currentBuffer >= buffers.size()) {
- memset(buf, 0, nframes*sizeof(float));
- } else {
- int frames = nframes;
- float *ff = buf;
- while (frames) {
- const QAudioBuffer &b = buffers.at(currentBuffer);
-// qDebug() << s << b.format().sampleRate() << b.format().channelCount() << b.format().sampleFormat();
- auto *f = b.constData<float>() + bufPos*nchannels;
- int toCopy = qMin(b.frameCount() - bufPos, frames);
- memcpy(ff, f, toCopy*sizeof(float)*nchannels);
- ff += toCopy*nchannels;
- frames -= toCopy;
- bufPos += toCopy;
- Q_ASSERT(bufPos <= b.frameCount());
- if (bufPos == b.frameCount()) {
- ++currentBuffer;
- bufPos = 0;
- }
- if (!m_loading) {
- if (currentBuffer == buffers.size()) {
- currentBuffer = 0;
- ++m_currentLoop;
- }
- if (m_loops > 0 && m_currentLoop >= m_loops) {
- m_playing = false;
- m_currentLoop = 0;
- }
- }
- }
- Q_ASSERT(ff - buf == channels*nframes);
- }
-}
-
-void QSpatialAudioSound::bufferReady()
-{
- QMutexLocker l(&mutex);
- auto b = decoder->read();
-// qDebug() << "read buffer" << b.format() << b.startTime() << b.duration();
- buffers.append(b);
- if (m_autoPlay)
- m_playing = true;
-}
-
-void QSpatialAudioSound::finished()
-{
-// qDebug() << "finished";
- m_loading = false;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qspatialaudioengine.cpp"
-#include "qspatialaudioengine.moc"
diff --git a/src/multimedia/spatial/qspatialaudioengine.h b/src/multimedia/spatial/qspatialaudioengine.h
deleted file mode 100644
index f67ce5726..000000000
--- a/src/multimedia/spatial/qspatialaudioengine.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSPATIALAUDIOENGINE_H
-#define QSPATIALAUDIOENGINE_H
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtCore/qobject.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioEnginePrivate;
-class QAudioDevice;
-
-class Q_MULTIMEDIA_EXPORT QSpatialAudioEngine : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(OutputMode outputMode READ outputMode WRITE setOutputMode NOTIFY outputModeChanged)
- Q_PROPERTY(QAudioDevice outputDevice READ outputDevice WRITE setOutputDevice NOTIFY outputDeviceChanged)
- Q_PROPERTY(float masterVolume READ masterVolume WRITE setMasterVolume NOTIFY masterVolumeChanged)
- Q_PROPERTY(bool paused READ paused WRITE setPaused NOTIFY pausedChanged)
- Q_PROPERTY(float distanceScale READ distanceScale WRITE setDistanceScale NOTIFY distanceScaleChanged)
-public:
- explicit QSpatialAudioEngine(QObject *parent = nullptr, int sampleRate = 44100);
- ~QSpatialAudioEngine();
-
- enum OutputMode {
- Normal,
- Headphone
- };
- Q_ENUM(OutputMode)
-
- void setOutputMode(OutputMode mode);
- OutputMode outputMode() const;
-
- int sampleRate() const;
-
- void setOutputDevice(const QAudioDevice &device);
- QAudioDevice outputDevice() const;
-
- void setMasterVolume(float volume);
- float masterVolume() const;
-
- void start();
- void stop();
-
- void setPaused(bool paused);
- bool paused() const;
-
- void setRoomEffectsEnabled(bool enabled);
- bool roomEffectsEnabled() const;
-
- static constexpr float DistanceScaleCentimeter = 1.f;
- static constexpr float DistanceScaleMeter = 100.f;
-
- void setDistanceScale(float scale);
- float distanceScale() const;
-
-Q_SIGNALS:
- void outputModeChanged();
- void outputDeviceChanged();
- void masterVolumeChanged();
- void pausedChanged();
- void distanceScaleChanged();
-
-public Q_SLOTS:
- void pause() { setPaused(true); }
- void resume() { setPaused(false); }
-
-private:
- friend class QSpatialAudioEnginePrivate;
- QSpatialAudioEnginePrivate *d;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimedia/spatial/qspatialaudioengine_p.h b/src/multimedia/spatial/qspatialaudioengine_p.h
deleted file mode 100644
index 3163f0c86..000000000
--- a/src/multimedia/spatial/qspatialaudioengine_p.h
+++ /dev/null
@@ -1,179 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSPATIALAUDIOENGINE_P_H
-#define QSPATIALAUDIOENGINE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <qspatialaudioengine.h>
-#include <qaudiodevice.h>
-#include <qaudiodecoder.h>
-#include <qthread.h>
-#include <qmutex.h>
-#include <qurl.h>
-#include <qaudiobuffer.h>
-#include <qvector3d.h>
-
-namespace vraudio {
-class ResonanceAudioApi;
-}
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioSoundSource;
-class QSpatialAudioStereoSource;
-class QAudioSink;
-class QAudioOutputStream;
-class QAmbisonicDecoder;
-class QAudioDecoder;
-class QSpatialAudioRoom;
-class QSpatialAudioListener;
-
-class QSpatialAudioEnginePrivate
-{
-public:
- static QSpatialAudioEnginePrivate *get(QSpatialAudioEngine *engine) { return engine ? engine->d : nullptr; }
-
- static constexpr int bufferSize = 128;
-
- QSpatialAudioEnginePrivate();
- ~QSpatialAudioEnginePrivate();
- vraudio::ResonanceAudioApi *api = nullptr;
- int sampleRate = 44100;
- float masterVolume = 1.;
- QSpatialAudioEngine::OutputMode outputMode = QSpatialAudioEngine::Normal;
- bool roomEffectsEnabled = true;
-
- // Resonance Audio uses meters internally, while Qt Quick 3D and our API uses cm by default.
- // To make things independent from the scale setting, we store all distances in meters internally
- // and convert in the setters and getters.
- float distanceScale = 0.01f;
-
- QMutex mutex;
- QAudioFormat format;
- QAudioDevice device;
- QAtomicInteger<bool> paused = false;
-
- QThread audioThread;
- std::unique_ptr<QAudioOutputStream> outputStream;
- std::unique_ptr<QAmbisonicDecoder> ambisonicDecoder;
-
- QSpatialAudioListener *listener = nullptr;
- QList<QSpatialAudioSoundSource *> sources;
- QList<QSpatialAudioStereoSource *> stereoSources;
- QList<QSpatialAudioRoom *> rooms;
- mutable bool listenerPositionDirty = true;
- QSpatialAudioRoom *currentRoom = nullptr;
-
- void addSpatialSound(QSpatialAudioSoundSource *sound);
- void removeSpatialSound(QSpatialAudioSoundSource *sound);
- void addStereoSound(QSpatialAudioStereoSource *sound);
- void removeStereoSound(QSpatialAudioStereoSource *sound);
-
- void addRoom(QSpatialAudioRoom *room);
- void removeRoom(QSpatialAudioRoom *room);
- void updateRooms();
-
- QVector3D listenerPosition() const;
-};
-
-class QSpatialAudioSound : public QObject
-{
-public:
- QSpatialAudioSound(QObject *parent, int nchannels = 2)
- : QObject(parent)
- , nchannels(nchannels)
- {}
-
- template<typename T>
- static QSpatialAudioSound *get(T *soundSource) { return soundSource ? soundSource->d : nullptr; }
-
-
- QUrl url;
- float volume = 1.;
- int nchannels = 2;
- std::unique_ptr<QAudioDecoder> decoder;
- QSpatialAudioEngine *engine = nullptr;
-
- QMutex mutex;
- int currentBuffer = 0;
- int bufPos = 0;
- int m_currentLoop = 0;
- QList<QAudioBuffer> buffers;
- int sourceId = -1; // kInvalidSourceId
-
- QAtomicInteger<bool> m_autoPlay = true;
- QAtomicInteger<bool> m_playing = false;
- QAtomicInt m_loops = 1;
- bool m_loading = false;
-
- void play() {
- m_playing = true;
- }
- void pause() {
- m_playing = false;
- }
- void stop() {
- QMutexLocker locker(&mutex);
- m_playing = false;
- currentBuffer = 0;
- bufPos = 0;
- m_currentLoop = 0;
- }
-
- void load();
- void getBuffer(float *buf, int frames, int channels);
-
-private Q_SLOTS:
- void bufferReady();
- void finished();
-
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimedia/spatial/qspatialaudiolistener.cpp b/src/multimedia/spatial/qspatialaudiolistener.cpp
deleted file mode 100644
index c93e7a1e5..000000000
--- a/src/multimedia/spatial/qspatialaudiolistener.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "qspatialaudiolistener.h"
-#include "qspatialaudioengine_p.h"
-#include "api/resonance_audio_api.h"
-#include <qaudiosink.h>
-#include <qurl.h>
-#include <qdebug.h>
-#include <qaudiodecoder.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioListenerPrivate
-{
-public:
- QSpatialAudioEngine *engine = nullptr;
- QVector3D pos;
- QQuaternion rotation;
-};
-
-/*!
- \class QSpatialAudioListener
- \inmodule QtMultimedia
- \ingroup multimedia_spatialaudio
-
- \brief Defines the position and orientation of the person listening to a sound field
- defined by QSpatialAudioEngine.
-
- A QSpatialAudioEngine can have exactly one listener that defines the position and orientation
- of the person listening to the sound field.
- */
-
-/*!
- Creates a listener for the spatial audio engine for \a engine.
- */
-QSpatialAudioListener::QSpatialAudioListener(QSpatialAudioEngine *engine)
- : d(new QSpatialAudioListenerPrivate)
-{
- setEngine(engine);
-}
-
-/*!
- Destroys the listener.
- */
-QSpatialAudioListener::~QSpatialAudioListener()
-{
- delete d;
-}
-
-/*!
- Sets the listener's position in 3D space to \a pos. Units are in centimeters
- by default.
-
- \sa QSpatialAudioEngine::distanceScale
- */
-void QSpatialAudioListener::setPosition(QVector3D pos)
-{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
- pos *= ep->distanceScale;
- if (d->pos == pos)
- return;
-
- d->pos = pos;
- if (ep && ep->api) {
- ep->api->SetHeadPosition(pos.x(), pos.y(), pos.z());
- ep->listenerPositionDirty = true;
- }
-}
-
-/*!
- Returns the current position of the listener.
- */
-QVector3D QSpatialAudioListener::position() const
-{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
- return d->pos/ep->distanceScale;
-}
-
-/*!
- Sets the listener's orientation in 3D space to \a q.
- */
-void QSpatialAudioListener::setRotation(const QQuaternion &q)
-{
- d->rotation = q;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
- if (ep && ep->api)
- ep->api->SetHeadRotation(d->rotation.x(), d->rotation.y(), d->rotation.z(), d->rotation.scalar());
-}
-
-/*!
- Returns the listener's orientation in 3D space.
- */
-QQuaternion QSpatialAudioListener::rotation() const
-{
- return d->rotation;
-}
-
-/*!
- \internal
- */
-void QSpatialAudioListener::setEngine(QSpatialAudioEngine *engine)
-{
- if (d->engine) {
- auto *ed = QSpatialAudioEnginePrivate::get(d->engine);
- ed->listener = nullptr;
- }
- d->engine = engine;
- if (d->engine) {
- auto *ed = QSpatialAudioEnginePrivate::get(d->engine);
- if (ed->listener) {
- qWarning() << "Ignoring attempt to add a second listener to the spatial audio engine.";
- d->engine = nullptr;
- return;
- }
- ed->listener = this;
- }
-}
-
-/*!
- Returns the engine associated with this listener.
- */
-QSpatialAudioEngine *QSpatialAudioListener::engine() const
-{
- return d->engine;
-}
-
-QT_END_NAMESPACE
diff --git a/src/multimedia/spatial/qspatialaudiolistener.h b/src/multimedia/spatial/qspatialaudiolistener.h
deleted file mode 100644
index 715223521..000000000
--- a/src/multimedia/spatial/qspatialaudiolistener.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QSPATIALAUDIOLISTENER_H
-#define QSPATIALAUDIOLISTENER_H
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtCore/QObject>
-#include <QtMultimedia/qaudioformat.h>
-#include <QtGui/qvector3d.h>
-#include <QtGui/qquaternion.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioEngine;
-
-class QSpatialAudioListenerPrivate;
-class Q_MULTIMEDIA_EXPORT QSpatialAudioListener : public QObject
-{
-public:
- explicit QSpatialAudioListener(QSpatialAudioEngine *engine);
- ~QSpatialAudioListener();
-
- QAudioFormat format() const;
-
- void setPosition(QVector3D pos);
- QVector3D position() const;
- void setRotation(const QQuaternion &q);
- QQuaternion rotation() const;
-
- QSpatialAudioEngine *engine() const;
-
-private:
- void setEngine(QSpatialAudioEngine *engine);
- QSpatialAudioListenerPrivate *d = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimedia/spatial/qspatialaudioroom_p.h b/src/multimedia/spatial/qspatialaudioroom_p.h
deleted file mode 100644
index 668570eef..000000000
--- a/src/multimedia/spatial/qspatialaudioroom_p.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QSPATIALAUDIOROOM_P_H
-#define QSPATIALAUDIOROOM_P_H
-
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <qspatialaudioroom.h>
-#include <qspatialaudioengine_p.h>
-#include <QtGui/qquaternion.h>
-
-#include <resonance_audio_api_extensions.h>
-#include "platforms/common/room_effects_utils.h"
-#include "platforms/common/room_properties.h"
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioRoomPrivate
-{
-public:
- static QSpatialAudioRoomPrivate *get(const QSpatialAudioRoom *r) { return r->d; }
-
- QSpatialAudioEngine *engine = nullptr;
- vraudio::RoomProperties roomProperties;
- bool dirty = true;
-
- vraudio::ReverbProperties reverb;
- vraudio::ReflectionProperties reflections;
-
- float m_wallOcclusion[6] = { -1.f, -1.f, -1.f, -1.f, -1.f, -1.f };
- float m_wallDampening[6] = { -1.f, -1.f, -1.f, -1.f, -1.f, -1.f };
-
- float wallOcclusion(QSpatialAudioRoom::Wall wall) const;
- float wallDampening(QSpatialAudioRoom::Wall wall) const;
-
- void update();
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimedia/spatial/qspatialaudiosoundsource_p.h b/src/multimedia/spatial/qspatialaudiosoundsource_p.h
deleted file mode 100644
index 83bd2d232..000000000
--- a/src/multimedia/spatial/qspatialaudiosoundsource_p.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSPATIALAUDIOSOUNDSOURCE_P_H
-#define QSPATIALAUDIOSOUNDSOURCE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <qspatialaudiosoundsource.h>
-#include <qspatialaudioengine_p.h>
-#include <qurl.h>
-#include <qvector3d.h>
-#include <qquaternion.h>
-#include <qaudiobuffer.h>
-#include <qaudiodevice.h>
-#include <qmutex.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAudioDecoder;
-class QSpatialAudioEnginePrivate;
-
-class QSpatialAudioSoundSourcePrivate : public QSpatialAudioSound
-{
-public:
- QSpatialAudioSoundSourcePrivate(QObject *parent)
- : QSpatialAudioSound(parent, 1)
- {}
-
- static QSpatialAudioSoundSourcePrivate *get(QSpatialAudioSoundSource *soundSource)
- { return soundSource ? soundSource->d : nullptr; }
-
- QVector3D pos;
- QQuaternion rotation;
- QSpatialAudioSoundSource::DistanceModel distanceModel = QSpatialAudioSoundSource::DistanceModel_Logarithmic;
- float size = .1f;
- float distanceCutoff = 50.f;
- float manualAttenuation = 0.f;
- float occlusionIntensity = 0.f;
- float directivity = 0.f;
- float directivityOrder = 1.f;
- float nearFieldGain = 0.f;
- float wallDampening = 1.f;
- float wallOcclusion = 0.f;
-
- void updateDistanceModel();
- void updateRoomEffects();
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimedia/spatial/qspatialaudiostereosource.cpp b/src/multimedia/spatial/qspatialaudiostereosource.cpp
deleted file mode 100644
index 61f3a47b2..000000000
--- a/src/multimedia/spatial/qspatialaudiostereosource.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "qspatialaudiostereosource.h"
-#include "qspatialaudiolistener.h"
-#include "qspatialaudioengine_p.h"
-#include "api/resonance_audio_api.h"
-#include <qaudiosink.h>
-#include <qurl.h>
-#include <qdebug.h>
-#include <qaudiodecoder.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QSpatialAudioStereoSource
- \inmodule QtMultimedia
- \ingroup multimedia_spatialaudio
-
- \brief A stereo overlay sound.
-
- QSpatialAudioStereoSource represents a position and orientation independent sound.
- It's commonly used for background sounds (e.g. music) that is supposed to be independent
- of the listeners position and orientation.
- */
-
-/*!
- Creates a stereo sound source for \a engine.
- */
-QSpatialAudioStereoSource::QSpatialAudioStereoSource(QSpatialAudioEngine *engine)
- : d(new QSpatialAudioSound(this))
-{
- setEngine(engine);
-}
-
-QSpatialAudioStereoSource::~QSpatialAudioStereoSource()
-{
- setEngine(nullptr);
- delete d;
-}
-
-/*!
- \property QSpatialAudioStereoSource::volume
-
- Defines the volume of the sound.
-
- Values between 0 and 1 will attenuate the sound, while values above 1
- provide an additional gain boost.
- */
-void QSpatialAudioStereoSource::setVolume(float volume)
-{
- if (d->volume == volume)
- return;
- d->volume = volume;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
- if (ep)
- ep->api->SetSourceVolume(d->sourceId, d->volume);
- emit volumeChanged();
-}
-
-float QSpatialAudioStereoSource::volume() const
-{
- return d->volume;
-}
-
-void QSpatialAudioStereoSource::setSource(const QUrl &url)
-{
- if (d->url == url)
- return;
- d->url = url;
-
- d->load();
- emit sourceChanged();
-}
-
-/*!
- \property QSpatialAudioStereoSource::source
-
- The source file for the sound to be played.
- */
-QUrl QSpatialAudioStereoSource::source() const
-{
- return d->url;
-}
-
-/*!
- \property QSpatialAudioStereoSource::loops
-
- Determines how many times the sound is played before the player stops.
- Set to QSpatialAudioSoundSource::Infinite to play the current sound in
- a loop forever.
-
- The default value is \c 1.
- */
-int QSpatialAudioStereoSource::loops() const
-{
- return d->m_loops.loadRelaxed();
-}
-
-void QSpatialAudioStereoSource::setLoops(int loops)
-{
- int oldLoops = d->m_loops.fetchAndStoreRelaxed(loops);
- if (oldLoops != loops)
- emit loopsChanged();
-}
-
-/*!
- \property QSpatialAudioStereoSource::autoPlay
-
- Determines whether the sound should automatically start playing when a source
- gets specified.
-
- The default value is \c true.
- */
-bool QSpatialAudioStereoSource::autoPlay() const
-{
- return d->m_autoPlay.loadRelaxed();
-}
-
-void QSpatialAudioStereoSource::setAutoPlay(bool autoPlay)
-{
- bool old = d->m_autoPlay.fetchAndStoreRelaxed(autoPlay);
- if (old != autoPlay)
- emit autoPlayChanged();
-}
-
-/*!
- Starts playing back the sound. Does nothing if the sound is already playing.
- */
-void QSpatialAudioStereoSource::play()
-{
- d->play();
-}
-
-/*!
- Pauses sound playback. Calling play() will continue playback.
- */
-void QSpatialAudioStereoSource::pause()
-{
- d->pause();
-}
-
-/*!
- Stops sound playback and resets the current position and current loop count to 0.
- Calling play() will start playback at the beginning of the sound file.
- */
-void QSpatialAudioStereoSource::stop()
-{
- d->stop();
-}
-
-/*!
- \internal
- */
-void QSpatialAudioStereoSource::setEngine(QSpatialAudioEngine *engine)
-{
- if (d->engine == engine)
- return;
- auto *ep = QSpatialAudioEnginePrivate::get(engine);
-
- if (ep)
- ep->removeStereoSound(this);
- d->engine = engine;
-
- ep = QSpatialAudioEnginePrivate::get(engine);
- if (ep) {
- ep->addStereoSound(this);
- ep->api->SetSourceVolume(d->sourceId, d->volume);
- }
-}
-
-/*!
- Returns the engine associated with this listener.
- */
-QSpatialAudioEngine *QSpatialAudioStereoSource::engine() const
-{
- return d->engine;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qspatialaudiostereosource.cpp"
diff --git a/src/multimedia/spatial/qspatialaudiostereosource.h b/src/multimedia/spatial/qspatialaudiostereosource.h
deleted file mode 100644
index 402aee9f8..000000000
--- a/src/multimedia/spatial/qspatialaudiostereosource.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QSPATIALAUDIOSTEREOSOURCE_H
-#define QSPATIALAUDIOSTEREOSOURCE_H
-
-#include <QtMultimedia/qtmultimediaglobal.h>
-#include <QtCore/QUrl>
-#include <QtCore/QObject>
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioEngine;
-class QSpatialAudioSound;
-
-class QSpatialAudioStereoSourcePrivate;
-class Q_MULTIMEDIA_EXPORT QSpatialAudioStereoSource : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
- Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)
- Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged)
- Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged)
-
-public:
- explicit QSpatialAudioStereoSource(QSpatialAudioEngine *engine);
- ~QSpatialAudioStereoSource();
-
- void setSource(const QUrl &url);
- QUrl source() const;
-
- enum Loops
- {
- Infinite = -1,
- Once = 1
- };
- Q_ENUM(Loops)
-
- int loops() const;
- void setLoops(int loops);
-
- bool autoPlay() const;
- void setAutoPlay(bool autoPlay);
-
- void setVolume(float volume);
- float volume() const;
-
- QSpatialAudioEngine *engine() const;
-
-Q_SIGNALS:
- void sourceChanged();
- void loopsChanged();
- void autoPlayChanged();
- void volumeChanged();
-
-public Q_SLOTS:
- void play();
- void pause();
- void stop();
-
-private:
- void setEngine(QSpatialAudioEngine *engine);
- friend class QSpatialAudioSound;
- QSpatialAudioSound *d = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimedia/video/qabstractvideobuffer.cpp b/src/multimedia/video/qabstractvideobuffer.cpp
index 803c09c8d..d65438855 100644
--- a/src/multimedia/video/qabstractvideobuffer.cpp
+++ b/src/multimedia/video/qabstractvideobuffer.cpp
@@ -1,46 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qabstractvideobuffer_p.h"
#include <qvariant.h>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QDebug>
@@ -136,11 +100,6 @@ QVideoFrame::HandleType QAbstractVideoBuffer::handleType() const
return m_type;
}
-std::unique_ptr<QRhiTexture> QAbstractVideoBuffer::texture(int /*plane*/) const
-{
- return {};
-}
-
/*!
Returns the QRhi instance.
*/
diff --git a/src/multimedia/video/qabstractvideobuffer_p.h b/src/multimedia/video/qabstractvideobuffer_p.h
index 7fe0d1ad9..2004e25f7 100644
--- a/src/multimedia/video/qabstractvideobuffer_p.h
+++ b/src/multimedia/video/qabstractvideobuffer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTVIDEOBUFFER_H
#define QABSTRACTVIDEOBUFFER_H
@@ -67,6 +31,13 @@ class QVariant;
class QRhi;
class QRhiTexture;
+class Q_MULTIMEDIA_EXPORT QVideoFrameTextures
+{
+public:
+ virtual ~QVideoFrameTextures() {}
+ virtual QRhiTexture *texture(uint plane) const = 0;
+};
+
class Q_MULTIMEDIA_EXPORT QAbstractVideoBuffer
{
public:
@@ -88,11 +59,12 @@ public:
virtual MapData map(QVideoFrame::MapMode mode) = 0;
virtual void unmap() = 0;
- virtual void mapTextures() {}
- virtual quint64 textureHandle(int /*plane*/) const { return 0; }
- virtual std::unique_ptr<QRhiTexture> texture(int /*plane*/) const;
+ virtual std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *) { return {}; }
+ virtual quint64 textureHandle(QRhi *, int /*plane*/) const { return 0; }
virtual QMatrix4x4 externalTextureMatrix() const { return {}; }
+
+ virtual QByteArray underlyingByteArray(int /*plane*/) const { return {}; }
protected:
QVideoFrame::HandleType m_type;
QRhi *m_rhi = nullptr;
diff --git a/src/multimedia/video/qimagevideobuffer.cpp b/src/multimedia/video/qimagevideobuffer.cpp
new file mode 100644
index 000000000..bc825004e
--- /dev/null
+++ b/src/multimedia/video/qimagevideobuffer.cpp
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qimagevideobuffer_p.h"
+#include "qvideoframeformat.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+QImage::Format fixImageFormat(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::Format_RGBA8888_Premultiplied:
+ case QImage::Format_A2BGR30_Premultiplied:
+ case QImage::Format_A2RGB30_Premultiplied:
+ case QImage::Format_RGBA64_Premultiplied:
+ case QImage::Format_RGBA16FPx4_Premultiplied:
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ return QImage::Format_ARGB32_Premultiplied;
+ case QImage::Format_ARGB32:
+ case QImage::Format_RGBA8888:
+ case QImage::Format_Alpha8:
+ case QImage::Format_RGBA64:
+ case QImage::Format_RGBA16FPx4:
+ case QImage::Format_RGBA32FPx4:
+ return QImage::Format_ARGB32;
+ case QImage::Format_Invalid:
+ return QImage::Format_Invalid;
+ default:
+ return QImage::Format_RGB32;
+ }
+}
+
+QImage fixImage(QImage image)
+{
+ if (image.format() == QImage::Format_Invalid)
+ return image;
+
+ const auto frameFormat = QVideoFrameFormat::pixelFormatFromImageFormat(image.format());
+ if (frameFormat != QVideoFrameFormat::Format_Invalid)
+ return image;
+
+ return image.convertToFormat(fixImageFormat(image.format()));
+}
+
+} // namespace
+
+QImageVideoBuffer::QImageVideoBuffer(QImage image)
+ : QAbstractVideoBuffer(QVideoFrame::NoHandle), m_image(fixImage(std::move(image)))
+{
+}
+
+QVideoFrame::MapMode QImageVideoBuffer::mapMode() const
+{
+ return m_mapMode;
+}
+
+QAbstractVideoBuffer::MapData QImageVideoBuffer::map(QVideoFrame::MapMode mode)
+{
+ MapData mapData;
+ if (m_mapMode == QVideoFrame::NotMapped && !m_image.isNull()
+ && mode != QVideoFrame::NotMapped) {
+ m_mapMode = mode;
+
+ mapData.nPlanes = 1;
+ mapData.bytesPerLine[0] = m_image.bytesPerLine();
+ mapData.data[0] = m_image.bits();
+ mapData.size[0] = m_image.sizeInBytes();
+ }
+
+ return mapData;
+}
+
+void QImageVideoBuffer::unmap()
+{
+ m_mapMode = QVideoFrame::NotMapped;
+}
+
+QImage QImageVideoBuffer::underlyingImage() const
+{
+ return m_image;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/video/qimagevideobuffer_p.h b/src/multimedia/video/qimagevideobuffer_p.h
new file mode 100644
index 000000000..e5467563a
--- /dev/null
+++ b/src/multimedia/video/qimagevideobuffer_p.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QIMAGEVIDEOBUFFER_P_H
+#define QIMAGEVIDEOBUFFER_P_H
+
+#include <private/qabstractvideobuffer_p.h>
+#include <qimage.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class Q_MULTIMEDIA_EXPORT QImageVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ QImageVideoBuffer(QImage image);
+
+ QVideoFrame::MapMode mapMode() const override;
+
+ MapData map(QVideoFrame::MapMode mode) override;
+
+ void unmap() override;
+
+ QImage underlyingImage() const;
+
+private:
+ QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped;
+ QImage m_image;
+};
+
+QT_END_NAMESPACE
+
+#endif // QIMAGEVIDEOBUFFER_P_H
diff --git a/src/multimedia/video/qmemoryvideobuffer.cpp b/src/multimedia/video/qmemoryvideobuffer.cpp
index 8bb3eae17..bcbbe7e59 100644
--- a/src/multimedia/video/qmemoryvideobuffer.cpp
+++ b/src/multimedia/video/qmemoryvideobuffer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmemoryvideobuffer_p.h"
@@ -53,11 +17,11 @@ QT_BEGIN_NAMESPACE
/*!
Constructs a video buffer with an image stride of \a bytesPerLine from a byte \a array.
*/
-QMemoryVideoBuffer::QMemoryVideoBuffer(const QByteArray &array, int bytesPerLine)
- : QAbstractVideoBuffer(QVideoFrame::NoHandle)
+QMemoryVideoBuffer::QMemoryVideoBuffer(QByteArray data, int bytesPerLine)
+ : QAbstractVideoBuffer(QVideoFrame::NoHandle),
+ m_bytesPerLine(bytesPerLine),
+ m_data(std::move(data))
{
- data = array;
- this->bytesPerLine = bytesPerLine;
}
/*!
@@ -79,13 +43,18 @@ QVideoFrame::MapMode QMemoryVideoBuffer::mapMode() const
QAbstractVideoBuffer::MapData QMemoryVideoBuffer::map(QVideoFrame::MapMode mode)
{
MapData mapData;
- if (m_mapMode == QVideoFrame::NotMapped && data.size() && mode != QVideoFrame::NotMapped) {
+ if (m_mapMode == QVideoFrame::NotMapped && m_data.size() && mode != QVideoFrame::NotMapped) {
m_mapMode = mode;
mapData.nPlanes = 1;
- mapData.bytesPerLine[0] = bytesPerLine;
- mapData.data[0] = reinterpret_cast<uchar *>(data.data());
- mapData.size[0] = data.size();
+ mapData.bytesPerLine[0] = m_bytesPerLine;
+ // avoid detaching and extra copying in case the underlyingByteArray is
+ // being held by textures or anything else.
+ if (mode == QVideoFrame::ReadOnly)
+ mapData.data[0] = reinterpret_cast<uchar *>(const_cast<char*>(m_data.constData()));
+ else
+ mapData.data[0] = reinterpret_cast<uchar *>(m_data.data());
+ mapData.size[0] = m_data.size();
}
return mapData;
@@ -99,4 +68,12 @@ void QMemoryVideoBuffer::unmap()
m_mapMode = QVideoFrame::NotMapped;
}
+/*!
+ \reimp
+*/
+QByteArray QMemoryVideoBuffer::underlyingByteArray(int plane) const
+{
+ return plane == 0 ? m_data : QByteArray{};
+}
+
QT_END_NAMESPACE
diff --git a/src/multimedia/video/qmemoryvideobuffer_p.h b/src/multimedia/video/qmemoryvideobuffer_p.h
index 078b1af26..ec97abd4f 100644
--- a/src/multimedia/video/qmemoryvideobuffer_p.h
+++ b/src/multimedia/video/qmemoryvideobuffer_p.h
@@ -1,47 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMEMORYVIDEOBUFFER_P_H
#define QMEMORYVIDEOBUFFER_P_H
#include <private/qabstractvideobuffer_p.h>
-#include <qvideoframe.h>
//
// W A R N I N G
@@ -59,7 +22,7 @@ QT_BEGIN_NAMESPACE
class Q_MULTIMEDIA_EXPORT QMemoryVideoBuffer : public QAbstractVideoBuffer
{
public:
- QMemoryVideoBuffer(const QByteArray &data, int bytesPerLine);
+ QMemoryVideoBuffer(QByteArray data, int bytesPerLine);
~QMemoryVideoBuffer();
QVideoFrame::MapMode mapMode() const override;
@@ -67,9 +30,11 @@ public:
MapData map(QVideoFrame::MapMode mode) override;
void unmap() override;
- int bytesPerLine = 0;
+ QByteArray underlyingByteArray(int plane) const override;
+private:
+ int m_bytesPerLine = 0;
QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped;
- QByteArray data;
+ QByteArray m_data;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/video/qtvideo.cpp b/src/multimedia/video/qtvideo.cpp
new file mode 100644
index 000000000..29747b776
--- /dev/null
+++ b/src/multimedia/video/qtvideo.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtvideo.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \namespace QtVideo
+ \since 6.7
+ \inmodule QtMultimedia
+ \brief Enumerations for camera and video functionality.
+*/
+
+/*!
+ \enum QtVideo::Rotation
+ \since 6.7
+
+ The angle of the clockwise rotation that should be applied to a video
+ frame before displaying.
+
+ \value None No rotation required, the frame has correct orientation
+ \value Clockwise90 The frame should be rotated clockwise by 90 degrees
+ \value Clockwise180 The frame should be rotated clockwise by 180 degrees
+ \value Clockwise270 The frame should be rotated clockwise by 270 degrees
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qtvideo.cpp"
diff --git a/src/multimedia/video/qtvideo.h b/src/multimedia/video/qtvideo.h
new file mode 100644
index 000000000..a5f22ea2c
--- /dev/null
+++ b/src/multimedia/video/qtvideo.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTVIDEO_H
+#define QTVIDEO_H
+
+#include <QtMultimedia/qtmultimediaexports.h>
+#include <QtCore/qobjectdefs.h>
+#include <QtCore/qtconfigmacros.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtVideo
+{
+Q_NAMESPACE_EXPORT(Q_MULTIMEDIA_EXPORT)
+
+enum class Rotation { None = 0, Clockwise90 = 90, Clockwise180 = 180, Clockwise270 = 270 };
+Q_ENUM_NS(Rotation)
+}
+
+QT_END_NAMESPACE
+
+#endif // QTVIDEO_H
diff --git a/src/multimedia/video/qvideoframe.cpp b/src/multimedia/video/qvideoframe.cpp
index 0894cc466..6198ebcf4 100644
--- a/src/multimedia/video/qvideoframe.cpp
+++ b/src/multimedia/video/qvideoframe.cpp
@@ -1,91 +1,27 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideoframe.h"
+#include "qvideoframe_p.h"
#include "qvideotexturehelper_p.h"
+#include "qmultimediautils_p.h"
#include "qmemoryvideobuffer_p.h"
#include "qvideoframeconverter_p.h"
-#include "qvideoframeformat.h"
+#include "qimagevideobuffer_p.h"
#include "qpainter.h"
#include <qtextlayout.h>
#include <qimage.h>
-#include <qmutex.h>
#include <qpair.h>
#include <qsize.h>
#include <qvariant.h>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
-class QVideoFramePrivate : public QSharedData
-{
-public:
- QVideoFramePrivate() = default;
- QVideoFramePrivate(const QVideoFrameFormat &format)
- : format(format)
- {
- }
-
- ~QVideoFramePrivate()
- {
- delete buffer;
- }
-
- qint64 startTime = -1;
- qint64 endTime = -1;
- QAbstractVideoBuffer::MapData mapData;
- QVideoFrameFormat format;
- QAbstractVideoBuffer *buffer = nullptr;
- int mappedCount = 0;
- QMutex mapMutex;
- QString subtitleText;
- QVideoFrame::RotationAngle rotationAngle = QVideoFrame::Rotation0;
- bool mirrored = false;
- QImage image;
-private:
- Q_DISABLE_COPY(QVideoFramePrivate)
-};
-
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QVideoFramePrivate);
/*!
@@ -141,7 +77,7 @@ QVideoFrame::QVideoFrame()
QVideoFrame::QVideoFrame(QAbstractVideoBuffer *buffer, const QVideoFrameFormat &format)
: d(new QVideoFramePrivate(format))
{
- d->buffer = buffer;
+ d->buffer.reset(buffer);
}
/*!
@@ -149,7 +85,7 @@ QVideoFrame::QVideoFrame(QAbstractVideoBuffer *buffer, const QVideoFrameFormat &
*/
QAbstractVideoBuffer *QVideoFrame::videoBuffer() const
{
- return d ? d->buffer : nullptr;
+ return d ? d->buffer.get() : nullptr;
}
/*!
@@ -167,11 +103,54 @@ QVideoFrame::QVideoFrame(const QVideoFrameFormat &format)
// Check the memory was successfully allocated.
if (!data.isEmpty())
- d->buffer = new QMemoryVideoBuffer(data, textureDescription->strideForWidth(format.frameWidth()));
+ d->buffer = std::make_unique<QMemoryVideoBuffer>(data, textureDescription->strideForWidth(format.frameWidth()));
}
}
/*!
+ Constructs a QVideoFrame from a QImage. The QImage pixels are copied
+ into the QVideoFrame's memory buffer. The resulting frame has the
+ same size as the QImage, but the number of bytes per line may
+ differ.
+ \since 6.8
+
+ If the QImage::Format matches one of the formats in
+ QVideoFrameFormat::PixelFormat, the QVideoFrame will use that format
+ without any pixel format conversion. Otherwise, the image is first
+ converted to a supported (A)RGB format using QImage::convertedTo()
+ with the Qt::AutoColor flag. This may incur a performance penalty.
+
+ If QImage::isNull() evaluates to true for the input QImage, the
+ QVideoFrame will be invalid and QVideoFrameFormat::isValid() will
+ return false.
+
+ \sa QVideoFrameFormat::pixelFormatFromImageFormat()
+ \sa QImage::convertedTo()
+ \sa QImage::isNull()
+*/
+QVideoFrame::QVideoFrame(const QImage &image)
+{
+ auto buffer = std::make_unique<QImageVideoBuffer>(image);
+
+ // If the QImage::Format is not convertible to QVideoFrameFormat,
+ // QImageVideoBuffer automatically converts image to a compatible
+ // (A)RGB format.
+ const QImage &bufferImage = buffer->underlyingImage();
+
+ if (bufferImage.isNull())
+ return;
+
+ // `bufferImage` is now supported by QVideoFrameFormat::pixelFormatFromImageFormat()
+ QVideoFrameFormat format = {
+ bufferImage.size(), QVideoFrameFormat::pixelFormatFromImageFormat(bufferImage.format())
+ };
+
+ Q_ASSERT(format.isValid());
+
+ d = new QVideoFramePrivate{ std::move(format), std::move(buffer) };
+}
+
+/*!
Constructs a shallow copy of \a other. Since QVideoFrame is
explicitly shared, these two instances will reflect the same frame.
@@ -185,6 +164,12 @@ QVideoFrame::QVideoFrame(const QVideoFrame &other) = default;
*/
/*!
+ \fn void QVideoFrame::swap(QVideoFrame &other) noexcept
+
+ Swaps the current video frame with \a other.
+*/
+
+/*!
\fn QVideoFrame &QVideoFrame::operator=(QVideoFrame &&other)
Moves \a other into this QVideoFrame.
@@ -486,6 +471,15 @@ bool QVideoFrame::map(QVideoFrame::MapMode mode)
}
d->mappedCount++;
+
+ // unlock mapMutex to avoid potential deadlock imageMutex <--> mapMutex
+ lock.unlock();
+
+ if ((mode & QVideoFrame::WriteOnly) != 0) {
+ QMutexLocker lock(&d->imageMutex);
+ d->image = {};
+ }
+
return true;
}
@@ -601,30 +595,6 @@ int QVideoFrame::planeCount() const
}
/*!
- \internal
- Returns a texture id to the video frame's buffers.
-*/
-quint64 QVideoFrame::textureHandle(int plane) const
-{
- if (!d || !d->buffer)
- return 0;
- d->buffer->mapTextures();
- return d->buffer->textureHandle(plane);
-}
-
-/*!
- \internal
- Returns a QRhiTexture of the video frame
-*/
-std::unique_ptr<QRhiTexture> QVideoFrame::rhiTexture(int plane) const
-{
- if (!d || !d->buffer)
- return {};
- d->buffer->mapTextures();
- return d->buffer->texture(plane);
-}
-
-/*!
Returns the presentation time (in microseconds) when the frame should be displayed.
An invalid time is represented as -1.
@@ -676,8 +646,10 @@ void QVideoFrame::setEndTime(qint64 time)
d->endTime = time;
}
+#if QT_DEPRECATED_SINCE(6, 7)
/*!
\enum QVideoFrame::RotationAngle
+ \deprecated [6.7] Use QtVideo::Rotation instead.
The angle of the clockwise rotation that should be applied to a video
frame before displaying.
@@ -689,29 +661,47 @@ void QVideoFrame::setEndTime(qint64 time)
*/
/*!
+ \fn void QVideoFrame::setRotationAngle(RotationAngle)
+ \deprecated [6.7] Use \c QVideoFrame::setRotation instead.
+
Sets the \a angle the frame should be rotated clockwise before displaying.
*/
-void QVideoFrame::setRotationAngle(QVideoFrame::RotationAngle angle)
+
+/*!
+ \fn QVideoFrame::RotationAngle QVideoFrame::rotationAngle() const
+ \deprecated [6.7] Use \c QVideoFrame::rotation instead.
+
+ Returns the angle the frame should be rotated clockwise before displaying.
+*/
+
+#endif
+
+
+/*!
+ Sets the \a angle the frame should be rotated clockwise before displaying.
+*/
+void QVideoFrame::setRotation(QtVideo::Rotation angle)
{
if (d)
- d->rotationAngle = angle;
+ d->format.setRotation(angle);
}
/*!
Returns the angle the frame should be rotated clockwise before displaying.
*/
-QVideoFrame::RotationAngle QVideoFrame::rotationAngle() const
+QtVideo::Rotation QVideoFrame::rotation() const
{
- return d ? d->rotationAngle : Rotation0;
+ return d ? d->format.rotation() : QtVideo::Rotation::None;
}
/*!
- Sets the \a mirrored flag for the frame.
+ Sets the \a mirrored flag for the frame and
+ sets the flag to the underlying \l surfaceFormat.
*/
void QVideoFrame::setMirrored(bool mirrored)
{
if (d)
- d->mirrored = mirrored;
+ d->format.setMirrored(mirrored);
}
/*!
@@ -719,7 +709,24 @@ void QVideoFrame::setMirrored(bool mirrored)
*/
bool QVideoFrame::mirrored() const
{
- return d && d->mirrored;
+ return d && d->format.isMirrored();
+}
+
+/*!
+ Sets the frame \a rate of a video stream in frames per second.
+*/
+void QVideoFrame::setStreamFrameRate(qreal rate)
+{
+ if (d)
+ d->format.setStreamFrameRate(rate);
+}
+
+/*!
+ Returns the frame rate of a video stream in frames per second.
+*/
+qreal QVideoFrame::streamFrameRate() const
+{
+ return d ? d->format.streamFrameRate() : 0.;
}
/*!
@@ -730,11 +737,14 @@ QImage QVideoFrame::toImage() const
{
if (!isValid())
return {};
- if (!d->image.isNull())
- return d->image;
- d->image = qImageFromVideoFrame(*this, rotationAngle(), mirrored(),
- surfaceFormat().scanLineDirection() != QVideoFrameFormat::TopToBottom);
+ QMutexLocker lock(&d->imageMutex);
+
+ if (d->image.isNull()) {
+ const bool mirrorY = surfaceFormat().scanLineDirection() != QVideoFrameFormat::TopToBottom;
+ d->image = qImageFromVideoFrame(*this, rotation(), mirrored(), mirrorY);
+ }
+
return d->image;
}
@@ -772,9 +782,7 @@ void QVideoFrame::paint(QPainter *painter, const QRectF &rect, const PaintOption
}
QRectF targetRect = rect;
- QSizeF size = this->size();
- if (rotationAngle() % 180)
- size.transpose();
+ QSizeF size = qRotatedFrameSize(*this);
size.scale(targetRect.size(), options.aspectRatioMode);
@@ -850,12 +858,12 @@ static QString qFormatTimeStamps(qint64 start, qint64 end)
if (onlyOne) {
if (start > 0)
- return QString::fromLatin1("@%1:%2:%3.%4")
+ return QStringLiteral("@%1:%2:%3.%4")
.arg(start, 1, 10, QLatin1Char('0'))
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'));
- return QString::fromLatin1("@%1:%2.%3")
+ return QStringLiteral("@%1:%2.%3")
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'));
@@ -864,12 +872,12 @@ static QString qFormatTimeStamps(qint64 start, qint64 end)
if (end == -1) {
// Similar to start-start, except it means keep displaying it?
if (start > 0)
- return QString::fromLatin1("%1:%2:%3.%4 - forever")
+ return QStringLiteral("%1:%2:%3.%4 - forever")
.arg(start, 1, 10, QLatin1Char('0'))
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'));
- return QString::fromLatin1("%1:%2.%3 - forever")
+ return QStringLiteral("%1:%2.%3 - forever")
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'));
@@ -883,7 +891,7 @@ static QString qFormatTimeStamps(qint64 start, qint64 end)
end /= 60;
if (start > 0 || end > 0)
- return QString::fromLatin1("%1:%2:%3.%4 - %5:%6:%7.%8")
+ return QStringLiteral("%1:%2:%3.%4 - %5:%6:%7.%8")
.arg(start, 1, 10, QLatin1Char('0'))
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
@@ -892,7 +900,7 @@ static QString qFormatTimeStamps(qint64 start, qint64 end)
.arg(e_minutes, 2, 10, QLatin1Char('0'))
.arg(e_seconds, 2, 10, QLatin1Char('0'))
.arg(e_millis, 2, 10, QLatin1Char('0'));
- return QString::fromLatin1("%1:%2.%3 - %4:%5.%6")
+ return QStringLiteral("%1:%2.%3 - %4:%5.%6")
.arg(s_minutes, 2, 10, QLatin1Char('0'))
.arg(s_seconds, 2, 10, QLatin1Char('0'))
.arg(s_millis, 2, 10, QLatin1Char('0'))
diff --git a/src/multimedia/video/qvideoframe.h b/src/multimedia/video/qvideoframe.h
index f1e329c38..5a2cf177d 100644
--- a/src/multimedia/video/qvideoframe.h
+++ b/src/multimedia/video/qvideoframe.h
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOFRAME_H
#define QVIDEOFRAME_H
#include <QtMultimedia/qtmultimediaglobal.h>
+#include <QtMultimedia/qtvideo.h>
#include <QtMultimedia/qvideoframeformat.h>
#include <QtCore/qmetatype.h>
@@ -76,16 +41,19 @@ public:
ReadWrite = ReadOnly | WriteOnly
};
+#if QT_DEPRECATED_SINCE(6, 7)
enum RotationAngle
{
- Rotation0 = 0,
- Rotation90 = 90,
- Rotation180 = 180,
- Rotation270 = 270
+ Rotation0 Q_DECL_ENUMERATOR_DEPRECATED_X("Use QtVideo::Rotation::None instead") = 0,
+ Rotation90 Q_DECL_ENUMERATOR_DEPRECATED_X("Use QtVideo::Rotation::Clockwise90 instead") = 90,
+ Rotation180 Q_DECL_ENUMERATOR_DEPRECATED_X("Use QtVideo::Rotation::Clockwise180 instead") = 180,
+ Rotation270 Q_DECL_ENUMERATOR_DEPRECATED_X("Use QtVideo::Rotation::Clockwise270 instead") = 270
};
+#endif
QVideoFrame();
QVideoFrame(const QVideoFrameFormat &format);
+ explicit QVideoFrame(const QImage &image);
QVideoFrame(const QVideoFrame &other);
~QVideoFrame();
@@ -126,21 +94,29 @@ public:
int mappedBytes(int plane) const;
int planeCount() const;
- quint64 textureHandle(int plane) const;
- std::unique_ptr<QRhiTexture> rhiTexture(int plane) const;
-
qint64 startTime() const;
void setStartTime(qint64 time);
qint64 endTime() const;
void setEndTime(qint64 time);
- void setRotationAngle(RotationAngle);
- RotationAngle rotationAngle() const;
+#if QT_DEPRECATED_SINCE(6, 7)
+ QT_DEPRECATED_VERSION_X_6_7("Use QVideoFrame::setRotation(QtVideo::Rotation) instead")
+ void setRotationAngle(RotationAngle angle) { setRotation(QtVideo::Rotation(angle)); }
+
+ QT_DEPRECATED_VERSION_X_6_7("Use QVideoFrame::rotation() instead")
+ RotationAngle rotationAngle() const { return RotationAngle(rotation()); }
+#endif
+
+ void setRotation(QtVideo::Rotation angle);
+ QtVideo::Rotation rotation() const;
void setMirrored(bool);
bool mirrored() const;
+ void setStreamFrameRate(qreal rate);
+ qreal streamFrameRate() const;
+
QImage toImage() const;
struct PaintOptions {
@@ -162,6 +138,7 @@ public:
QAbstractVideoBuffer *videoBuffer() const;
private:
+ friend class QVideoFramePrivate;
QExplicitlySharedDataPointer<QVideoFramePrivate> d;
};
diff --git a/src/multimedia/video/qvideoframe_p.h b/src/multimedia/video/qvideoframe_p.h
new file mode 100644
index 000000000..cb8b7b747
--- /dev/null
+++ b/src/multimedia/video/qvideoframe_p.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QVIDEOFRAME_P_H
+#define QVIDEOFRAME_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qvideoframe.h"
+#include "qabstractvideobuffer_p.h"
+
+#include <qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoFramePrivate : public QSharedData
+{
+public:
+ QVideoFramePrivate() = default;
+ QVideoFramePrivate(const QVideoFrameFormat &format) : format(format) { }
+ QVideoFramePrivate(QVideoFrameFormat format, std::unique_ptr<QAbstractVideoBuffer> buffer)
+ : format{ std::move(format) }, buffer{ std::move(buffer) }
+ {
+ }
+
+ static QVideoFramePrivate *handle(QVideoFrame &frame) { return frame.d.get(); };
+
+ QVideoFrame adoptThisByVideoFrame()
+ {
+ QVideoFrame frame;
+ frame.d = QExplicitlySharedDataPointer(this, QAdoptSharedDataTag{});
+ return frame;
+ }
+
+ qint64 startTime = -1;
+ qint64 endTime = -1;
+ QAbstractVideoBuffer::MapData mapData;
+ QVideoFrameFormat format;
+ std::unique_ptr<QAbstractVideoBuffer> buffer;
+ int mappedCount = 0;
+ QMutex mapMutex;
+ QString subtitleText;
+ QImage image;
+ QMutex imageMutex;
+
+private:
+ Q_DISABLE_COPY(QVideoFramePrivate)
+};
+
+QT_END_NAMESPACE
+
+#endif // QVIDEOFRAMEPRIVATE_P_H
diff --git a/src/multimedia/video/qvideoframeconversionhelper.cpp b/src/multimedia/video/qvideoframeconversionhelper.cpp
index b49fc539f..1b570b74f 100644
--- a/src/multimedia/video/qvideoframeconversionhelper.cpp
+++ b/src/multimedia/video/qvideoframeconversionhelper.cpp
@@ -1,45 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideoframeconversionhelper_p.h"
#include "qrgb.h"
+#include <mutex>
+
QT_BEGIN_NAMESPACE
#define CLAMP(n) (n > 255 ? 255 : (n < 0 ? 0 : n))
@@ -67,6 +33,7 @@ static inline void planarYUV420_to_ARGB32(const uchar *y, int yStride,
quint32 *rgb,
int width, int height)
{
+ height &= ~1;
quint32 *rgb0 = rgb;
quint32 *rgb1 = rgb + width;
@@ -415,6 +382,7 @@ static inline void planarYUV420_16bit_to_ARGB32(const uchar *y, int yStride,
quint32 *rgb,
int width, int height)
{
+ height &= ~1;
quint32 *rgb0 = rgb;
quint32 *rgb1 = rgb + width;
@@ -494,6 +462,14 @@ static void QT_FASTCALL qt_convert_Y_to_ARGB32(const QVideoFrame &frame, uchar *
MERGE_LOOPS(width, height, stride, 1)
}
+template<typename Pixel>
+static void QT_FASTCALL qt_copy_pixels_with_mask(Pixel *dst, const Pixel *src, size_t size,
+ Pixel mask)
+{
+ for (size_t x = 0; x < size; ++x)
+ dst[x] = src[x] | mask;
+}
+
static VideoFrameConvertFunc qConvertFuncs[QVideoFrameFormat::NPixelFormats] = {
/* Format_Invalid */ nullptr, // Not needed
/* Format_ARGB8888 */ qt_convert_to_ARGB32<ARGB8888>,
@@ -526,24 +502,32 @@ static VideoFrameConvertFunc qConvertFuncs[QVideoFrameFormat::NPixelFormats] = {
/* Format_Jpeg */ nullptr, // Not needed
};
-static void qInitConvertFuncsAsm()
+static PixelsCopyFunc qPixelsCopyFunc = qt_copy_pixels_with_mask<uint32_t>;
+
+static std::once_flag InitFuncsAsmFlag;
+
+static void qInitFuncsAsm()
{
#ifdef QT_COMPILER_SUPPORTS_SSE2
extern void QT_FASTCALL qt_convert_ARGB8888_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output);
extern void QT_FASTCALL qt_convert_ABGR8888_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output);
extern void QT_FASTCALL qt_convert_RGBA8888_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output);
extern void QT_FASTCALL qt_convert_BGRA8888_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output);
+ extern void QT_FASTCALL qt_copy_pixels_with_mask_sse2(uint32_t * dst, const uint32_t *src, size_t size, uint32_t mask);
+
if (qCpuHasFeature(SSE2)){
qConvertFuncs[QVideoFrameFormat::Format_ARGB8888] = qt_convert_ARGB8888_to_ARGB32_sse2;
qConvertFuncs[QVideoFrameFormat::Format_ARGB8888_Premultiplied] = qt_convert_ARGB8888_to_ARGB32_sse2;
qConvertFuncs[QVideoFrameFormat::Format_XRGB8888] = qt_convert_ARGB8888_to_ARGB32_sse2;
qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_sse2;
qConvertFuncs[QVideoFrameFormat::Format_BGRA8888_Premultiplied] = qt_convert_BGRA8888_to_ARGB32_sse2;
- qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_BGRA8888_to_ARGB32_sse2;
+ qConvertFuncs[QVideoFrameFormat::Format_BGRX8888] = qt_convert_BGRA8888_to_ARGB32_sse2;
qConvertFuncs[QVideoFrameFormat::Format_ABGR8888] = qt_convert_ABGR8888_to_ARGB32_sse2;
qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_ABGR8888_to_ARGB32_sse2;
- qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_sse2;
- qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_sse2;
+ qConvertFuncs[QVideoFrameFormat::Format_RGBA8888] = qt_convert_RGBA8888_to_ARGB32_sse2;
+ qConvertFuncs[QVideoFrameFormat::Format_RGBX8888] = qt_convert_RGBA8888_to_ARGB32_sse2;
+
+ qPixelsCopyFunc = qt_copy_pixels_with_mask_sse2;
}
#endif
#ifdef QT_COMPILER_SUPPORTS_SSSE3
@@ -557,11 +541,11 @@ static void qInitConvertFuncsAsm()
qConvertFuncs[QVideoFrameFormat::Format_XRGB8888] = qt_convert_ARGB8888_to_ARGB32_ssse3;
qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_ssse3;
qConvertFuncs[QVideoFrameFormat::Format_BGRA8888_Premultiplied] = qt_convert_BGRA8888_to_ARGB32_ssse3;
- qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_BGRA8888_to_ARGB32_ssse3;
+ qConvertFuncs[QVideoFrameFormat::Format_BGRX8888] = qt_convert_BGRA8888_to_ARGB32_ssse3;
qConvertFuncs[QVideoFrameFormat::Format_ABGR8888] = qt_convert_ABGR8888_to_ARGB32_ssse3;
qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_ABGR8888_to_ARGB32_ssse3;
- qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_ssse3;
- qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_ssse3;
+ qConvertFuncs[QVideoFrameFormat::Format_RGBA8888] = qt_convert_RGBA8888_to_ARGB32_ssse3;
+ qConvertFuncs[QVideoFrameFormat::Format_RGBX8888] = qt_convert_RGBA8888_to_ARGB32_ssse3;
}
#endif
#ifdef QT_COMPILER_SUPPORTS_AVX2
@@ -569,31 +553,82 @@ static void qInitConvertFuncsAsm()
extern void QT_FASTCALL qt_convert_ABGR8888_to_ARGB32_avx2(const QVideoFrame &frame, uchar *output);
extern void QT_FASTCALL qt_convert_RGBA8888_to_ARGB32_avx2(const QVideoFrame &frame, uchar *output);
extern void QT_FASTCALL qt_convert_BGRA8888_to_ARGB32_avx2(const QVideoFrame &frame, uchar *output);
+ extern void QT_FASTCALL qt_copy_pixels_with_mask_avx2(uint32_t * dst, const uint32_t *src, size_t size, uint32_t mask);
if (qCpuHasFeature(AVX2)){
qConvertFuncs[QVideoFrameFormat::Format_ARGB8888] = qt_convert_ARGB8888_to_ARGB32_avx2;
qConvertFuncs[QVideoFrameFormat::Format_ARGB8888_Premultiplied] = qt_convert_ARGB8888_to_ARGB32_avx2;
qConvertFuncs[QVideoFrameFormat::Format_XRGB8888] = qt_convert_ARGB8888_to_ARGB32_avx2;
qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_avx2;
qConvertFuncs[QVideoFrameFormat::Format_BGRA8888_Premultiplied] = qt_convert_BGRA8888_to_ARGB32_avx2;
- qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_BGRA8888_to_ARGB32_avx2;
+ qConvertFuncs[QVideoFrameFormat::Format_BGRX8888] = qt_convert_BGRA8888_to_ARGB32_avx2;
qConvertFuncs[QVideoFrameFormat::Format_ABGR8888] = qt_convert_ABGR8888_to_ARGB32_avx2;
qConvertFuncs[QVideoFrameFormat::Format_XBGR8888] = qt_convert_ABGR8888_to_ARGB32_avx2;
- qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_avx2;
- qConvertFuncs[QVideoFrameFormat::Format_BGRA8888] = qt_convert_BGRA8888_to_ARGB32_avx2;
+ qConvertFuncs[QVideoFrameFormat::Format_RGBA8888] = qt_convert_RGBA8888_to_ARGB32_avx2;
+ qConvertFuncs[QVideoFrameFormat::Format_RGBX8888] = qt_convert_RGBA8888_to_ARGB32_avx2;
+
+ qPixelsCopyFunc = qt_copy_pixels_with_mask_avx2;
}
#endif
}
VideoFrameConvertFunc qConverterForFormat(QVideoFrameFormat::PixelFormat format)
{
- static bool initAsmFuncsDone = false;
- if (!initAsmFuncsDone) {
- qInitConvertFuncsAsm();
- initAsmFuncsDone = true;
- }
+ std::call_once(InitFuncsAsmFlag, &qInitFuncsAsm);
+
VideoFrameConvertFunc convert = qConvertFuncs[format];
return convert;
}
+void Q_MULTIMEDIA_EXPORT qCopyPixelsWithAlphaMask(uint32_t *dst,
+ const uint32_t *src,
+ size_t pixCount,
+ QVideoFrameFormat::PixelFormat format,
+ bool srcAlphaVaries)
+{
+ if (pixCount == 0)
+ return;
+
+ const auto mask = qAlphaMask(format);
+
+ if (srcAlphaVaries || (src[0] & mask) != mask)
+ qCopyPixelsWithMask(dst, src, pixCount, mask);
+ else
+ memcpy(dst, src, pixCount * 4);
+}
+
+void qCopyPixelsWithMask(uint32_t *dst, const uint32_t *src, size_t size, uint32_t mask)
+{
+ std::call_once(InitFuncsAsmFlag, &qInitFuncsAsm);
+
+ qPixelsCopyFunc(dst, src, size, mask);
+}
+
+uint32_t qAlphaMask(QVideoFrameFormat::PixelFormat format)
+{
+ switch (format) {
+ case QVideoFrameFormat::Format_ARGB8888:
+ case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
+ case QVideoFrameFormat::Format_XRGB8888:
+ case QVideoFrameFormat::Format_ABGR8888:
+ case QVideoFrameFormat::Format_XBGR8888:
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ return 0xff;
+#else
+ return 0xff000000;
+#endif
+ case QVideoFrameFormat::Format_BGRA8888:
+ case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
+ case QVideoFrameFormat::Format_BGRX8888:
+ case QVideoFrameFormat::Format_RGBA8888:
+ case QVideoFrameFormat::Format_RGBX8888:
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ return 0xff000000;
+#else
+ return 0xff;
+#endif
+ default:
+ return 0;
+ }
+}
QT_END_NAMESPACE
diff --git a/src/multimedia/video/qvideoframeconversionhelper_avx2.cpp b/src/multimedia/video/qvideoframeconversionhelper_avx2.cpp
index bffc48561..6802cca74 100644
--- a/src/multimedia/video/qvideoframeconversionhelper_avx2.cpp
+++ b/src/multimedia/video/qvideoframeconversionhelper_avx2.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideoframeconversionhelper_p.h"
@@ -78,7 +42,7 @@ void convert_to_ARGB32_avx2(const QVideoFrame &frame, uchar *output)
auto *pixel = reinterpret_cast<const Pixel *>(src);
int x = 0;
- ALIGN(32, argb, x, width) {
+ QT_MEDIA_ALIGN(32, argb, x, width) {
*argb = pixel->convert();
++pixel;
++argb;
@@ -129,6 +93,43 @@ void QT_FASTCALL qt_convert_BGRA8888_to_ARGB32_avx2(const QVideoFrame &frame, uc
convert_to_ARGB32_avx2<3, 2, 1, 0>(frame, output);
}
+void QT_FASTCALL qt_copy_pixels_with_mask_avx2(uint32_t *dst, const uint32_t *src, size_t size, uint32_t mask)
+{
+ const auto mask256 = _mm256_set_epi32(mask, mask, mask, mask, mask, mask, mask, mask);
+
+ size_t x = 0;
+
+ QT_MEDIA_ALIGN(32, dst, x, size)
+ *(dst++) = *(src++) | mask;
+
+ for (; x < size - (8 * 4 + 1); x += 8 * 4) {
+ const auto srcData1 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src));
+ const auto srcData2 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src += 8));
+ const auto srcData3 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src += 8));
+ const auto srcData4 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src += 8));
+
+ _mm256_store_si256(reinterpret_cast<__m256i *>(dst), _mm256_or_si256(srcData1, mask256));
+ _mm256_store_si256(reinterpret_cast<__m256i *>(dst += 8), _mm256_or_si256(srcData2, mask256));
+ _mm256_store_si256(reinterpret_cast<__m256i *>(dst += 8), _mm256_or_si256(srcData3, mask256));
+ _mm256_store_si256(reinterpret_cast<__m256i *>(dst += 8), _mm256_or_si256(srcData4, mask256));
+
+ src += 8;
+ dst += 8;
+ }
+
+ // leftovers
+ for (; x < size - 7; x += 8) {
+ const auto srcData = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src));
+ _mm256_store_si256(reinterpret_cast<__m256i *>(dst), _mm256_or_si256(srcData, mask256));
+
+ src += 8;
+ dst += 8;
+ }
+
+ for (; x < size; ++x)
+ *(dst++) = *(src++) | mask;
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/multimedia/video/qvideoframeconversionhelper_p.h b/src/multimedia/video/qvideoframeconversionhelper_p.h
index 792b3964d..17490a817 100644
--- a/src/multimedia/video/qvideoframeconversionhelper_p.h
+++ b/src/multimedia/video/qvideoframeconversionhelper_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOFRAMECONVERSIONHELPER_P_H
#define QVIDEOFRAMECONVERSIONHELPER_P_H
@@ -58,9 +22,20 @@ QT_BEGIN_NAMESPACE
// Converts to RGB32 or ARGB32_Premultiplied
typedef void (QT_FASTCALL *VideoFrameConvertFunc)(const QVideoFrame &frame, uchar *output);
+typedef void(QT_FASTCALL *PixelsCopyFunc)(uint32_t *dst, const uint32_t *src, size_t size, uint32_t mask);
VideoFrameConvertFunc qConverterForFormat(QVideoFrameFormat::PixelFormat format);
+void Q_MULTIMEDIA_EXPORT qCopyPixelsWithAlphaMask(uint32_t *dst,
+ const uint32_t *src,
+ size_t size,
+ QVideoFrameFormat::PixelFormat format,
+ bool srcAlphaVaries);
+
+void Q_MULTIMEDIA_EXPORT qCopyPixelsWithMask(uint32_t *dst, const uint32_t *src, size_t size, uint32_t mask);
+
+uint32_t Q_MULTIMEDIA_EXPORT qAlphaMask(QVideoFrameFormat::PixelFormat format);
+
template<int a, int r, int g, int b>
struct ArgbPixel
{
@@ -158,7 +133,7 @@ using BGRX8888 = RgbPixel<2, 1, 0>;
stride = 0; \
}
-#define ALIGN(boundary, ptr, x, length) \
+#define QT_MEDIA_ALIGN(boundary, ptr, x, length) \
for (; ((reinterpret_cast<qintptr>(ptr) & (boundary - 1)) != 0) && x < length; ++x)
QT_END_NAMESPACE
diff --git a/src/multimedia/video/qvideoframeconversionhelper_sse2.cpp b/src/multimedia/video/qvideoframeconversionhelper_sse2.cpp
index 5c6250224..b7049d806 100644
--- a/src/multimedia/video/qvideoframeconversionhelper_sse2.cpp
+++ b/src/multimedia/video/qvideoframeconversionhelper_sse2.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideoframeconversionhelper_p.h"
@@ -65,7 +29,7 @@ void convert_to_ARGB32_sse2(const QVideoFrame &frame, uchar *output)
auto *pixel = reinterpret_cast<const Pixel *>(src);
int x = 0;
- ALIGN(16, argb, x, width) {
+ QT_MEDIA_ALIGN(16, argb, x, width) {
*argb = pixel->convert();
++pixel;
++argb;
@@ -116,6 +80,44 @@ void QT_FASTCALL qt_convert_BGRA8888_to_ARGB32_sse2(const QVideoFrame &frame, uc
convert_to_ARGB32_sse2<3, 2, 1, 0>(frame, output);
}
+void QT_FASTCALL qt_copy_pixels_with_mask_sse2(uint32_t *dst, const uint32_t *src, size_t size, uint32_t mask)
+{
+ const auto mask128 = _mm_set_epi32(mask, mask, mask, mask);
+
+ size_t x = 0;
+
+ QT_MEDIA_ALIGN(16, dst, x, size)
+ *(dst++) = *(src++) | mask;
+
+ for (; x < size - (4 * 4 - 1); x += 4 * 4) {
+ const auto srcData0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
+ const auto srcData1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src += 4));
+ const auto srcData2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src += 4));
+ const auto srcData3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src += 4));
+
+ _mm_store_si128(reinterpret_cast<__m128i *>(dst), _mm_or_si128(srcData0, mask128));
+ _mm_store_si128(reinterpret_cast<__m128i *>(dst += 4), _mm_or_si128(srcData1, mask128));
+ _mm_store_si128(reinterpret_cast<__m128i *>(dst += 4), _mm_or_si128(srcData2, mask128));
+ _mm_store_si128(reinterpret_cast<__m128i *>(dst += 4), _mm_or_si128(srcData3, mask128));
+
+ src += 4;
+ dst += 4;
+ }
+
+ for (; x < size - 3; x += 4) {
+ const auto srcData = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
+
+ _mm_store_si128(reinterpret_cast<__m128i *>(dst), _mm_or_si128(srcData, mask128));
+
+ src += 4;
+ dst += 4;
+ }
+
+ // leftovers
+ for (; x < size; ++x)
+ *(dst++) = *(src++) | mask;
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/multimedia/video/qvideoframeconversionhelper_ssse3.cpp b/src/multimedia/video/qvideoframeconversionhelper_ssse3.cpp
index 47c0f683b..10a244fb0 100644
--- a/src/multimedia/video/qvideoframeconversionhelper_ssse3.cpp
+++ b/src/multimedia/video/qvideoframeconversionhelper_ssse3.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideoframeconversionhelper_p.h"
@@ -70,7 +34,7 @@ void convert_to_ARGB32_ssse3(const QVideoFrame &frame, uchar *output)
const auto *pixel = reinterpret_cast<const Pixel *>(src);
int x = 0;
- ALIGN(16, argb, x, width) {
+ QT_MEDIA_ALIGN(16, argb, x, width) {
*argb = pixel->convert();
++pixel;
++argb;
diff --git a/src/multimedia/video/qvideoframeconverter.cpp b/src/multimedia/video/qvideoframeconverter.cpp
index 6ad1e8dc2..82e0a0af5 100644
--- a/src/multimedia/video/qvideoframeconverter.cpp
+++ b/src/multimedia/video/qvideoframeconverter.cpp
@@ -1,60 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideoframeconverter_p.h"
#include "qvideoframeconversionhelper_p.h"
#include "qvideoframeformat.h"
-
-#include <QtGui/private/qrhinull_p.h>
-#if QT_CONFIG(opengl)
-#include <QtGui/private/qrhigles2_p.h>
-#include <QOffscreenSurface>
-#endif
-#if QT_CONFIG(vulkan)
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
-#endif
+#include "qvideoframe_p.h"
+#include "qmultimediautils_p.h"
#include <QtCore/qcoreapplication.h>
#include <QtCore/qsize.h>
@@ -62,16 +13,20 @@
#include <QtCore/qfile.h>
#include <QtCore/qthreadstorage.h>
#include <QtGui/qimage.h>
+#include <QtGui/qoffscreensurface.h>
#include <qpa/qplatformintegration.h>
#include <private/qvideotexturehelper_p.h>
#include <private/qabstractvideobuffer_p.h>
#include <private/qguiapplication_p.h>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
+#ifdef Q_OS_DARWIN
+#include <QtCore/private/qcore_mac_p.h>
+#endif
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcVideoFrameConverter, "qt.multimedia.video.frameconverter")
+static Q_LOGGING_CATEGORY(qLcVideoFrameConverter, "qt.multimedia.video.frameconverter")
namespace {
@@ -82,11 +37,21 @@ struct State
QOffscreenSurface *fallbackSurface = nullptr;
#endif
bool cpuOnly = false;
+#if defined(Q_OS_ANDROID)
+ QMetaObject::Connection appStateChangedConnection;
+#endif
~State() {
+ resetRhi();
+ }
+
+ void resetRhi() {
delete rhi;
+ rhi = nullptr;
#if QT_CONFIG(opengl)
delete fallbackSurface;
+ fallbackSurface = nullptr;
#endif
+ cpuOnly = false;
}
};
@@ -135,7 +100,7 @@ static bool pixelFormatHasAlpha(QVideoFrameFormat::PixelFormat format)
}
};
-static QShader getShader(const QString &name)
+static QShader vfcGetShader(const QString &name)
{
QShader shader = g_shaderCache.value(name);
if (shader.isValid())
@@ -151,13 +116,13 @@ static QShader getShader(const QString &name)
return shader;
}
-static void rasterTransform(QImage &image, QVideoFrame::RotationAngle rotation,
+static void rasterTransform(QImage &image, QtVideo::Rotation rotation,
bool mirrorX, bool mirrorY)
{
QTransform t;
if (mirrorX)
t.scale(-1.f, 1.f);
- if (rotation != QVideoFrame::Rotation0)
+ if (rotation != QtVideo::Rotation::None)
t.rotate(float(rotation));
if (mirrorY)
t.scale(1.f, -1.f);
@@ -171,11 +136,13 @@ static void imageCleanupHandler(void *info)
delete imageData;
}
-static QRhi *initializeRHI(QRhi::Implementation backend)
+static QRhi *initializeRHI(QRhi *videoFrameRhi)
{
if (g_state.localData().rhi || g_state.localData().cpuOnly)
return g_state.localData().rhi;
+ QRhi::Implementation backend = videoFrameRhi ? videoFrameRhi->backend() : QRhi::Null;
+
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering)) {
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
@@ -201,7 +168,19 @@ static QRhi *initializeRHI(QRhi::Implementation backend)
g_state.localData().fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
QRhiGles2InitParams params;
params.fallbackSurface = g_state.localData().fallbackSurface;
+ if (backend == QRhi::OpenGLES2)
+ params.shareContext = static_cast<const QRhiGles2NativeHandles*>(videoFrameRhi->nativeHandles())->context;
g_state.localData().rhi = QRhi::create(QRhi::OpenGLES2, &params);
+
+#if defined(Q_OS_ANDROID)
+ // reset RHI state on application suspension, as this will be invalid after resuming
+ if (!g_state.localData().appStateChangedConnection) {
+ g_state.localData().appStateChangedConnection = QObject::connect(qApp, &QGuiApplication::applicationStateChanged, qApp, [](auto state) {
+ if (state == Qt::ApplicationSuspended)
+ g_state.localData().resetRhi();
+ });
+ }
+#endif
}
}
#endif
@@ -216,41 +195,37 @@ static QRhi *initializeRHI(QRhi::Implementation backend)
}
static bool updateTextures(QRhi *rhi,
- QRhiResourceUpdateBatch *rub,
std::unique_ptr<QRhiBuffer> &uniformBuffer,
std::unique_ptr<QRhiSampler> &textureSampler,
std::unique_ptr<QRhiShaderResourceBindings> &shaderResourceBindings,
std::unique_ptr<QRhiGraphicsPipeline> &graphicsPipeline,
std::unique_ptr<QRhiRenderPassDescriptor> &renderPass,
- const QVideoFrame &frame,
- std::unique_ptr<QRhiTexture> (&textures)[QVideoTextureHelper::TextureDescription::maxPlanes])
+ QVideoFrame &frame,
+ const std::unique_ptr<QVideoFrameTextures> &videoFrameTextures)
{
auto format = frame.surfaceFormat();
auto pixelFormat = format.pixelFormat();
auto textureDesc = QVideoTextureHelper::textureDescription(pixelFormat);
- for (int i = 0; i < QVideoTextureHelper::TextureDescription::maxPlanes; ++i)
- QVideoTextureHelper::updateRhiTexture(frame, rhi, rub, i, textures[i]);
-
QRhiShaderResourceBinding bindings[4];
auto *b = bindings;
*b++ = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
uniformBuffer.get());
for (int i = 0; i < textureDesc->nplanes; ++i)
*b++ = QRhiShaderResourceBinding::sampledTexture(i + 1, QRhiShaderResourceBinding::FragmentStage,
- textures[i].get(), textureSampler.get());
+ videoFrameTextures->texture(i), textureSampler.get());
shaderResourceBindings->setBindings(bindings, b);
shaderResourceBindings->create();
graphicsPipeline.reset(rhi->newGraphicsPipeline());
graphicsPipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
- QShader vs = getShader(QVideoTextureHelper::vertexShaderFileName(format));
+ QShader vs = vfcGetShader(QVideoTextureHelper::vertexShaderFileName(format));
if (!vs.isValid())
return false;
- QShader fs = getShader(QVideoTextureHelper::fragmentShaderFileName(format));
+ QShader fs = vfcGetShader(QVideoTextureHelper::fragmentShaderFileName(format));
if (!fs.isValid())
return false;
@@ -276,7 +251,7 @@ static bool updateTextures(QRhi *rhi,
return true;
}
-static QImage convertJPEG(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
+static QImage convertJPEG(const QVideoFrame &frame, QtVideo::Rotation rotation, bool mirrorX, bool mirrorY)
{
QVideoFrame varFrame = frame;
if (!varFrame.map(QVideoFrame::ReadOnly)) {
@@ -290,7 +265,7 @@ static QImage convertJPEG(const QVideoFrame &frame, QVideoFrame::RotationAngle r
return image;
}
-static QImage convertCPU(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
+static QImage convertCPU(const QVideoFrame &frame, QtVideo::Rotation rotation, bool mirrorX, bool mirrorY)
{
VideoFrameConvertFunc convert = qConverterForFormat(frame.pixelFormat());
if (!convert) {
@@ -311,7 +286,7 @@ static QImage convertCPU(const QVideoFrame &frame, QVideoFrame::RotationAngle ro
}
}
-QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
+QImage qImageFromVideoFrame(const QVideoFrame &frame, QtVideo::Rotation rotation, bool mirrorX, bool mirrorY)
{
#ifdef Q_OS_DARWIN
QMacAutoReleasePool releasePool;
@@ -328,7 +303,6 @@ QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle
std::unique_ptr<QRhiSampler> textureSampler;
std::unique_ptr<QRhiShaderResourceBindings> shaderResourceBindings;
std::unique_ptr<QRhiGraphicsPipeline> graphicsPipeline;
- std::unique_ptr<QRhiTexture> frameTextures[QVideoTextureHelper::TextureDescription::maxPlanes];
if (frame.size().isEmpty() || frame.pixelFormat() == QVideoFrameFormat::Format_Invalid)
return {};
@@ -337,27 +311,19 @@ QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle
return convertJPEG(frame, rotation, mirrorX, mirrorY);
QRhi *rhi = nullptr;
- QRhi::Implementation backend = QRhi::Null;
- if (frame.videoBuffer()) {
+ if (frame.videoBuffer())
rhi = frame.videoBuffer()->rhi();
- if (rhi)
- backend = rhi->backend();
- }
if (!rhi || rhi->thread() != QThread::currentThread())
- rhi = initializeRHI(backend);
+ rhi = initializeRHI(rhi);
if (!rhi || rhi->isRecordingFrame())
return convertCPU(frame, rotation, mirrorX, mirrorY);
// Do conversion using shaders
- const int rotationIndex = (rotation / 90) % 4;
-
- QSize frameSize = frame.size();
- if (rotationIndex % 2)
- frameSize.transpose();
+ const QSize frameSize = qRotatedFrameSize(frame.size(), rotation);
vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(g_quad)));
vertexBuffer->create();
@@ -393,8 +359,15 @@ QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle
rub->uploadStaticBuffer(vertexBuffer.get(), g_quad);
- if (!updateTextures(rhi, rub, uniformBuffer, textureSampler, shaderResourceBindings,
- graphicsPipeline, renderPass, frame, frameTextures)) {
+ QVideoFrame frameTmp = frame;
+ auto videoFrameTextures = QVideoTextureHelper::createTextures(frameTmp, rhi, rub, {});
+ if (!videoFrameTextures) {
+ qCDebug(qLcVideoFrameConverter) << "Failed obtain textures. Using CPU conversion.";
+ return convertCPU(frame, rotation, mirrorX, mirrorY);
+ }
+
+ if (!updateTextures(rhi, uniformBuffer, textureSampler, shaderResourceBindings,
+ graphicsPipeline, renderPass, frameTmp, videoFrameTextures)) {
qCDebug(qLcVideoFrameConverter) << "Failed to update textures. Using CPU conversion.";
return convertCPU(frame, rotation, mirrorX, mirrorY);
}
@@ -418,7 +391,8 @@ QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle
cb->setViewport({ 0, 0, float(frameSize.width()), float(frameSize.height()) });
cb->setShaderResources(shaderResourceBindings.get());
- quint32 vertexOffset = quint32(sizeof(float)) * 16 * rotationIndex;
+ const int rotationIndex = (qToUnderlying(rotation) / 90) % 4;
+ const quint32 vertexOffset = quint32(sizeof(float)) * 16 * rotationIndex;
const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer.get(), vertexOffset);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(4);
@@ -441,19 +415,43 @@ QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle
return convertCPU(frame, rotation, mirrorX, mirrorY);
}
- if (!qConverterForFormat(frame.pixelFormat())) {
- qCDebug(qLcVideoFrameConverter) << "Unsupported pixel format" << frame.pixelFormat();
+ QByteArray *imageData = new QByteArray(readResult.data);
+
+ return QImage(reinterpret_cast<const uchar *>(imageData->constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888_Premultiplied, imageCleanupHandler, imageData);
+}
+
+QImage videoFramePlaneAsImage(QVideoFrame &frame, int plane, QImage::Format targetFormat,
+ QSize targetSize)
+{
+ if (plane >= frame.planeCount())
+ return {};
+
+ if (!frame.map(QVideoFrame::ReadOnly)) {
+ qWarning() << "Cannot map a video frame in ReadOnly mode!";
return {};
}
- QImage::Format format = pixelFormatHasAlpha(frame.pixelFormat()) ?
- QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
+ auto frameHandle = QVideoFramePrivate::handle(frame);
- QByteArray *imageData = new QByteArray(readResult.data);
+ // With incrementing the reference counter, we share the mapped QVideoFrame
+ // with the target QImage. The function imageCleanupFunction is going to adopt
+ // the frameHandle by QVideoFrame and dereference it upon the destruction.
+ frameHandle->ref.ref();
- return QImage(reinterpret_cast<const uchar *>(imageData->constData()),
- readResult.pixelSize.width(), readResult.pixelSize.height(),
- format, imageCleanupHandler, imageData);
+ auto imageCleanupFunction = [](void *data) {
+ QVideoFrame frame = reinterpret_cast<QVideoFramePrivate *>(data)->adoptThisByVideoFrame();
+ Q_ASSERT(frame.isMapped());
+ frame.unmap();
+ };
+
+ const auto bytesPerLine = frame.bytesPerLine(plane);
+ const auto height =
+ bytesPerLine ? qMin(targetSize.height(), frame.mappedBytes(plane) / bytesPerLine) : 0;
+
+ return QImage(reinterpret_cast<const uchar *>(frame.bits(plane)), targetSize.width(), height,
+ bytesPerLine, targetFormat, imageCleanupFunction, frameHandle);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/video/qvideoframeconverter_p.h b/src/multimedia/video/qvideoframeconverter_p.h
index d912728fb..d22491f66 100644
--- a/src/multimedia/video/qvideoframeconverter_p.h
+++ b/src/multimedia/video/qvideoframeconverter_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOFRAMECONVERTER_H
#define QVIDEOFRAMECONVERTER_H
@@ -55,7 +19,14 @@
QT_BEGIN_NAMESPACE
-Q_MULTIMEDIA_EXPORT QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation = QVideoFrame::Rotation0, bool mirrorX = false, bool mirrorY = false);
+Q_MULTIMEDIA_EXPORT QImage qImageFromVideoFrame(const QVideoFrame &frame, QtVideo::Rotation rotation = QtVideo::Rotation::None, bool mirrorX = false, bool mirrorY = false);
+
+/**
+ * @brief Maps the video frame and returns an image having a shared ownership for the video frame
+ * and referencing to its mapped data.
+ */
+Q_MULTIMEDIA_EXPORT QImage videoFramePlaneAsImage(QVideoFrame &frame, int plane,
+ QImage::Format targetFromat, QSize targetSize);
QT_END_NAMESPACE
diff --git a/src/multimedia/video/qvideoframeformat.cpp b/src/multimedia/video/qvideoframeformat.cpp
index ead8a4655..b3177234f 100644
--- a/src/multimedia/video/qvideoframeformat.cpp
+++ b/src/multimedia/video/qvideoframeformat.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideoframeformat.h"
#include "qvideotexturehelper_p.h"
@@ -48,7 +12,7 @@
#include <qmatrix4x4.h>
static void initResource() {
- Q_INIT_RESOURCE(shaders);
+ Q_INIT_RESOURCE(qtmultimedia_shaders);
}
QT_BEGIN_NAMESPACE
@@ -75,7 +39,8 @@ public:
&& viewport == other.viewport
&& frameRatesEqual(frameRate, other.frameRate)
&& colorSpace == other.colorSpace
- && mirrored == other.mirrored)
+ && mirrored == other.mirrored
+ && rotation == other.rotation)
return true;
return false;
@@ -96,6 +61,7 @@ public:
float frameRate = 0.0;
float maxLuminance = -1.;
bool mirrored = false;
+ QtVideo::Rotation rotation = QtVideo::Rotation::None;
};
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QVideoFrameFormatPrivate);
@@ -112,7 +78,7 @@ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QVideoFrameFormatPrivate);
A video sink presents a stream of video frames. QVideoFrameFormat describes the type of
the frames and determines how they should be presented.
- The core properties of a video stream required to setup a video sink are the pixel format
+ The core properties of a video stream required to set up a video sink are the pixel format
given by pixelFormat(), and the frame dimensions given by frameSize().
The region of a frame that is actually displayed on a video surface is given by the viewport().
@@ -408,6 +374,12 @@ QVideoFrameFormat::QVideoFrameFormat(const QVideoFrameFormat &other) = default;
*/
/*!
+ \fn void QVideoFrameFormat::swap(QVideoFrameFormat &other) noexcept
+
+ Swaps the current video frame format with the \a other.
+*/
+
+/*!
Assigns the values of \a other to this object.
*/
QVideoFrameFormat &QVideoFrameFormat::operator =(const QVideoFrameFormat &other) = default;
@@ -568,12 +540,13 @@ void QVideoFrameFormat::setScanLineDirection(Direction direction)
d->scanLineDirection = direction;
}
+#if QT_DEPRECATED_SINCE(6, 8)
/*!
Returns the frame rate of a video stream in frames per second.
*/
qreal QVideoFrameFormat::frameRate() const
{
- return d->frameRate;
+ return streamFrameRate();
}
/*!
@@ -581,10 +554,28 @@ qreal QVideoFrameFormat::frameRate() const
*/
void QVideoFrameFormat::setFrameRate(qreal rate)
{
+ setStreamFrameRate(rate);
+}
+#endif
+
+/*!
+ Returns the frame rate of a video stream in frames per second.
+*/
+qreal QVideoFrameFormat::streamFrameRate() const
+{
+ return d->frameRate;
+}
+
+/*!
+ Sets the frame \a rate of a video stream in frames per second.
+*/
+void QVideoFrameFormat::setStreamFrameRate(qreal rate)
+{
detach();
d->frameRate = rate;
}
+#if QT_DEPRECATED_SINCE(6, 4)
/*!
\deprecated Use colorSpace() instead
@@ -606,6 +597,7 @@ void QVideoFrameFormat::setYCbCrColorSpace(QVideoFrameFormat::YCbCrColorSpace sp
detach();
d->colorSpace = ColorSpace(space);
}
+#endif // QT_DEPRECATED_SINCE(6, 4)
/*!
Returns the color space of a video stream.
@@ -693,6 +685,23 @@ void QVideoFrameFormat::setMirrored(bool mirrored)
}
/*!
+ Returns the rotation angle the matching video frame should be rotated clockwise before displaying.
+ */
+QtVideo::Rotation QVideoFrameFormat::rotation() const
+{
+ return d->rotation;
+}
+
+/*!
+ Sets the \a rotation angle the matching video frame should be rotated clockwise before displaying.
+ */
+void QVideoFrameFormat::setRotation(QtVideo::Rotation rotation)
+{
+ detach();
+ d->rotation = rotation;
+}
+
+/*!
\internal
*/
QString QVideoFrameFormat::vertexShaderFileName() const
@@ -733,7 +742,9 @@ float QVideoFrameFormat::maxLuminance() const
}
return d->maxLuminance;
}
-
+/*!
+ Sets the maximum luminance to the given value, \a lum.
+*/
void QVideoFrameFormat::setMaxLuminance(float lum)
{
detach();
@@ -769,7 +780,9 @@ QVideoFrameFormat::PixelFormat QVideoFrameFormat::pixelFormatFromImageFormat(QIm
case QImage::Format_RGBA8888:
return QVideoFrameFormat::Format_RGBA8888;
case QImage::Format_RGBA8888_Premultiplied:
- return QVideoFrameFormat::Format_ARGB8888_Premultiplied;
+ // QVideoFrameFormat::Format_RGBA8888_Premultiplied is to be added in 6.8
+ // Format_RGBX8888 suits the best as a workaround
+ return QVideoFrameFormat::Format_RGBX8888;
case QImage::Format_RGBX8888:
return QVideoFrameFormat::Format_RGBX8888;
case QImage::Format_Grayscale8:
@@ -925,6 +938,7 @@ QString QVideoFrameFormat::pixelFormatToString(QVideoFrameFormat::PixelFormat pi
}
#ifndef QT_NO_DEBUG_STREAM
+# if QT_DEPRECATED_SINCE(6, 4)
QDebug operator<<(QDebug dbg, QVideoFrameFormat::YCbCrColorSpace cs)
{
QDebugStateSaver saver(dbg);
@@ -954,6 +968,7 @@ QDebug operator<<(QDebug dbg, QVideoFrameFormat::YCbCrColorSpace cs)
}
return dbg;
}
+# endif // QT_DEPRECATED_SINCE(6, 4)
QDebug operator<<(QDebug dbg, QVideoFrameFormat::ColorSpace cs)
{
@@ -1006,7 +1021,7 @@ QDebug operator<<(QDebug dbg, const QVideoFrameFormat &f)
<< "\n frame size=" << f.frameSize()
<< "\n viewport=" << f.viewport()
<< "\n colorSpace=" << f.colorSpace()
- << "\n frameRate=" << f.frameRate()
+ << "\n frameRate=" << f.streamFrameRate()
<< "\n mirrored=" << f.isMirrored();
return dbg;
diff --git a/src/multimedia/video/qvideoframeformat.h b/src/multimedia/video/qvideoframeformat.h
index 3925f10a2..18dc9952d 100644
--- a/src/multimedia/video/qvideoframeformat.h
+++ b/src/multimedia/video/qvideoframeformat.h
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOSURFACEFORMAT_H
#define QVIDEOSURFACEFORMAT_H
#include <QtMultimedia/qtmultimediaglobal.h>
+#include <QtMultimedia/qtvideo.h>
#include <QtCore/qlist.h>
#include <QtCore/qmetatype.h>
@@ -189,12 +154,21 @@ public:
Direction scanLineDirection() const;
void setScanLineDirection(Direction direction);
+#if QT_DEPRECATED_SINCE(6, 8)
+ QT_DEPRECATED_VERSION_X_6_8("Use streamFrameRate()")
qreal frameRate() const;
+ QT_DEPRECATED_VERSION_X_6_8("Use setStreamFrameRate()")
void setFrameRate(qreal rate);
+#endif
+
+ qreal streamFrameRate() const;
+ void setStreamFrameRate(qreal rate);
#if QT_DEPRECATED_SINCE(6, 4)
- QT_DEPRECATED_VERSION_X_6_0("Use colorSpace()") YCbCrColorSpace yCbCrColorSpace() const;
- QT_DEPRECATED_VERSION_X_6_0("Use setColorSpace()") void setYCbCrColorSpace(YCbCrColorSpace colorSpace);
+ QT_DEPRECATED_VERSION_X_6_4("Use colorSpace()")
+ YCbCrColorSpace yCbCrColorSpace() const;
+ QT_DEPRECATED_VERSION_X_6_4("Use setColorSpace()")
+ void setYCbCrColorSpace(YCbCrColorSpace colorSpace);
#endif
ColorSpace colorSpace() const;
@@ -209,6 +183,9 @@ public:
bool isMirrored() const;
void setMirrored(bool mirrored);
+ QtVideo::Rotation rotation() const;
+ void setRotation(QtVideo::Rotation rotation);
+
QString vertexShaderFileName() const;
QString fragmentShaderFileName() const;
void updateUniformData(QByteArray *dst, const QVideoFrame &frame, const QMatrix4x4 &transform, float opacity) const;
@@ -230,8 +207,10 @@ Q_DECLARE_SHARED(QVideoFrameFormat)
#ifndef QT_NO_DEBUG_STREAM
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, const QVideoFrameFormat &);
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QVideoFrameFormat::Direction);
-QT_DEPRECATED_VERSION_X_6_0("Use QVideoFrameFormat::ColorSpace")
+#if QT_DEPRECATED_SINCE(6, 4)
+QT_DEPRECATED_VERSION_X_6_4("Use QVideoFrameFormat::ColorSpace")
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QVideoFrameFormat::YCbCrColorSpace);
+#endif
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QVideoFrameFormat::ColorSpace);
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, QVideoFrameFormat::PixelFormat);
#endif
diff --git a/src/multimedia/video/qvideooutputorientationhandler.cpp b/src/multimedia/video/qvideooutputorientationhandler.cpp
index 86e6bd664..ff91bd7fb 100644
--- a/src/multimedia/video/qvideooutputorientationhandler.cpp
+++ b/src/multimedia/video/qvideooutputorientationhandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideooutputorientationhandler_p.h"
@@ -54,8 +18,8 @@ QVideoOutputOrientationHandler::QVideoOutputOrientationHandler(QObject *parent)
if (!screen)
return;
- connect(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)),
- this, SLOT(screenOrientationChanged(Qt::ScreenOrientation)));
+ connect(screen, &QScreen::orientationChanged, this,
+ &QVideoOutputOrientationHandler::screenOrientationChanged);
screenOrientationChanged(screen->orientation());
}
@@ -84,3 +48,5 @@ void QVideoOutputOrientationHandler::screenOrientationChanged(Qt::ScreenOrientat
}
QT_END_NAMESPACE
+
+#include "moc_qvideooutputorientationhandler_p.cpp"
diff --git a/src/multimedia/video/qvideooutputorientationhandler_p.h b/src/multimedia/video/qvideooutputorientationhandler_p.h
index dc7702a1f..ae31f333b 100644
--- a/src/multimedia/video/qvideooutputorientationhandler_p.h
+++ b/src/multimedia/video/qvideooutputorientationhandler_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOOUTPUTORIENTATIONHANDLER_P_H
#define QVIDEOOUTPUTORIENTATIONHANDLER_P_H
diff --git a/src/multimedia/video/qvideosink.cpp b/src/multimedia/video/qvideosink.cpp
index f70763157..4551a8960 100644
--- a/src/multimedia/video/qvideosink.cpp
+++ b/src/multimedia/video/qvideosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideosink.h"
@@ -58,7 +22,12 @@ public:
QVideoSinkPrivate(QVideoSink *q)
: q_ptr(q)
{
- videoSink = QPlatformMediaIntegration::instance()->createVideoSink(q);
+ auto maybeVideoSink = QPlatformMediaIntegration::instance()->createVideoSink(q);
+ if (maybeVideoSink) {
+ videoSink = maybeVideoSink.value();
+ } else {
+ qWarning() << "Failed to create QVideoSink" << maybeVideoSink.error();
+ }
}
~QVideoSinkPrivate()
{
@@ -122,12 +91,12 @@ QVideoSink::QVideoSink(QObject *parent)
*/
QVideoSink::~QVideoSink()
{
+ disconnect(this);
d->unregisterSource();
delete d;
}
/*!
- \internal
Returns the QRhi instance being used to create texture data in the video frames.
*/
QRhi *QVideoSink::rhi() const
@@ -145,7 +114,8 @@ void QVideoSink::setRhi(QRhi *rhi)
if (d->rhi == rhi)
return;
d->rhi = rhi;
- d->videoSink->setRhi(rhi);
+ if (d->videoSink)
+ d->videoSink->setRhi(rhi);
}
/*!
@@ -161,23 +131,31 @@ QPlatformVideoSink *QVideoSink::platformVideoSink() const
*/
QVideoFrame QVideoSink::videoFrame() const
{
- return d->videoSink->currentVideoFrame();
+ return d->videoSink ? d->videoSink->currentVideoFrame() : QVideoFrame{};
}
/*!
+ \fn void QVideoSink::videoFrameChanged(const QVideoFrame &frame) const
+
+ Signals when the video \a frame changes.
+*/
+/*!
Sets the current video \a frame.
*/
void QVideoSink::setVideoFrame(const QVideoFrame &frame)
{
- d->videoSink->setVideoFrame(frame);
+ if (d->videoSink)
+ d->videoSink->setVideoFrame(frame);
}
/*!
+ \property QVideoSink::subtitleText
+
Returns the current subtitle text.
*/
QString QVideoSink::subtitleText() const
{
- return d->videoSink->subtitleText();
+ return d->videoSink ? d->videoSink->subtitleText() : QString{};
}
/*!
@@ -185,10 +163,13 @@ QString QVideoSink::subtitleText() const
*/
void QVideoSink::setSubtitleText(const QString &subtitle)
{
- d->videoSink->setSubtitleText(subtitle);
+ if (d->videoSink)
+ d->videoSink->setSubtitleText(subtitle);
}
/*!
+ \property QVideoSink::videoSize
+
Returns the size of the video currently being played back. If no video is
being played, this method returns an invalid size.
*/
diff --git a/src/multimedia/video/qvideosink.h b/src/multimedia/video/qvideosink.h
index 0e7f3d8cd..1a2e77834 100644
--- a/src/multimedia/video/qvideosink.h
+++ b/src/multimedia/video/qvideosink.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTVIDEOSINK_H
#define QABSTRACTVIDEOSINK_H
@@ -57,6 +21,8 @@ class QRhi;
class Q_MULTIMEDIA_EXPORT QVideoSink : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QString subtitleText READ subtitleText WRITE setSubtitleText NOTIFY subtitleTextChanged)
+ Q_PROPERTY(QSize videoSize READ videoSize NOTIFY videoSizeChanged)
public:
QVideoSink(QObject *parent = nullptr);
~QVideoSink();
@@ -74,9 +40,8 @@ public:
QPlatformVideoSink *platformVideoSink() const;
Q_SIGNALS:
- void videoFrameChanged(const QVideoFrame &frame) const;
- void subtitleTextChanged(const QString &subtitleText) const;
-
+ void videoFrameChanged(const QVideoFrame &frame) QT6_ONLY(const);
+ void subtitleTextChanged(const QString &subtitleText) QT6_ONLY(const);
void videoSizeChanged();
private:
diff --git a/src/multimedia/video/qvideotexturehelper.cpp b/src/multimedia/video/qvideotexturehelper.cpp
index 48d328b51..e9c26044c 100644
--- a/src/multimedia/video/qvideotexturehelper.cpp
+++ b/src/multimedia/video/qvideotexturehelper.cpp
@@ -1,53 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideotexturehelper_p.h"
-#include "qvideoframe.h"
#include "qabstractvideobuffer_p.h"
+#include "qvideoframeconverter_p.h"
#include <qpainter.h>
#include <qloggingcategory.h>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcVideoTextureHelper, "qt.multimedia.video.texturehelper")
-
namespace QVideoTextureHelper
{
@@ -138,7 +100,7 @@ static const TextureDescription descriptions[QVideoFrameFormat::NPixelFormats] =
},
// Format_YUV422P
{ 3, 1,
- [](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
+ [](int stride, int height) { return stride * height * 2; },
{ QRhiTexture::R8, QRhiTexture::R8, QRhiTexture::R8 },
{ { 1, 1 }, { 2, 1 }, { 2, 1 } }
},
@@ -251,7 +213,7 @@ static const TextureDescription descriptions[QVideoFrameFormat::NPixelFormats] =
{ { 1, 1 }, { 1, 1 }, { 1, 1 } }
},
// Format_YUV420P10
- { 3, 1,
+ { 3, 2,
[](int stride, int height) { return stride * ((height * 3 / 2 + 1) & ~1); },
{ QRhiTexture::R16, QRhiTexture::R16, QRhiTexture::R16 },
{ { 1, 1 }, { 2, 2 }, { 2, 2 } }
@@ -408,6 +370,8 @@ QString fragmentShaderFileName(const QVideoFrameFormat &format, QRhiSwapChain::F
// d = 1.42
// e = 1.772
//
+
+// clang-format off
static QMatrix4x4 colorMatrix(const QVideoFrameFormat &format)
{
auto colorSpace = format.colorSpace();
@@ -421,86 +385,60 @@ static QMatrix4x4 colorMatrix(const QVideoFrameFormat &format)
}
switch (colorSpace) {
case QVideoFrameFormat::ColorSpace_AdobeRgb:
- return QMatrix4x4(
+ return {
1.0f, 0.000f, 1.402f, -0.701f,
1.0f, -0.344f, -0.714f, 0.529f,
1.0f, 1.772f, 0.000f, -0.886f,
- 0.0f, 0.000f, 0.000f, 1.0000f);
+ 0.0f, 0.000f, 0.000f, 1.000f
+ };
default:
case QVideoFrameFormat::ColorSpace_BT709:
if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
- return QMatrix4x4(
- 1.f, 0.000f, 1.5748f, -0.8774f,
- 1.f, -0.187324f, -0.468124f, 0.327724f,
- 1.f, 1.8556f, 0.000f, -0.9278f,
- 0.0f, 0.000f, 0.000f, 1.0000f);
- return QMatrix4x4(
- 1.1644f, 0.000f, 1.7928f, -0.9731f,
- 1.1644f, -0.5329f, -0.2132f, 0.3015f,
- 1.1644f, 2.1124f, 0.000f, -1.1335f,
- 0.0f, 0.000f, 0.000f, 1.0000f);
+ return {
+ 1.0f, 0.0f, 1.5748f, -0.790488f,
+ 1.0f, -0.187324f, -0.468124f, 0.329010f,
+ 1.0f, 1.855600f, 0.0f, -0.931439f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+ };
+ return {
+ 1.1644f, 0.0000f, 1.7927f, -0.9729f,
+ 1.1644f, -0.2132f, -0.5329f, 0.3015f,
+ 1.1644f, 2.1124f, 0.0000f, -1.1334f,
+ 0.0000f, 0.0000f, 0.0000f, 1.0000f
+ };
case QVideoFrameFormat::ColorSpace_BT2020:
if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
- return QMatrix4x4(
- 1.f, 0.000f, 1.4746f, -0.7373f,
- 1.f, -0.2801f, -0.91666f, 0.5984f,
- 1.f, 1.8814f, 0.000f, -0.9407f,
- 0.0f, 0.000f, 0.000f, 1.0000f);
- return QMatrix4x4(
- 1.1644f, 0.000f, 1.6787f, -0.9158f,
- 1.1644f, -0.1874f, -0.6511f, 0.3478f,
- 1.1644f, 2.1418f, 0.000f, -1.1483f,
- 0.0f, 0.000f, 0.000f, 1.0000f);
+ return {
+ 1.f, 0.0000f, 1.4746f, -0.7402f,
+ 1.f, -0.1646f, -0.5714f, 0.3694f,
+ 1.f, 1.8814f, 0.000f, -0.9445f,
+ 0.0f, 0.0000f, 0.000f, 1.0000f
+ };
+ return {
+ 1.1644f, 0.000f, 1.6787f, -0.9157f,
+ 1.1644f, -0.1874f, -0.6504f, 0.3475f,
+ 1.1644f, 2.1418f, 0.0000f, -1.1483f,
+ 0.0000f, 0.0000f, 0.0000f, 1.0000f
+ };
case QVideoFrameFormat::ColorSpace_BT601:
// Corresponds to the primaries used by NTSC BT601. For PAL BT601, we use the BT709 conversion
// as those are very close.
if (format.colorRange() == QVideoFrameFormat::ColorRange_Full)
- return QMatrix4x4(
- 1.f, 0.000f, 1.772f, -0.886f,
+ return {
+ 1.f, 0.000f, 1.772f, -0.886f,
1.f, -0.1646f, -0.57135f, 0.36795f,
- 1.f, 1.42f, 0.000f, -0.71f,
- 0.0f, 0.000f, 0.000f, 1.0000f);
- return QMatrix4x4(
+ 1.f, 1.42f, 0.000f, -0.71f,
+ 0.0f, 0.000f, 0.000f, 1.0000f
+ };
+ return {
1.164f, 0.000f, 1.596f, -0.8708f,
1.164f, -0.392f, -0.813f, 0.5296f,
- 1.164f, 2.017f, 0.000f, -1.081f,
- 0.0f, 0.000f, 0.000f, 1.0000f);
+ 1.164f, 2.017f, 0.000f, -1.0810f,
+ 0.000f, 0.000f, 0.000f, 1.0000f
+ };
}
}
-
-#if 0
-static QMatrix4x4 yuvColorCorrectionMatrix(float brightness, float contrast, float hue, float saturation)
-{
- // Color correction in YUV space is done as follows:
-
- // The formulas assumes values in range 0-255, and a blackpoint of Y=16, whitepoint of Y=235
- //
- // Bightness: b
- // Contrast: c
- // Hue: h
- // Saturation: s
- //
- // Y' = (Y - 16)*c + b + 16
- // U' = ((U - 128)*cos(h) + (V - 128)*sin(h))*c*s + 128
- // V' = ((V - 128)*cos(h) - (U - 128)*sin(h))*c*s + 128
- //
- // For normalized YUV values (0-1 range) as we have them in the pixel shader, this translates to:
- //
- // Y' = (Y - .0625)*c + b + .0625
- // U' = ((U - .5)*cos(h) + (V - .5)*sin(h))*c*s + .5
- // V' = ((V - .5)*cos(h) - (U - .5)*sin(h))*c*s + .5
- //
- // The values need to be clamped to 0-1 after the correction and before converting to RGB
- // The transformation can be encoded in a 4x4 matrix assuming we have an A component of 1
-
- float chcs = cos(hue)*contrast*saturation;
- float shcs = sin(hue)*contrast*saturation;
- return QMatrix4x4(contrast, 0, 0, .0625*(1 - contrast) + brightness,
- 0, chcs, shcs, .5*(1 - chcs - shcs),
- 0, -shcs, chcs, .5*(1 + shcs - chcs),
- 0, 0, 0, 1);
-}
-#endif
+// clang-format on
// PQ transfer function, see also https://en.wikipedia.org/wiki/Perceptual_quantizer
// or https://ieeexplore.ieee.org/document/7291452
@@ -623,12 +561,17 @@ void updateUniformData(QByteArray *dst, const QVideoFrameFormat &format, const Q
ud->maxLum = fromLinear(float(maxNits)/100.f);
}
-static bool updateTextureWithMap(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, int plane, std::unique_ptr<QRhiTexture> &tex)
+enum class UpdateTextureWithMapResult : uint8_t {
+ Failed,
+ UpdatedWithDataCopy,
+ UpdatedWithDataReference
+};
+
+static UpdateTextureWithMapResult updateTextureWithMap(const QVideoFrame &frame, QRhi *rhi,
+ QRhiResourceUpdateBatch *rub, int plane,
+ std::unique_ptr<QRhiTexture> &tex)
{
- if (!frame.map(QVideoFrame::ReadOnly)) {
- qWarning() << "could not map data of QVideoFrame for upload";
- return false;
- }
+ Q_ASSERT(frame.isMapped());
QVideoFrameFormat fmt = frame.surfaceFormat();
QVideoFrameFormat::PixelFormat pixelFormat = fmt.pixelFormat();
@@ -642,7 +585,7 @@ static bool updateTextureWithMap(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdat
tex.reset(rhi->newTexture(texDesc.textureFormat[plane], planeSize, 1, {}));
if (!tex) {
qWarning("Failed to create new texture (size %dx%d)", planeSize.width(), planeSize.height());
- return false;
+ return UpdateTextureWithMapResult::Failed;
}
}
@@ -651,30 +594,40 @@ static bool updateTextureWithMap(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdat
tex->setPixelSize(planeSize);
if (!tex->create()) {
qWarning("Failed to create texture (size %dx%d)", planeSize.width(), planeSize.height());
- return false;
+ return UpdateTextureWithMapResult::Failed;
}
}
+ auto result = UpdateTextureWithMapResult::UpdatedWithDataCopy;
+
QRhiTextureSubresourceUploadDescription subresDesc;
- QImage image;
+
if (pixelFormat == QVideoFrameFormat::Format_Jpeg) {
+ Q_ASSERT(plane == 0);
+
+ QImage image;
+
+ // calling QVideoFrame::toImage is not accurate. To be fixed.
image = frame.toImage();
image.convertTo(QImage::Format_ARGB32);
- subresDesc.setData(QByteArray((const char *)image.bits(), image.bytesPerLine()*image.height()));
- subresDesc.setDataStride(image.bytesPerLine());
+ subresDesc.setImage(image);
+
} else {
- subresDesc.setData(QByteArray::fromRawData((const char *)frame.bits(plane), frame.mappedBytes(plane)));
+ // Note, QByteArray::fromRawData creare QByteArray as a view without data copying
+ subresDesc.setData(QByteArray::fromRawData(
+ reinterpret_cast<const char *>(frame.bits(plane)), frame.mappedBytes(plane)));
subresDesc.setDataStride(frame.bytesPerLine(plane));
+ result = UpdateTextureWithMapResult::UpdatedWithDataReference;
}
QRhiTextureUploadEntry entry(0, 0, subresDesc);
QRhiTextureUploadDescription desc({ entry });
rub->uploadTexture(tex.get(), desc);
- return true;
+ return result;
}
-static bool updateTextureWithHandle(QVideoFrame frame, QRhi *rhi, int plane, std::unique_ptr<QRhiTexture> &tex)
+static std::unique_ptr<QRhiTexture> createTextureFromHandle(const QVideoFrame &frame, QRhi *rhi, int plane)
{
QVideoFrameFormat fmt = frame.surfaceFormat();
QVideoFrameFormat::PixelFormat pixelFormat = fmt.pixelFormat();
@@ -697,38 +650,103 @@ static bool updateTextureWithHandle(QVideoFrame frame, QRhi *rhi, int plane, std
#endif
}
- if (quint64 handle = frame.textureHandle(plane); handle) {
- tex.reset(rhi->newTexture(texDesc.textureFormat[plane], planeSize, 1, textureFlags));
- if (!tex->createFrom({handle, 0})) {
- qWarning("Failed to initialize QRhiTexture wrapper for native texture object %llu",handle);
- return false;
- }
- } else {
- qCDebug(qLcVideoTextureHelper) << "Incorrect texture handle from QVideoFrame, trying to map and upload texture";
- return false;
+ if (quint64 handle = frame.videoBuffer()->textureHandle(rhi, plane); handle) {
+ std::unique_ptr<QRhiTexture> tex(rhi->newTexture(texDesc.textureFormat[plane], planeSize, 1, textureFlags));
+ if (tex->createFrom({handle, 0}))
+ return tex;
+
+ qWarning("Failed to initialize QRhiTexture wrapper for native texture object %llu",handle);
}
- return true;
+ return {};
}
-void updateRhiTexture(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, int plane, std::unique_ptr<QRhiTexture> &tex)
+class QVideoFrameTexturesArray : public QVideoFrameTextures
+{
+public:
+ using TextureArray = std::array<std::unique_ptr<QRhiTexture>, TextureDescription::maxPlanes>;
+ QVideoFrameTexturesArray(TextureArray &&textures, QVideoFrame mappedFrame = {})
+ : m_textures(std::move(textures)), m_mappedFrame(std::move(mappedFrame))
+ {
+ Q_ASSERT(!m_mappedFrame.isValid() || m_mappedFrame.isReadable());
+ }
+
+ // We keep the source frame mapped during the target texture lifetime.
+ // Alternatively, we may use setting a custom image to QRhiTextureSubresourceUploadDescription,
+ // unsig videoFramePlaneAsImage, however, the OpenGL rendering pipeline in QRhi
+ // may keep QImage, and consequently the mapped QVideoFrame,
+ // even after the target texture is deleted: QTBUG-123174.
+ ~QVideoFrameTexturesArray() { m_mappedFrame.unmap(); }
+
+ QRhiTexture *texture(uint plane) const override
+ {
+ return plane < std::size(m_textures) ? m_textures[plane].get() : nullptr;
+ }
+
+ TextureArray takeTextures() { return std::move(m_textures); }
+
+private:
+ TextureArray m_textures;
+ QVideoFrame m_mappedFrame;
+};
+
+static std::unique_ptr<QVideoFrameTextures> createTexturesFromHandles(const QVideoFrame &frame, QRhi *rhi)
{
const TextureDescription &texDesc = descriptions[frame.surfaceFormat().pixelFormat()];
- if (plane >= texDesc.nplanes) {
- tex.reset();
- return;
+ bool ok = true;
+ QVideoFrameTexturesArray::TextureArray textures;
+ for (quint8 plane = 0; plane < texDesc.nplanes; ++plane) {
+ textures[plane] = QVideoTextureHelper::createTextureFromHandle(frame, rhi, plane);
+ ok &= bool(textures[plane]);
}
+ if (ok)
+ return std::make_unique<QVideoFrameTexturesArray>(std::move(textures));
+ else
+ return {};
+}
- if (frame.handleType() == QVideoFrame::RhiTextureHandle) {
- if (std::unique_ptr<QRhiTexture> ftex = frame.rhiTexture(plane); ftex) {
- tex = std::move(ftex);
- return;
- }
+static std::unique_ptr<QVideoFrameTextures> createTexturesFromMemory(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, QVideoFrameTextures *old)
+{
+ const TextureDescription &texDesc = descriptions[frame.surfaceFormat().pixelFormat()];
+ QVideoFrameTexturesArray::TextureArray textures;
+ auto oldArray = dynamic_cast<QVideoFrameTexturesArray *>(old);
+ if (oldArray)
+ textures = oldArray->takeTextures();
+
+ if (!frame.map(QVideoFrame::ReadOnly)) {
+ qWarning() << "Cannot map a video frame in ReadOnly mode!";
+ return {};
+ }
+
+ auto unmapFrameGuard = qScopeGuard([&frame] { frame.unmap(); });
+
+ bool shouldKeepMapping = false;
+ for (quint8 plane = 0; plane < texDesc.nplanes; ++plane) {
+ const auto result = updateTextureWithMap(frame, rhi, rub, plane, textures[plane]);
+ if (result == UpdateTextureWithMapResult::Failed)
+ return {};
- if (QVideoTextureHelper::updateTextureWithHandle(frame, rhi, plane, tex))
- return;
+ if (result == UpdateTextureWithMapResult::UpdatedWithDataReference)
+ shouldKeepMapping = true;
}
- QVideoTextureHelper::updateTextureWithMap(frame, rhi, rub, plane, tex);
+ // as QVideoFrame::unmap does nothing with null frames, we just move the frame to the result
+ return std::make_unique<QVideoFrameTexturesArray>(
+ std::move(textures), shouldKeepMapping ? std::move(frame) : QVideoFrame());
+}
+
+std::unique_ptr<QVideoFrameTextures> createTextures(QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, std::unique_ptr<QVideoFrameTextures> &&oldTextures)
+{
+ QAbstractVideoBuffer *vf = frame.videoBuffer();
+ if (!vf)
+ return {};
+
+ if (auto vft = vf->mapTextures(rhi))
+ return vft;
+
+ if (auto vft = createTexturesFromHandles(frame, rhi))
+ return vft;
+
+ return createTexturesFromMemory(frame, rhi, rub, oldTextures.get());
}
bool SubtitleLayout::update(const QSize &frameSize, QString text)
diff --git a/src/multimedia/video/qvideotexturehelper_p.h b/src/multimedia/video/qvideotexturehelper_p.h
index 8ec49c77c..982c1b48a 100644
--- a/src/multimedia/video/qvideotexturehelper_p.h
+++ b/src/multimedia/video/qvideotexturehelper_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOTEXTUREHELPER_H
#define QVIDEOTEXTUREHELPER_H
@@ -52,7 +16,7 @@
//
#include <qvideoframeformat.h>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtGui/qtextlayout.h>
@@ -60,6 +24,7 @@ QT_BEGIN_NAMESPACE
class QVideoFrame;
class QTextLayout;
+class QVideoFrameTextures;
namespace QVideoTextureHelper
{
@@ -99,7 +64,7 @@ Q_MULTIMEDIA_EXPORT QString vertexShaderFileName(const QVideoFrameFormat &format
Q_MULTIMEDIA_EXPORT QString fragmentShaderFileName(const QVideoFrameFormat &format, QRhiSwapChain::Format surfaceFormat = QRhiSwapChain::SDR);
Q_MULTIMEDIA_EXPORT void updateUniformData(QByteArray *dst, const QVideoFrameFormat &format, const QVideoFrame &frame,
const QMatrix4x4 &transform, float opacity, float maxNits = 100);
-Q_MULTIMEDIA_EXPORT void updateRhiTexture(QVideoFrame frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, int plane, std::unique_ptr<QRhiTexture> &tex);
+Q_MULTIMEDIA_EXPORT std::unique_ptr<QVideoFrameTextures> createTextures(QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, std::unique_ptr<QVideoFrameTextures> &&oldTextures);
struct UniformData {
float transformMatrix[4][4];
diff --git a/src/multimedia/video/qvideowindow.cpp b/src/multimedia/video/qvideowindow.cpp
index 2741313b4..9cab23f5f 100644
--- a/src/multimedia/video/qvideowindow.cpp
+++ b/src/multimedia/video/qvideowindow.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qvideowindow_p.h"
#include <QPlatformSurfaceEvent>
@@ -43,6 +7,7 @@
#include <qpainter.h>
#include <private/qguiapplication_p.h>
#include <private/qmemoryvideobuffer_p.h>
+#include <private/qmultimediautils_p.h>
#include <qpa/qplatformintegration.h>
QT_BEGIN_NAMESPACE
@@ -109,7 +74,7 @@ QVideoWindowPrivate::~QVideoWindowPrivate()
q, &QVideoWindow::setVideoFrame);
}
-static const float g_quad[] = {
+static const float g_vw_quad[] = {
// 4 clockwise rotation of texture vertexes (the second pair)
// Rotation 0
-1.f, -1.f, 0.f, 0.f,
@@ -134,7 +99,7 @@ static const float g_quad[] = {
1.f, 1.f, 0.f, 1.f
};
-static QShader getShader(const QString &name)
+static QShader vwGetShader(const QString &name)
{
QFile f(name);
if (f.open(QIODevice::ReadOnly))
@@ -188,13 +153,11 @@ void QVideoWindowPrivate::initRhi()
return;
m_swapChain.reset(m_rhi->newSwapChain());
- if (m_swapChain->isFormatSupported(QRhiSwapChain::HDRExtendedSrgbLinear))
- m_swapChain->setFormat(QRhiSwapChain::HDRExtendedSrgbLinear);
m_swapChain->setWindow(q);
m_renderPass.reset(m_swapChain->newCompatibleRenderPassDescriptor());
m_swapChain->setRenderPassDescriptor(m_renderPass.get());
- m_vertexBuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(g_quad)));
+ m_vertexBuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(g_vw_quad)));
m_vertexBuf->create();
m_vertexBufReady = false;
@@ -218,9 +181,9 @@ void QVideoWindowPrivate::setupGraphicsPipeline(QRhiGraphicsPipeline *pipeline,
{
pipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
- QShader vs = getShader(QVideoTextureHelper::vertexShaderFileName(fmt));
+ QShader vs = vwGetShader(QVideoTextureHelper::vertexShaderFileName(fmt));
Q_ASSERT(vs.isValid());
- QShader fs = getShader(QVideoTextureHelper::fragmentShaderFileName(fmt, m_swapChain->format()));
+ QShader fs = vwGetShader(QVideoTextureHelper::fragmentShaderFileName(fmt, m_swapChain->format()));
Q_ASSERT(fs.isValid());
pipeline->setShaderStages({
{ QRhiShaderStage::Vertex, vs },
@@ -249,8 +212,9 @@ void QVideoWindowPrivate::updateTextures(QRhiResourceUpdateBatch *rub)
m_currentFrame = QVideoFrame(new QMemoryVideoBuffer(QByteArray{4, 0}, 4),
QVideoFrameFormat(QSize(1,1), QVideoFrameFormat::Format_RGBA8888));
- for (int i = 0; i < QVideoTextureHelper::TextureDescription::maxPlanes; ++i)
- QVideoTextureHelper::updateRhiTexture(m_currentFrame, m_rhi.get(), rub, i, m_frameTextures[i]);
+ m_frameTextures = QVideoTextureHelper::createTextures(m_currentFrame, m_rhi.get(), rub, std::move(m_frameTextures));
+ if (!m_frameTextures)
+ return;
QRhiShaderResourceBinding bindings[4];
auto *b = bindings;
@@ -262,7 +226,7 @@ void QVideoWindowPrivate::updateTextures(QRhiResourceUpdateBatch *rub)
for (int i = 0; i < textureDesc->nplanes; ++i)
(*b++) = QRhiShaderResourceBinding::sampledTexture(i + 1, QRhiShaderResourceBinding::FragmentStage,
- m_frameTextures[i].get(), m_textureSampler.get());
+ m_frameTextures->texture(i), m_textureSampler.get());
m_shaderResourceBindings->setBindings(bindings, b);
m_shaderResourceBindings->create();
@@ -367,7 +331,7 @@ void QVideoWindowPrivate::render()
return;
}
- int frameRotationIndex = (m_currentFrame.rotationAngle() / 90) % 4;
+ const int frameRotationIndex = (static_cast<int>(m_currentFrame.rotation()) / 90) % 4;
QSize frameSize = m_currentFrame.size();
if (frameRotationIndex % 2)
frameSize.transpose();
@@ -376,9 +340,17 @@ void QVideoWindowPrivate::render()
videoRect.moveCenter(rect.center());
QRect subtitleRect = videoRect.intersected(rect);
- if (m_swapChain->currentPixelSize() != m_swapChain->surfacePixelSize())
+ if (!m_hasSwapChain || (m_swapChain->currentPixelSize() != m_swapChain->surfacePixelSize()))
resizeSwapChain();
+ const auto requiredSwapChainFormat =
+ qGetRequiredSwapChainFormat(m_currentFrame.surfaceFormat());
+ if (qShouldUpdateSwapChainFormat(m_swapChain.get(), requiredSwapChainFormat)) {
+ releaseSwapChain();
+ m_swapChain->setFormat(requiredSwapChainFormat);
+ resizeSwapChain();
+ }
+
if (!m_hasSwapChain)
return;
@@ -403,7 +375,7 @@ void QVideoWindowPrivate::render()
if (!m_vertexBufReady) {
m_vertexBufReady = true;
- rub->uploadStaticBuffer(m_vertexBuf.get(), g_quad);
+ rub->uploadStaticBuffer(m_vertexBuf.get(), g_vw_quad);
}
if (m_texturesDirty)
@@ -522,7 +494,7 @@ bool QVideoWindow::event(QEvent *e)
case QEvent::Expose:
d->isExposed = isExposed();
if (d->isExposed)
- requestUpdate();
+ d->render();
return true;
default:
@@ -552,3 +524,5 @@ void QVideoWindow::setVideoFrame(const QVideoFrame &frame)
}
QT_END_NAMESPACE
+
+#include "moc_qvideowindow_p.cpp"
diff --git a/src/multimedia/video/qvideowindow_p.h b/src/multimedia/video/qvideowindow_p.h
index 68a47062b..3305d3b40 100644
--- a/src/multimedia/video/qvideowindow_p.h
+++ b/src/multimedia/video/qvideowindow_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOWINDOW_P_H
#define QVIDEOWINDOW_P_H
@@ -52,23 +16,9 @@
//
#include <QWindow>
-#include <qtextlayout.h>
-
-#include <QtGui/private/qrhinull_p.h>
-#if QT_CONFIG(opengl)
-#include <QtGui/private/qrhigles2_p.h>
#include <QOffscreenSurface>
-#endif
-#if QT_CONFIG(vulkan)
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
-#ifdef Q_OS_WIN
-#include <QtGui/private/qrhid3d11_p.h>
-#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
-#include <QtGui/private/qrhimetal_p.h>
-#endif
-
+#include <qtextlayout.h>
+#include <rhi/qrhi.h>
#include <qvideoframe.h>
#include <private/qplatformvideosink_p.h>
#include <private/qvideotexturehelper_p.h>
@@ -114,7 +64,7 @@ public:
std::unique_ptr<QRhiBuffer> m_vertexBuf;
bool m_vertexBufReady = false;
std::unique_ptr<QRhiBuffer> m_uniformBuf;
- std::unique_ptr<QRhiTexture> m_frameTextures[3];
+ std::unique_ptr<QVideoFrameTextures> m_frameTextures;
std::unique_ptr<QRhiSampler> m_textureSampler;
std::unique_ptr<QRhiShaderResourceBindings> m_shaderResourceBindings;
std::unique_ptr<QRhiGraphicsPipeline> m_graphicsPipeline;
diff --git a/src/multimedia/wasm/qwasmaudiodevice.cpp b/src/multimedia/wasm/qwasmaudiodevice.cpp
index 5eb6a3816..c87a0ad54 100644
--- a/src/multimedia/wasm/qwasmaudiodevice.cpp
+++ b/src/multimedia/wasm/qwasmaudiodevice.cpp
@@ -1,44 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwasmaudiodevice_p.h"
#include <emscripten.h>
+#include <emscripten/val.h>
+#include <emscripten/bind.h>
+
#include <AL/al.h>
#include <AL/alc.h>
@@ -48,12 +15,12 @@ QWasmAudioDevice::QWasmAudioDevice(const char *device, const char *desc, bool is
: QAudioDevicePrivate(device, mode)
{
description = QString::fromUtf8(desc);
- isDefault = isDef;
+ isDefault = isDef;
minimumChannelCount = 1;
maximumChannelCount = 2;
- minimumSampleRate = 1;
- maximumSampleRate = 192'000;
+ minimumSampleRate = 8000;
+ maximumSampleRate = 96000; // js AudioContext max according to docs
// native openAL formats
supportedSampleFormats.append(QAudioFormat::UInt8);
@@ -65,13 +32,19 @@ QWasmAudioDevice::QWasmAudioDevice(const char *device, const char *desc, bool is
preferredFormat.setChannelCount(2);
- preferredFormat.setSampleRate(EM_ASM_INT({
- var AudioContext = window.AudioContext || window.webkitAudioContext;
- var ctx = new AudioContext();
- var sr = ctx.sampleRate;
- ctx.close();
- return sr;
- }));
+ // FIXME: firefox
+ // An AudioContext was prevented from starting automatically.
+ // It must be created or resumed after a user gesture on the page.
+ emscripten::val audioContext = emscripten::val::global("window")["AudioContext"].new_();
+ if (audioContext == emscripten::val::undefined())
+ audioContext = emscripten::val::global("window")["webkitAudioContext"].new_();
+
+ if (audioContext != emscripten::val::undefined()) {
+ audioContext.call<void>("resume");
+ int sRate = audioContext["sampleRate"].as<int>();
+ audioContext.call<void>("close");
+ preferredFormat.setSampleRate(sRate);
+ }
auto f = QAudioFormat::Float;
diff --git a/src/multimedia/wasm/qwasmaudiodevice_p.h b/src/multimedia/wasm/qwasmaudiodevice_p.h
index 1b8bc7571..cc86c1575 100644
--- a/src/multimedia/wasm/qwasmaudiodevice_p.h
+++ b/src/multimedia/wasm/qwasmaudiodevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWASMAUDIODEVICEINFO_H
#define QWASMAUDIODEVICEINFO_H
diff --git a/src/multimedia/wasm/qwasmaudiosink.cpp b/src/multimedia/wasm/qwasmaudiosink.cpp
index 82456e0b6..d1068e744 100644
--- a/src/multimedia/wasm/qwasmaudiosink.cpp
+++ b/src/multimedia/wasm/qwasmaudiosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwasmaudiosink_p.h"
@@ -77,11 +41,14 @@ protected:
qint64 writeData(const char *data, qint64 len) override;
};
-QWasmAudioSink::QWasmAudioSink(const QByteArray &device) : m_name(device)
+QWasmAudioSink::QWasmAudioSink(const QByteArray &device, QObject *parent)
+ : QPlatformAudioSink(parent),
+ m_name(device),
+ m_timer(new QTimer(this))
{
- m_timer.setSingleShot(false);
+ m_timer->setSingleShot(false);
aldata = new ALData();
- connect(&m_timer, &QTimer::timeout, this, [this](){
+ connect(m_timer, &QTimer::timeout, this, [this](){
if (m_pullMode)
nextALBuffers();
else {
@@ -177,7 +144,6 @@ void QWasmAudioSink::start(bool mode)
}
alcMakeContextCurrent(aldata->context);
-
alGenSources(1, &aldata->source);
if (m_bufferSize > 0)
@@ -192,8 +158,8 @@ void QWasmAudioSink::start(bool mode)
alSourcef(aldata->source, AL_GAIN, m_volume);
if (m_pullMode)
loadALBuffers();
- m_timer.setInterval(DEFAULT_BUFFER_DURATION / 3000);
- m_timer.start();
+ m_timer->setInterval(DEFAULT_BUFFER_DURATION / 3000);
+ m_timer->start();
if (m_pullMode)
alSourcePlay(aldata->source);
m_running = true;
@@ -208,7 +174,7 @@ void QWasmAudioSink::stop()
m_elapsedTimer.invalidate();
alSourceStop(aldata->source);
alSourceRewind(aldata->source);
- m_timer.stop();
+ m_timer->stop();
m_bufferFragmentsBusyCount = 0;
alDeleteSources(1, &aldata->source);
alDeleteBuffers(m_bufferFragmentsCount, aldata->buffers);
@@ -234,6 +200,7 @@ void QWasmAudioSink::suspend()
if (!m_running)
return;
+ m_suspendedInState = m_state;
alSourcePause(aldata->source);
}
@@ -245,15 +212,15 @@ void QWasmAudioSink::resume()
alSourcePlay(aldata->source);
}
-int QWasmAudioSink::bytesFree() const
+qsizetype QWasmAudioSink::bytesFree() const
{
int processed;
alGetSourcei(aldata->source, AL_BUFFERS_PROCESSED, &processed);
return m_running ? m_bufferFragmentSize * (m_bufferFragmentsCount - m_bufferFragmentsBusyCount
- + processed) : 0;
+ + (qsizetype)processed) : 0;
}
-void QWasmAudioSink::setBufferSize(int value)
+void QWasmAudioSink::setBufferSize(qsizetype value)
{
if (m_running)
return;
@@ -261,7 +228,7 @@ void QWasmAudioSink::setBufferSize(int value)
m_bufferSize = value;
}
-int QWasmAudioSink::bufferSize() const
+qsizetype QWasmAudioSink::bufferSize() const
{
return m_bufferSize;
}
@@ -334,8 +301,12 @@ void QWasmAudioSink::loadALBuffers()
if (m_bufferFragmentsBusyCount == m_bufferFragmentsCount)
return;
+ if (m_device->bytesAvailable() == 0) {
+ return;
+ }
+
auto size = m_device->read(m_tmpData + m_tmpDataOffset, m_bufferFragmentSize -
- m_tmpDataOffset);
+ m_tmpDataOffset);
m_tmpDataOffset += size;
if (!m_tmpDataOffset || (m_tmpDataOffset != m_bufferFragmentSize &&
m_bufferFragmentsBusyCount >= m_bufferFragmentsCount * 2 / 3))
@@ -393,7 +364,7 @@ void QWasmAudioSink::nextALBuffers()
loadALBuffers();
ALint state;
alGetSourcei(aldata->source, AL_SOURCE_STATE, &state);
- if (state != AL_PLAYING)
+ if (state != AL_PLAYING && m_error == QAudio::NoError)
alSourcePlay(aldata->source);
updateState();
}
@@ -405,7 +376,7 @@ void QWasmAudioSink::updateState()
return;
m_state = current;
- if (m_state == QAudio::IdleState && m_running)
+ if (m_state == QAudio::IdleState && m_running && m_device->bytesAvailable() == 0)
setError(QAudio::UnderrunError);
emit stateChanged(m_state);
@@ -417,10 +388,16 @@ void QWasmAudioSink::setError(QAudio::Error error)
if (error == m_error)
return;
m_error = error;
+ if (error != QAudio::NoError) {
+ m_timer->stop();
+ alSourceRewind(aldata->source);
+ }
+
emit errorChanged(error);
}
-QWasmAudioSinkDevice::QWasmAudioSinkDevice(QWasmAudioSink *parent) : QIODevice(parent),
+QWasmAudioSinkDevice::QWasmAudioSinkDevice(QWasmAudioSink *parent)
+ : QIODevice(parent),
m_out(parent)
{
}
diff --git a/src/multimedia/wasm/qwasmaudiosink_p.h b/src/multimedia/wasm/qwasmaudiosink_p.h
index 1c75ec258..975b7f6cc 100644
--- a/src/multimedia/wasm/qwasmaudiosink_p.h
+++ b/src/multimedia/wasm/qwasmaudiosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWASMAUDIOSINK_H
#define QWASMAUDIOSINK_H
@@ -66,12 +30,13 @@ class QWasmAudioSink : public QPlatformAudioSink
QByteArray m_name;
ALData *aldata = nullptr;
- QTimer m_timer;
+ QTimer *m_timer = nullptr;
QIODevice *m_device = nullptr;
QAudioFormat m_format;
QAudio::Error m_error = QAudio::NoError;
bool m_running = false;
QAudio::State m_state = QAudio::StoppedState;
+ QAudio::State m_suspendedInState = QAudio::SuspendedState;
int m_bufferSize = 0;
quint64 m_processed = 0;
QElapsedTimer m_elapsedTimer;
@@ -94,7 +59,7 @@ private slots:
void setError(QAudio::Error);
public:
- QWasmAudioSink(const QByteArray &device);
+ QWasmAudioSink(const QByteArray &device, QObject *parent);
~QWasmAudioSink();
public:
@@ -105,9 +70,9 @@ public:
void reset() override;
void suspend() override;
void resume() override;
- int bytesFree() const override;
- void setBufferSize(int value) override;
- int bufferSize() const override;
+ qsizetype bytesFree() const override;
+ void setBufferSize(qsizetype value) override;
+ qsizetype bufferSize() const override;
qint64 processedUSecs() const override;
QAudio::Error error() const override;
QAudio::State state() const override;
diff --git a/src/multimedia/wasm/qwasmaudiosource.cpp b/src/multimedia/wasm/qwasmaudiosource.cpp
index 981eeefc0..81f222c4b 100644
--- a/src/multimedia/wasm/qwasmaudiosource.cpp
+++ b/src/multimedia/wasm/qwasmaudiosource.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwasmaudiosource_p.h"
@@ -100,18 +64,21 @@ void QWasmAudioSource::writeBuffer()
m_device->write(m_tmpData,bytes);
}
-QWasmAudioSource::QWasmAudioSource(const QByteArray &device)
- : QPlatformAudioSource(), m_name(device)
+QWasmAudioSource::QWasmAudioSource(const QByteArray &device , QObject *parent)
+ : QPlatformAudioSource(parent),
+ m_name(device),
+ m_timer(new QTimer(this))
{
- aldata = new ALData();
- connect(&m_timer, &QTimer::timeout, this, [this](){
- Q_ASSERT(m_running);
- if (m_pullMode)
- writeBuffer();
- else
- if (bytesReady() > 0)
+ if (device.contains("Emscripten")) {
+ aldata = new ALData();
+ connect(m_timer, &QTimer::timeout, this, [this](){
+ Q_ASSERT(m_running);
+ if (m_pullMode)
+ writeBuffer();
+ else if (bytesReady() > 0)
emit m_device->readyRead();
- });
+ });
+ }
}
void QWasmAudioSource::start(QIODevice *device)
@@ -182,8 +149,10 @@ void QWasmAudioSource::start(bool mode)
m_tmpData = new char[m_bufferSize];
else
m_tmpData = nullptr;
- m_timer.setInterval(m_format.durationForBytes(m_bufferSize) / 3'000);
+ m_timer->setInterval(m_format.durationForBytes(m_bufferSize) / 3000);
+ m_timer->start();
+ alcGetError(aldata->device); // clear error state
aldata->device = alcCaptureOpenDevice(m_name.data(), m_format.sampleRate(), format,
m_format.framesForBytes(m_bufferSize));
@@ -201,7 +170,6 @@ void QWasmAudioSource::start(bool mode)
}
m_processed = 0;
m_running = true;
- m_timer.start();
}
void QWasmAudioSource::stop()
@@ -217,7 +185,7 @@ void QWasmAudioSource::stop()
}
if (!m_pullMode)
m_device->deleteLater();
- m_timer.stop();
+ m_timer->stop();
m_running = false;
}
@@ -251,7 +219,7 @@ void QWasmAudioSource::resume()
alcCaptureStart(aldata->device);
}
-int QWasmAudioSource::bytesReady() const
+qsizetype QWasmAudioSource::bytesReady() const
{
if (!m_running)
return 0;
@@ -260,14 +228,14 @@ int QWasmAudioSource::bytesReady() const
return m_format.bytesForFrames(samples);
}
-void QWasmAudioSource::setBufferSize(int value)
+void QWasmAudioSource::setBufferSize(qsizetype value)
{
if (!m_running)
return;
m_bufferSize = value;
}
-int QWasmAudioSource::bufferSize() const
+qsizetype QWasmAudioSource::bufferSize() const
{
return m_bufferSize;
}
diff --git a/src/multimedia/wasm/qwasmaudiosource_p.h b/src/multimedia/wasm/qwasmaudiosource_p.h
index aef81d75b..97b4ec52a 100644
--- a/src/multimedia/wasm/qwasmaudiosource_p.h
+++ b/src/multimedia/wasm/qwasmaudiosource_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWASMAUDIOSOURCE_H
#define QWASMAUDIOSOURCE_H
@@ -65,11 +29,11 @@ class QWasmAudioSource : public QPlatformAudioSource
QByteArray m_name;
ALData *aldata = nullptr;
- QTimer m_timer;
+ QTimer *m_timer = nullptr;
QIODevice *m_device = nullptr;
QAudioFormat m_format;
qreal m_volume = 1;
- int m_bufferSize;
+ qsizetype m_bufferSize;
bool m_running = false;
bool m_suspended = false;
QAudio::Error m_error;
@@ -81,7 +45,7 @@ class QWasmAudioSource : public QPlatformAudioSource
void writeBuffer();
public:
- QWasmAudioSource(const QByteArray &device);
+ QWasmAudioSource(const QByteArray &device, QObject *parent);
public:
void start(QIODevice *device) override;
@@ -91,9 +55,9 @@ public:
void reset() override;
void suspend() override;
void resume() override;
- int bytesReady() const override;
- void setBufferSize(int value) override;
- int bufferSize() const override;
+ qsizetype bytesReady() const override;
+ void setBufferSize(qsizetype value) override;
+ qsizetype bufferSize() const override;
qint64 processedUSecs() const override;
QAudio::Error error() const override;
QAudio::State state() const override;
diff --git a/src/multimedia/wasm/qwasmmediadevices.cpp b/src/multimedia/wasm/qwasmmediadevices.cpp
index 825e19ba2..4e59fd161 100644
--- a/src/multimedia/wasm/qwasmmediadevices.cpp
+++ b/src/multimedia/wasm/qwasmmediadevices.cpp
@@ -1,92 +1,276 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwasmmediadevices_p.h"
#include "private/qcameradevice_p.h"
-
+#include "private/qplatformmediaintegration_p.h"
#include "qwasmaudiosource_p.h"
#include "qwasmaudiosink_p.h"
#include "qwasmaudiodevice_p.h"
#include <AL/al.h>
#include <AL/alc.h>
+#include <QMap>
+#include <QDebug>
+
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(qWasmMediaDevices, "qt.multimedia.wasm.mediadevices")
+
+QWasmCameraDevices::QWasmCameraDevices(QPlatformMediaIntegration *integration)
+ : QPlatformVideoDevices(integration)
+{
+ m_mediaDevices = QPlatformMediaIntegration::instance()->mediaDevices();
+}
+
+QList<QCameraDevice> QWasmCameraDevices::videoDevices() const
+{
+ QWasmMediaDevices *wasmMediaDevices = reinterpret_cast<QWasmMediaDevices *>(m_mediaDevices);
+ return wasmMediaDevices ? wasmMediaDevices->videoInputs() : QList<QCameraDevice>();
+}
+
QWasmMediaDevices::QWasmMediaDevices()
- : QPlatformMediaDevices()
{
- auto capture = alcGetString(nullptr, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
- // present even if there is no capture device
- if (capture)
- m_ins.append((new QWasmAudioDevice(capture, "WebAssembly audio capture device", true,
- QAudioDevice::Input))->create());
+ initDevices();
+}
- auto playback = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
- // present even if there is no playback device
- if (playback)
- m_outs.append((new QWasmAudioDevice(playback, "WebAssembly audio playback device", true,
- QAudioDevice::Output))->create());
+void QWasmMediaDevices::initDevices()
+{
+ if (m_initDone)
+ return;
+
+ m_initDone = true;
+ getOpenALAudioDevices();
+ getMediaDevices(); // asynchronous
}
QList<QAudioDevice> QWasmMediaDevices::audioInputs() const
{
- return m_ins;
+ return m_audioInputs.values();
}
QList<QAudioDevice> QWasmMediaDevices::audioOutputs() const
{
- return m_outs;
+ return m_audioOutputs.values();
}
QList<QCameraDevice> QWasmMediaDevices::videoInputs() const
{
- return {};
+ return m_cameraDevices.values();
+}
+
+QPlatformAudioSource *QWasmMediaDevices::createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent)
+{
+ return new QWasmAudioSource(deviceInfo.id(), parent);
}
-QPlatformAudioSource *QWasmMediaDevices::createAudioSource(const QAudioDevice &deviceInfo)
+QPlatformAudioSink *QWasmMediaDevices::createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
- return new QWasmAudioSource(deviceInfo.id());
+ return new QWasmAudioSink(deviceInfo.id(), parent);
}
-QPlatformAudioSink *QWasmMediaDevices::createAudioSink(const QAudioDevice &deviceInfo)
+void QWasmMediaDevices::parseDevices(emscripten::val devices)
{
- return new QWasmAudioSink(deviceInfo.id());
+ if (devices.isNull() || devices.isUndefined()) {
+ qWarning() << "Something went wrong enumerating devices";
+ return;
+ }
+
+ QList<std::string> cameraDevicesToRemove = m_cameraDevices.keys();
+ QList<std::string> audioOutputsToRemove;
+ QList<std::string> audioInputsToRemove;
+
+ if (m_firstInit) {
+ m_firstInit = false;
+ qWarning() << "m_audioInputs count" << m_audioInputs.count();
+
+ } else {
+ audioOutputsToRemove = m_audioOutputs.keys();
+ audioInputsToRemove = m_audioInputs.keys();
+ m_audioInputsAdded = false;
+ m_audioOutputsAdded = false;
+ }
+ m_videoInputsAdded = false;
+
+ bool m_videoInputsRemoved = false;
+ bool m_audioInputsRemoved = false;
+ bool m_audioOutputsRemoved = false;
+
+ for (int i = 0; i < devices["length"].as<int>(); i++) {
+
+ emscripten::val mediaDevice = devices[i];
+
+ std::string defaultDeviceLabel = "";
+
+ const std::string deviceKind = mediaDevice["kind"].as<std::string>();
+ const std::string label = mediaDevice["label"].as<std::string>();
+ const std::string deviceId = mediaDevice["deviceId"].as<std::string>();
+
+ qCDebug(qWasmMediaDevices) << QString::fromStdString(deviceKind)
+ << QString::fromStdString(deviceId)
+ << QString::fromStdString(label);
+
+ if (deviceKind.empty())
+ continue;
+
+ if (deviceId == std::string("default")) {
+ // chrome specifies the default device with this as deviceId
+ // and then prepends "Default - " with the name of the device
+ // in the label
+ if (label.empty())
+ continue;
+
+ defaultDeviceLabel = label;
+ continue;
+ }
+
+ const bool isDefault = false; // FIXME
+ // (defaultDeviceLabel.find(label) != std::string::npos);
+
+ if (deviceKind == std::string("videoinput")) {
+ if (!m_cameraDevices.contains(deviceId)) {
+ QCameraDevicePrivate *camera = new QCameraDevicePrivate; // QSharedData
+ camera->id = QString::fromStdString(deviceId).toUtf8();
+ camera->description = QString::fromUtf8(label.c_str());
+ camera->isDefault = isDefault;
+
+ m_cameraDevices.insert(deviceId, camera->create());
+ m_videoInputsAdded = true;
+ }
+ cameraDevicesToRemove.removeOne(deviceId);
+ } else if (deviceKind == std::string("audioinput")) {
+ if (!m_audioInputs.contains(deviceId)) {
+ m_audioInputs.insert(deviceId,
+ (new QWasmAudioDevice(deviceId.c_str(), label.c_str(),
+ isDefault, QAudioDevice::Input))
+ ->create());
+
+ m_audioInputsAdded = true;
+ }
+ audioInputsToRemove.removeOne(deviceId);
+ } else if (deviceKind == std::string("audiooutput")) {
+ if (!m_audioOutputs.contains(deviceId)) {
+ m_audioOutputs.insert(deviceId,
+ (new QWasmAudioDevice(deviceId.c_str(), label.c_str(),
+ isDefault, QAudioDevice::Input))
+ ->create());
+
+ m_audioOutputsAdded = true;
+ }
+ audioOutputsToRemove.removeOne(deviceId);
+ }
+ // if permissions are given label will hold the actual
+ // camera name, such as "Live! Cam Sync 1080p (041e:409d)"
+ }
+ if (!m_firstInit)
+ getOpenALAudioDevices();
+
+ // any left here were removed
+ int j = 0;
+ for (; j < cameraDevicesToRemove.count(); j++) {
+ m_cameraDevices.remove(cameraDevicesToRemove.at(j));
+ }
+ m_videoInputsRemoved = !cameraDevicesToRemove.isEmpty();
+
+ for (j = 0; j < audioInputsToRemove.count(); j++) {
+ m_audioInputs.remove(audioInputsToRemove.at(j));
+ }
+ m_audioInputsRemoved = !audioInputsToRemove.isEmpty();
+
+ for (j = 0; j < audioOutputsToRemove.count(); j++) {
+ m_audioOutputs.remove(audioOutputsToRemove.at(j));
+ }
+ m_audioOutputsRemoved = !audioOutputsToRemove.isEmpty();
+
+ if (m_videoInputsAdded || m_videoInputsRemoved)
+ emit videoInputsChanged();
+ if (m_audioInputsAdded || m_audioInputsRemoved)
+ emit audioInputsChanged();
+ if (m_audioOutputsAdded || m_audioOutputsRemoved)
+ emit audioOutputsChanged();
+
+ m_firstInit = false;
+
+}
+
+void QWasmMediaDevices::getMediaDevices()
+{
+ emscripten::val navigator = emscripten::val::global("navigator");
+ m_jsMediaDevicesInterface = navigator["mediaDevices"];
+
+ if (m_jsMediaDevicesInterface.isNull() || m_jsMediaDevicesInterface.isUndefined()) {
+ qWarning() << "No media devices found";
+ return;
+ }
+
+ if (qstdweb::haveAsyncify()) {
+#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY
+ emscripten::val devicesList = m_jsMediaDevicesInterface.call<emscripten::val>("enumerateDevices").await();
+ if (devicesList.isNull() || devicesList.isUndefined()) {
+ qWarning() << "devices list error";
+ return;
+ }
+
+ parseDevices(devicesList);
+#endif
+ } else {
+ qstdweb::PromiseCallbacks enumerateDevicesCallback{
+ .thenFunc =
+ [&](emscripten::val devices) {
+ parseDevices(devices);
+ },
+ .catchFunc =
+ [this](emscripten::val error) {
+ qWarning() << "mediadevices enumerateDevices fail"
+ << QString::fromStdString(error["name"].as<std::string>())
+ << QString::fromStdString(error["message"].as<std::string>());
+ m_initDone = false;
+ }
+ };
+
+ qstdweb::Promise::make(m_jsMediaDevicesInterface,
+ QStringLiteral("enumerateDevices"),
+ std::move(enumerateDevicesCallback));
+
+ // setup devicechange monitor
+ m_deviceChangedCallback = std::make_unique<qstdweb::EventCallback>(
+ m_jsMediaDevicesInterface, "devicechange",
+ [this, enumerateDevicesCallback](emscripten::val) {
+ qstdweb::Promise::make(m_jsMediaDevicesInterface,
+ QStringLiteral("enumerateDevices"),
+ std::move(enumerateDevicesCallback));
+ });
+ }
+
+}
+
+void QWasmMediaDevices::getOpenALAudioDevices()
+{
+ // VM3959:4 The AudioContext was not allowed to start.
+ // It must be resumed (or created) after a user gesture on the page. https://goo.gl/7K7WLu
+ auto capture = alcGetString(nullptr, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
+ // present even if there is no capture device
+ if (capture && !m_audioOutputs.contains(capture)) {
+ m_audioInputs.insert(capture,
+ (new QWasmAudioDevice(capture, "WebAssembly audio capture device",
+ true, QAudioDevice::Input))
+ ->create());
+ m_audioInputsAdded = true;
+ emit audioInputsChanged();
+ }
+
+ auto playback = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
+ // present even if there is no playback device
+ if (playback && !m_audioOutputs.contains(capture)) {
+ m_audioOutputs.insert(playback,
+ (new QWasmAudioDevice(playback, "WebAssembly audio playback device",
+ true, QAudioDevice::Output))
+ ->create());
+ emit audioOutputsChanged();
+ }
+ m_firstInit = true;
}
QT_END_NAMESPACE
diff --git a/src/multimedia/wasm/qwasmmediadevices_p.h b/src/multimedia/wasm/qwasmmediadevices_p.h
index 63fa05476..b97036f97 100644
--- a/src/multimedia/wasm/qwasmmediadevices_p.h
+++ b/src/multimedia/wasm/qwasmmediadevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWASMMEDIADEVICES_H
#define QWASMMEDIADEVICES_H
@@ -52,14 +16,38 @@
//
#include <private/qplatformmediadevices_p.h>
-#include <qset.h>
+
+#include <private/qplatformvideodevices_p.h>
+
+#include <QtCore/private/qstdweb_p.h>
#include <qaudio.h>
#include <qaudiodevice.h>
+#include <qcameradevice.h>
+#include <qset.h>
+#include <QtCore/qloggingcategory.h>
+#include <emscripten.h>
+#include <emscripten/val.h>
+#include <emscripten/bind.h>
+#include <QMapIterator>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(qWasmMediaDevices)
+
class QWasmAudioEngine;
+class QWasmCameraDevices : public QPlatformVideoDevices
+{
+ Q_OBJECT
+public:
+ QWasmCameraDevices(QPlatformMediaIntegration *integration);
+
+ QList<QCameraDevice> videoDevices() const override;
+private:
+ // weak
+ QPlatformMediaDevices *m_mediaDevices;
+};
+
class QWasmMediaDevices : public QPlatformMediaDevices
{
public:
@@ -67,13 +55,33 @@ public:
QList<QAudioDevice> audioInputs() const override;
QList<QAudioDevice> audioOutputs() const override;
- QList<QCameraDevice> videoInputs() const override;
- QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override;
- QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override;
+ QList<QCameraDevice> videoInputs() const;
+
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
+ void initDevices();
private:
- QList<QAudioDevice> m_outs;
- QList<QAudioDevice> m_ins;
+ void updateCameraDevices();
+ void getMediaDevices();
+ void getOpenALAudioDevices();
+ void parseDevices(emscripten::val devices);
+
+ QMap <std::string, QAudioDevice> m_audioOutputs;
+ QMap <std::string, QAudioDevice> m_audioInputs;
+ QMap <std::string, QCameraDevice> m_cameraDevices;
+
+
+ std::unique_ptr<qstdweb::EventCallback> m_deviceChangedCallback;
+
+ bool m_videoInputsAdded = false;
+ bool m_audioInputsAdded = false;
+ bool m_audioOutputsAdded = false;
+ emscripten::val m_jsMediaDevicesInterface = emscripten::val::undefined();
+ bool m_initDone = false;
+ bool m_firstInit = false;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qcomptr_p.h b/src/multimedia/windows/qcomptr_p.h
new file mode 100644
index 000000000..befc97f11
--- /dev/null
+++ b/src/multimedia/windows/qcomptr_p.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCOMPTR_P_H
+#define QCOMPTR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qt_windows.h>
+#include <wrl/client.h>
+
+using Microsoft::WRL::ComPtr;
+
+template<typename T, typename... Args>
+ComPtr<T> makeComObject(Args &&...args)
+{
+ ComPtr<T> p;
+ // Don't use Attach because of MINGW64 bug
+ // #892 Microsoft::WRL::ComPtr::Attach leaks references
+ *p.GetAddressOf() = new T(std::forward<Args>(args)...);
+ return p;
+}
+
+#endif
diff --git a/src/multimedia/windows/qcomtaskresource_p.h b/src/multimedia/windows/qcomtaskresource_p.h
new file mode 100644
index 000000000..90554da5e
--- /dev/null
+++ b/src/multimedia/windows/qcomtaskresource_p.h
@@ -0,0 +1,152 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QCOMTASKRESOURCE_P_H
+#define QCOMTASKRESOURCE_P_H
+
+#include <QtCore/qassert.h>
+
+#include <objbase.h>
+#include <algorithm>
+#include <type_traits>
+#include <utility>
+
+class QEmptyDeleter final
+{
+public:
+ template<typename T>
+ void operator()(T /*element*/) const
+ {
+ }
+};
+
+class QComDeleter final
+{
+public:
+ template<typename T>
+ void operator()(T element) const
+ {
+ element->Release();
+ }
+};
+
+template<typename T>
+class QComTaskResourceBase
+{
+public:
+ QComTaskResourceBase(const QComTaskResourceBase<T> &source) = delete;
+ QComTaskResourceBase &operator=(const QComTaskResourceBase<T> &right) = delete;
+
+ explicit operator bool() const { return m_resource != nullptr; }
+
+ T *get() const { return m_resource; }
+
+protected:
+ QComTaskResourceBase() = default;
+ explicit QComTaskResourceBase(T *const resource) : m_resource(resource) { }
+
+ T *release() { return std::exchange(m_resource, nullptr); }
+
+ void reset(T *const resource = nullptr)
+ {
+ if (m_resource != resource) {
+ if (m_resource)
+ CoTaskMemFree(m_resource);
+ m_resource = resource;
+ }
+ }
+
+ T *m_resource = nullptr;
+};
+
+template<typename T, typename TElementDeleter = QEmptyDeleter>
+class QComTaskResource final : public QComTaskResourceBase<T>
+{
+ using Base = QComTaskResourceBase<T>;
+
+public:
+ using Base::QComTaskResourceBase;
+
+ ~QComTaskResource() { reset(); }
+
+ T *operator->() const { return m_resource; }
+ T &operator*() const { return *m_resource; }
+
+ T **address()
+ {
+ Q_ASSERT(m_resource == nullptr);
+ return &m_resource;
+ }
+
+ using Base::release;
+ using Base::reset;
+
+private:
+ using Base::m_resource;
+};
+
+template<typename T, typename TElementDeleter>
+class QComTaskResource<T[], TElementDeleter> final : public QComTaskResourceBase<T>
+{
+ using Base = QComTaskResourceBase<T>;
+
+public:
+ QComTaskResource() = default;
+ explicit QComTaskResource(T *const resource, const std::size_t size)
+ : Base(resource), m_size(size)
+ {
+ }
+
+ ~QComTaskResource() { reset(); }
+
+ T &operator[](const std::size_t index) const
+ {
+ Q_ASSERT(index < m_size);
+ return m_resource[index];
+ }
+
+ T *release()
+ {
+ m_size = 0;
+
+ return Base::release();
+ }
+
+ void reset() { reset(nullptr, 0); }
+
+ void reset(T *const resource, const std::size_t size)
+ {
+ if (m_resource != resource) {
+ resetElements();
+
+ Base::reset(resource);
+
+ m_size = size;
+ }
+ }
+
+private:
+ void resetElements()
+ {
+ if constexpr (!std::is_same_v<TElementDeleter, QEmptyDeleter>) {
+ std::for_each(m_resource, m_resource + m_size, TElementDeleter());
+ }
+ }
+
+ std::size_t m_size = 0;
+
+ using Base::m_resource;
+};
+
+#endif
diff --git a/src/multimedia/windows/qwindowsaudiodevice.cpp b/src/multimedia/windows/qwindowsaudiodevice.cpp
index ea9108113..f22567cbf 100644
--- a/src/multimedia/windows/qwindowsaudiodevice.cpp
+++ b/src/multimedia/windows/qwindowsaudiodevice.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -48,28 +12,47 @@
// INTERNAL USE ONLY: Do NOT use for any other purpose.
//
+#include "qwindowsaudiodevice_p.h"
+#include "qwindowsaudioutils_p.h"
#include <QtCore/qt_windows.h>
#include <QtCore/QDataStream>
#include <QtCore/QIODevice>
-#include <utility>
+
+#include <audioclient.h>
#include <mmsystem.h>
+
#include <initguid.h>
+#include <wtypes.h>
+#include <propkeydef.h>
#include <mmdeviceapi.h>
-#include "qwindowsaudiodevice_p.h"
-#include "qwindowsaudioutils_p.h"
QT_BEGIN_NAMESPACE
-QWindowsAudioDeviceInfo::QWindowsAudioDeviceInfo(QByteArray dev, QWindowsIUPointer<IMMDevice> immDev, int waveID, const QString &description, QAudioDevice::Mode mode)
+QWindowsAudioDeviceInfo::QWindowsAudioDeviceInfo(QByteArray dev, ComPtr<IMMDevice> immDev, int waveID, const QString &description, QAudioDevice::Mode mode)
: QAudioDevicePrivate(dev, mode),
m_devId(waveID),
m_immDev(std::move(immDev))
{
+ Q_ASSERT(m_immDev);
+
this->description = description;
- preferredFormat.setSampleRate(44100);
- preferredFormat.setChannelCount(2);
- preferredFormat.setSampleFormat(QAudioFormat::Int16);
+
+ ComPtr<IAudioClient> audioClient;
+ HRESULT hr = m_immDev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr,
+ (void **)audioClient.GetAddressOf());
+ if (SUCCEEDED(hr)) {
+ WAVEFORMATEX *pwfx = nullptr;
+ hr = audioClient->GetMixFormat(&pwfx);
+ if (SUCCEEDED(hr))
+ preferredFormat = QWindowsAudioUtils::waveFormatExToFormat(*pwfx);
+ }
+
+ if (!preferredFormat.isValid()) {
+ preferredFormat.setSampleRate(44100);
+ preferredFormat.setChannelCount(2);
+ preferredFormat.setSampleFormat(QAudioFormat::Int16);
+ }
DWORD fmt = 0;
@@ -211,16 +194,14 @@ QWindowsAudioDeviceInfo::QWindowsAudioDeviceInfo(QByteArray dev, QWindowsIUPoint
channelConfiguration = QAudioFormat::defaultChannelConfigForChannelCount(maximumChannelCount);
- QWindowsIUPointer<IPropertyStore> props;
- if (m_immDev) {
- HRESULT hr = m_immDev->OpenPropertyStore(STGM_READ, props.address());
- if (SUCCEEDED(hr)) {
- PROPVARIANT var;
- PropVariantInit(&var);
- hr = props->GetValue(PKEY_AudioEndpoint_PhysicalSpeakers, &var);
- if (SUCCEEDED(hr) && var.uintVal != 0)
- channelConfiguration = QWindowsAudioUtils::maskToChannelConfig(var.uintVal, maximumChannelCount);
- }
+ ComPtr<IPropertyStore> props;
+ hr = m_immDev->OpenPropertyStore(STGM_READ, props.GetAddressOf());
+ if (SUCCEEDED(hr)) {
+ PROPVARIANT var;
+ PropVariantInit(&var);
+ hr = props->GetValue(PKEY_AudioEndpoint_PhysicalSpeakers, &var);
+ if (SUCCEEDED(hr) && var.uintVal != 0)
+ channelConfiguration = QWindowsAudioUtils::maskToChannelConfig(var.uintVal, maximumChannelCount);
}
}
diff --git a/src/multimedia/windows/qwindowsaudiodevice_p.h b/src/multimedia/windows/qwindowsaudiodevice_p.h
index 6c83de5ad..b2af4bfe0 100644
--- a/src/multimedia/windows/qwindowsaudiodevice_p.h
+++ b/src/multimedia/windows/qwindowsaudiodevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -60,7 +24,7 @@
#include <QtMultimedia/qaudiodevice.h>
#include <private/qaudiosystem_p.h>
#include <private/qaudiodevice_p.h>
-#include <qwindowsiupointer_p.h>
+#include <qcomptr_p.h>
struct IMMDevice;
@@ -72,7 +36,7 @@ const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 };
class QWindowsAudioDeviceInfo : public QAudioDevicePrivate
{
public:
- QWindowsAudioDeviceInfo(QByteArray dev, QWindowsIUPointer<IMMDevice> immdev, int waveID, const QString &description, QAudioDevice::Mode mode);
+ QWindowsAudioDeviceInfo(QByteArray dev, ComPtr<IMMDevice> immdev, int waveID, const QString &description, QAudioDevice::Mode mode);
~QWindowsAudioDeviceInfo();
bool open();
@@ -81,11 +45,11 @@ public:
bool testSettings(const QAudioFormat& format) const;
int waveId() const { return m_devId; }
- QWindowsIUPointer<IMMDevice> immDev() const { return m_immDev; }
+ ComPtr<IMMDevice> immDev() const { return m_immDev; }
private:
quint32 m_devId;
- QWindowsIUPointer<IMMDevice> m_immDev;
+ ComPtr<IMMDevice> m_immDev;
};
diff --git a/src/multimedia/windows/qwindowsaudiosink.cpp b/src/multimedia/windows/qwindowsaudiosink.cpp
index 5d41e6ed6..404496970 100644
--- a/src/multimedia/windows/qwindowsaudiosink.cpp
+++ b/src/multimedia/windows/qwindowsaudiosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -49,33 +13,31 @@
//
#include "qwindowsaudiosink_p.h"
-#include "qwindowsaudiodevice_p.h"
#include "qwindowsaudioutils_p.h"
-#include <QtEndian>
+#include "qwindowsmultimediautils_p.h"
+#include "qcomtaskresource_p.h"
+
#include <QtCore/QDataStream>
#include <QtCore/qtimer.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qpointer.h>
#include <private/qaudiohelpers_p.h>
-#include <qloggingcategory.h>
-
-#include <system_error>
#include <audioclient.h>
#include <mmdeviceapi.h>
-
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcAudioOutput, "qt.multimedia.audiooutput")
+static Q_LOGGING_CATEGORY(qLcAudioOutput, "qt.multimedia.audiooutput")
-const IID IID_IAudioClient = __uuidof(IAudioClient);
-const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
+using namespace QWindowsMultimediaUtils;
class OutputPrivate : public QIODevice
{
Q_OBJECT
public:
- OutputPrivate(QWindowsAudioSink& audio) : audioDevice(audio) {}
+ OutputPrivate(QWindowsAudioSink &audio) : QIODevice(&audio), audioDevice(audio) {}
~OutputPrivate() override = default;
qint64 readData(char *, qint64) override { return 0; }
@@ -85,41 +47,25 @@ private:
QWindowsAudioSink &audioDevice;
};
-std::optional<quint32> audioClientFramesInUse(IAudioClient *client)
-{
- Q_ASSERT(client);
- UINT32 framesPadding = 0;
- if (SUCCEEDED(client->GetCurrentPadding(&framesPadding)))
- return framesPadding;
- return {};
-}
-
-std::optional<quint32> audioClientFramesAllocated(IAudioClient *client)
-{
- Q_ASSERT(client);
- UINT32 bufferFrameCount = 0;
- if (SUCCEEDED(client->GetBufferSize(&bufferFrameCount)))
- return bufferFrameCount;
- return {};
-}
-
std::optional<quint32> audioClientFramesAvailable(IAudioClient *client)
{
- auto framesAllocated = audioClientFramesAllocated(client);
- auto framesInUse = audioClientFramesInUse(client);
+ auto framesAllocated = QWindowsAudioUtils::audioClientFramesAllocated(client);
+ auto framesInUse = QWindowsAudioUtils::audioClientFramesInUse(client);
if (framesAllocated && framesInUse)
return *framesAllocated - *framesInUse;
return {};
}
-QWindowsAudioSink::QWindowsAudioSink(QWindowsIUPointer<IMMDevice> device) :
+QWindowsAudioSink::QWindowsAudioSink(ComPtr<IMMDevice> device, QObject *parent) :
+ QPlatformAudioSink(parent),
+ m_timer(new QTimer(this)),
m_pushSource(new OutputPrivate(*this)),
m_device(std::move(device))
{
m_pushSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
- m_timer.setSingleShot(true);
- m_timer.setTimerType(Qt::PreciseTimer);
+ m_timer->setSingleShot(true);
+ m_timer->setTimerType(Qt::PreciseTimer);
}
QWindowsAudioSink::~QWindowsAudioSink()
@@ -129,7 +75,7 @@ QWindowsAudioSink::~QWindowsAudioSink()
qint64 QWindowsAudioSink::remainingPlayTimeUs()
{
- auto framesInUse = audioClientFramesInUse(m_audioClient.get());
+ auto framesInUse = QWindowsAudioUtils::audioClientFramesInUse(m_audioClient.Get());
return m_resampler.outputFormat().durationForFrames(framesInUse ? *framesInUse : 0);
}
@@ -141,13 +87,16 @@ void QWindowsAudioSink::deviceStateChange(QAudio::State state, QAudio::Error err
qCDebug(qLcAudioOutput) << "Audio client started";
} else if (deviceState == QAudio::ActiveState) {
- m_timer.stop();
+ m_timer->stop();
m_audioClient->Stop();
qCDebug(qLcAudioOutput) << "Audio client stopped";
}
+ QPointer<QWindowsAudioSink> thisGuard(this);
deviceState = state;
emit stateChanged(deviceState);
+ if (!thisGuard)
+ return;
}
if (error != errorState) {
@@ -190,7 +139,7 @@ void QWindowsAudioSink::pullSource()
deviceStateChange(QAudio::IdleState, m_pullSource->atEnd() ? QAudio::NoError : QAudio::UnderrunError);
} else {
deviceStateChange(QAudio::ActiveState, QAudio::NoError);
- m_timer.start(playTimeUs / 2000);
+ m_timer->start(playTimeUs / 2000);
}
}
@@ -200,6 +149,9 @@ void QWindowsAudioSink::start(QIODevice* device)
if (deviceState != QAudio::StoppedState)
close();
+ if (device == nullptr)
+ return;
+
if (!open()) {
errorState = QAudio::OpenError;
emit errorChanged(QAudio::OpenError);
@@ -209,9 +161,9 @@ void QWindowsAudioSink::start(QIODevice* device)
m_pullSource = device;
connect(device, &QIODevice::readyRead, this, &QWindowsAudioSink::pullSource);
- m_timer.disconnect();
- m_timer.callOnTimeout(this, &QWindowsAudioSink::pullSource);
- m_timer.start(0);
+ m_timer->disconnect();
+ m_timer->callOnTimeout(this, &QWindowsAudioSink::pullSource);
+ pullSource();
}
qint64 QWindowsAudioSink::push(const char *data, qint64 len)
@@ -222,7 +174,7 @@ qint64 QWindowsAudioSink::push(const char *data, qint64 len)
qint64 written = write(data, len);
if (written > 0) {
deviceStateChange(QAudio::ActiveState, QAudio::NoError);
- m_timer.start(remainingPlayTimeUs() /1000);
+ m_timer->start(remainingPlayTimeUs() /1000);
}
return written;
@@ -242,8 +194,8 @@ QIODevice* QWindowsAudioSink::start()
deviceStateChange(QAudio::IdleState, QAudio::NoError);
- m_timer.disconnect();
- m_timer.callOnTimeout([&](){
+ m_timer->disconnect();
+ m_timer->callOnTimeout(this, [this](){
deviceStateChange(QAudio::IdleState, QAudio::UnderrunError);
});
@@ -252,23 +204,27 @@ QIODevice* QWindowsAudioSink::start()
bool QWindowsAudioSink::open()
{
- HRESULT hr = m_device->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER,
- nullptr, (void**)m_audioClient.address());
+ if (m_audioClient)
+ return true;
+
+ HRESULT hr = m_device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
+ nullptr, (void**)m_audioClient.GetAddressOf());
if (FAILED(hr)) {
- qCWarning(qLcAudioOutput) << "Failed to activate audio device";
+ qCWarning(qLcAudioOutput) << "Failed to activate audio device" << errorString(hr);
return false;
}
- WAVEFORMATEX *pwfx = nullptr;
- hr = m_audioClient->GetMixFormat(&pwfx);
+ auto resetClient = qScopeGuard([this](){ m_audioClient.Reset(); });
+
+ QComTaskResource<WAVEFORMATEX> pwfx;
+ hr = m_audioClient->GetMixFormat(pwfx.address());
if (FAILED(hr)) {
- qCWarning(qLcAudioOutput) << "Format unsupported" << hr;
+ qCWarning(qLcAudioOutput) << "Format unsupported" << errorString(hr);
return false;
}
if (!m_resampler.setup(m_format, QWindowsAudioUtils::waveFormatExToFormat(*pwfx))) {
- qCWarning(qLcAudioOutput) << "Failed to setup resampler";
- CoTaskMemFree(pwfx);
+ qCWarning(qLcAudioOutput) << "Failed to set up resampler";
return false;
}
@@ -277,22 +233,15 @@ bool QWindowsAudioSink::open()
REFERENCE_TIME requestedDuration = m_format.durationForBytes(m_bufferSize) * 10;
- hr = m_audioClient->Initialize(
- AUDCLNT_SHAREMODE_SHARED,
- 0,
- requestedDuration,
- 0,
- pwfx,
- nullptr);
-
- CoTaskMemFree(pwfx);
+ hr = m_audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, requestedDuration, 0, pwfx.get(),
+ nullptr);
if (FAILED(hr)) {
- qCWarning(qLcAudioOutput) << "Failed to initialize audio client" << hr;
+ qCWarning(qLcAudioOutput) << "Failed to initialize audio client" << errorString(hr);
return false;
}
- auto framesAllocated = audioClientFramesAllocated(m_audioClient.get());
+ auto framesAllocated = QWindowsAudioUtils::audioClientFramesAllocated(m_audioClient.Get());
if (!framesAllocated) {
qCWarning(qLcAudioOutput) << "Failed to get audio client buffer size";
return false;
@@ -301,12 +250,15 @@ bool QWindowsAudioSink::open()
m_bufferSize = m_format.bytesForDuration(
m_resampler.outputFormat().durationForFrames(*framesAllocated));
- hr = m_audioClient->GetService(IID_IAudioRenderClient, (void**)m_renderClient.address());
+ hr = m_audioClient->GetService(__uuidof(IAudioRenderClient), (void**)m_renderClient.GetAddressOf());
if (FAILED(hr)) {
- qCWarning(qLcAudioOutput) << "Failed to obtain audio client rendering service" << hr;
+ qCWarning(qLcAudioOutput) << "Failed to obtain audio client rendering service"
+ << errorString(hr);
return false;
}
+ resetClient.dismiss();
+
return true;
}
@@ -320,8 +272,8 @@ void QWindowsAudioSink::close()
if (m_pullSource)
disconnect(m_pullSource, &QIODevice::readyRead, this, &QWindowsAudioSink::pullSource);
- m_audioClient.reset();
- m_renderClient.reset();
+ m_audioClient.Reset();
+ m_renderClient.Reset();
m_pullSource = nullptr;
}
@@ -330,7 +282,7 @@ qsizetype QWindowsAudioSink::bytesFree() const
if (!m_audioClient)
return 0;
- auto framesAvailable = audioClientFramesAvailable(m_audioClient.get());
+ auto framesAvailable = audioClientFramesAvailable(m_audioClient.Get());
if (framesAvailable)
return m_resampler.inputBufferSize(*framesAvailable * m_resampler.outputFormat().bytesPerFrame());
return 0;
@@ -357,7 +309,7 @@ qint64 QWindowsAudioSink::write(const char *data, qint64 len)
qCDebug(qLcAudioOutput) << "write()" << len;
- auto framesAvailable = audioClientFramesAvailable(m_audioClient.get());
+ auto framesAvailable = audioClientFramesAvailable(m_audioClient.Get());
if (!framesAvailable)
return -1;
@@ -371,8 +323,7 @@ qint64 QWindowsAudioSink::write(const char *data, qint64 len)
quint8 *buffer = nullptr;
HRESULT hr = m_renderClient->GetBuffer(writeFramesNum, &buffer);
if (FAILED(hr)) {
- qCWarning(qLcAudioOutput) << "Failed to get buffer"
- << std::system_category().message(hr).c_str();
+ qCWarning(qLcAudioOutput) << "Failed to get buffer" << errorString(hr);
return -1;
}
@@ -384,8 +335,7 @@ qint64 QWindowsAudioSink::write(const char *data, qint64 len)
DWORD flags = writeBytes.isEmpty() ? AUDCLNT_BUFFERFLAGS_SILENT : 0;
hr = m_renderClient->ReleaseBuffer(writeFramesNum, flags);
if (FAILED(hr)) {
- qCWarning(qLcAudioOutput) << "Failed to return buffer"
- << std::system_category().message(hr).c_str();
+ qCWarning(qLcAudioOutput) << "Failed to return buffer" << errorString(hr);
return -1;
}
@@ -399,9 +349,7 @@ void QWindowsAudioSink::resume()
if (m_pullSource) {
pullSource();
} else {
- // FIXME: Set IdleState to be consistent with implementations on other platforms
- // even when playing the audio part that was in the buffer when suspended
- deviceStateChange(QAudio::IdleState, QAudio::NoError);
+ deviceStateChange(suspendedInState, QAudio::NoError);
if (remainingPlayTimeUs() > 0)
m_audioClient->Start();
}
@@ -411,8 +359,10 @@ void QWindowsAudioSink::resume()
void QWindowsAudioSink::suspend()
{
qCDebug(qLcAudioOutput) << "suspend()";
- if (deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState)
+ if (deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) {
+ suspendedInState = deviceState;
deviceStateChange(QAudio::SuspendedState, QAudio::NoError);
+ }
}
void QWindowsAudioSink::setVolume(qreal v)
@@ -423,12 +373,14 @@ void QWindowsAudioSink::setVolume(qreal v)
m_volume = qBound(qreal(0), v, qreal(1));
}
+void QWindowsAudioSink::stop() {
+ // TODO: investigate and find a way to drain and stop instead of closing
+ close();
+}
+
void QWindowsAudioSink::reset()
{
- if (m_audioClient) {
- m_audioClient->Stop();
- m_audioClient->Reset();
- }
+ close();
}
QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsaudiosink_p.h b/src/multimedia/windows/qwindowsaudiosink_p.h
index 6910a1385..1a98bc296 100644
--- a/src/multimedia/windows/qwindowsaudiosink_p.h
+++ b/src/multimedia/windows/qwindowsaudiosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -51,8 +15,6 @@
#ifndef QWINDOWSAUDIOOUTPUT_H
#define QWINDOWSAUDIOOUTPUT_H
-#include "qwindowsaudioutils_p.h"
-
#include <QtCore/qdebug.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qiodevice.h>
@@ -61,28 +23,17 @@
#include <QtCore/qdatetime.h>
#include <QtCore/qmutex.h>
#include <QtCore/qtimer.h>
+#include <QtCore/qpointer.h>
#include <QtMultimedia/qaudio.h>
#include <QtMultimedia/qaudiodevice.h>
#include <private/qaudiosystem_p.h>
-#include <qwindowsiupointer_p.h>
+#include <qcomptr_p.h>
#include <qwindowsresampler_p.h>
-#include <queue>
-#include <utility>
-
#include <audioclient.h>
#include <mmdeviceapi.h>
-// For compat with 4.6
-#if !defined(QT_WIN_CALLBACK)
-# if defined(Q_CC_MINGW)
-# define QT_WIN_CALLBACK CALLBACK __attribute__ ((force_align_arg_pointer))
-# else
-# define QT_WIN_CALLBACK CALLBACK
-# endif
-#endif
-
QT_BEGIN_NAMESPACE
class QWindowsResampler;
@@ -91,14 +42,14 @@ class QWindowsAudioSink : public QPlatformAudioSink
{
Q_OBJECT
public:
- QWindowsAudioSink(QWindowsIUPointer<IMMDevice> device);
+ QWindowsAudioSink(ComPtr<IMMDevice> device, QObject *parent);
~QWindowsAudioSink();
void setFormat(const QAudioFormat& fmt) override;
QAudioFormat format() const override;
QIODevice* start() override;
void start(QIODevice* device) override;
- void stop() override { close(); }
+ void stop() override;
void reset() override;
void suspend() override;
void resume() override;
@@ -127,15 +78,16 @@ private:
QAudioFormat m_format;
QAudio::Error errorState = QAudio::NoError;
QAudio::State deviceState = QAudio::StoppedState;
+ QAudio::State suspendedInState = QAudio::SuspendedState;
qsizetype m_bufferSize = 0;
qreal m_volume = 1.0;
- QTimer m_timer;
+ QTimer *m_timer = nullptr;
QScopedPointer<QIODevice> m_pushSource;
- QIODevice *m_pullSource = nullptr;
- QWindowsIUPointer<IMMDevice> m_device;
- QWindowsIUPointer<IAudioClient> m_audioClient;
- QWindowsIUPointer<IAudioRenderClient> m_renderClient;
+ QPointer<QIODevice> m_pullSource;
+ ComPtr<IMMDevice> m_device;
+ ComPtr<IAudioClient> m_audioClient;
+ ComPtr<IAudioRenderClient> m_renderClient;
QWindowsResampler m_resampler;
};
diff --git a/src/multimedia/windows/qwindowsaudiosource.cpp b/src/multimedia/windows/qwindowsaudiosource.cpp
index 8523f9982..85fc454ad 100644
--- a/src/multimedia/windows/qwindowsaudiosource.cpp
+++ b/src/multimedia/windows/qwindowsaudiosource.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -50,646 +14,393 @@
#include "qwindowsaudiosource_p.h"
+#include "qwindowsmultimediautils_p.h"
+#include "qcomtaskresource_p.h"
#include <QtCore/QDataStream>
#include <QtCore/qtimer.h>
-QT_BEGIN_NAMESPACE
+#include <private/qaudiohelpers_p.h>
-//#define DEBUG_AUDIO 1
+#include <qloggingcategory.h>
+#include <qdebug.h>
+#include <audioclient.h>
+#include <mmdeviceapi.h>
-QWindowsAudioSource::QWindowsAudioSource(int deviceId)
-{
- bytesAvailable = 0;
- buffer_size = 0;
- period_size = 0;
- m_deviceId = deviceId;
- totalTimeValue = 0;
- errorState = QAudio::NoError;
- deviceState = QAudio::StoppedState;
- audioSource = 0;
- pullMode = true;
- resuming = false;
- finished = false;
- waveBlockOffset = 0;
-
- mixerID = 0;
- cachedVolume = -1.0f;
- memset(&mixerLineControls, 0, sizeof(mixerLineControls));
-}
+QT_BEGIN_NAMESPACE
-QWindowsAudioSource::~QWindowsAudioSource()
-{
- stop();
-}
+static Q_LOGGING_CATEGORY(qLcAudioSource, "qt.multimedia.audiosource")
+
+using namespace QWindowsMultimediaUtils;
-void QT_WIN_CALLBACK QWindowsAudioSource::waveInProc( HWAVEIN hWaveIn, UINT uMsg,
- DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
+class OurSink : public QIODevice
{
- Q_UNUSED(dwParam1);
- Q_UNUSED(dwParam2);
- Q_UNUSED(hWaveIn);
+public:
+ OurSink(QWindowsAudioSource& source) : QIODevice(&source), m_audioSource(source) {}
- QWindowsAudioSource* qAudio;
- qAudio = (QWindowsAudioSource*)(dwInstance);
- if(!qAudio)
- return;
+ qint64 bytesAvailable() const override { return m_audioSource.bytesReady(); }
+ qint64 readData(char* data, qint64 len) override { return m_audioSource.read(data, len); }
+ qint64 writeData(const char*, qint64) override { return 0; }
- QMutexLocker locker(&qAudio->mutex);
+private:
+ QWindowsAudioSource &m_audioSource;
+};
- switch(uMsg) {
- case WIM_OPEN:
- break;
- case WIM_DATA:
- if(qAudio->waveFreeBlockCount > 0)
- qAudio->waveFreeBlockCount--;
- qAudio->feedback();
- break;
- case WIM_CLOSE:
- qAudio->finished = true;
- break;
- default:
- return;
- }
+QWindowsAudioSource::QWindowsAudioSource(ComPtr<IMMDevice> device, QObject *parent)
+ : QPlatformAudioSource(parent),
+ m_timer(new QTimer(this)),
+ m_device(std::move(device)),
+ m_ourSink(new OurSink(*this))
+{
+ m_ourSink->open(QIODevice::ReadOnly|QIODevice::Unbuffered);
+ m_timer->setTimerType(Qt::PreciseTimer);
+ m_timer->setSingleShot(true);
+ m_timer->callOnTimeout(this, &QWindowsAudioSource::pullCaptureClient);
}
-WAVEHDR* QWindowsAudioSource::allocateBlocks(int size, int count)
+void QWindowsAudioSource::setVolume(qreal volume)
{
- int i;
- unsigned char* buffer;
- WAVEHDR* blocks;
- DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count;
-
- if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
- totalBufferSize)) == 0) {
- qWarning("QAudioSource: Memory allocation error");
- return 0;
- }
- blocks = (WAVEHDR*)buffer;
- buffer += sizeof(WAVEHDR)*count;
- for(i = 0; i < count; i++) {
- blocks[i].dwBufferLength = size;
- blocks[i].lpData = (LPSTR)buffer;
- blocks[i].dwBytesRecorded=0;
- blocks[i].dwUser = 0L;
- blocks[i].dwFlags = 0L;
- blocks[i].dwLoops = 0L;
- result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR));
- if(result != MMSYSERR_NOERROR) {
- qWarning("QAudioSource: Can't prepare block %d",i);
- return 0;
- }
- buffer += size;
- }
- return blocks;
+ m_volume = qBound(qreal(0), volume, qreal(1));
}
-void QWindowsAudioSource::freeBlocks(WAVEHDR* blockArray)
+qreal QWindowsAudioSource::volume() const
{
- WAVEHDR* blocks = blockArray;
-
- int count = buffer_size/period_size;
+ return m_volume;
+}
- for(int i = 0; i < count; i++) {
- waveInUnprepareHeader(hWaveIn,blocks, sizeof(WAVEHDR));
- blocks++;
- }
- HeapFree(GetProcessHeap(), 0, blockArray);
+QWindowsAudioSource::~QWindowsAudioSource()
+{
+ stop();
}
QAudio::Error QWindowsAudioSource::error() const
{
- return errorState;
+ return m_errorState;
}
QAudio::State QWindowsAudioSource::state() const
{
- return deviceState;
+ return m_deviceState;
}
-#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET
- #ifndef DRVM_MAPPER
- #define DRVM_MAPPER 0x2000
- #endif
- #ifndef DRVM_MAPPER_STATUS
- #define DRVM_MAPPER_STATUS (DRVM_MAPPER+0)
- #endif
- #define DRVM_USER 0x4000
- #define DRVM_MAPPER_RECONFIGURE (DRVM_MAPPER+1)
- #define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21)
- #define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23)
-#endif
-
-void QWindowsAudioSource::setVolume(qreal volume)
+void QWindowsAudioSource::setFormat(const QAudioFormat& fmt)
{
- cachedVolume = volume;
- for (DWORD i = 0; i < mixerLineControls.cControls; i++) {
-
- MIXERCONTROLDETAILS controlDetails;
- controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
- controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID;
- controlDetails.cChannels = 1;
-
- if ((mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_FADER) ||
- (mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)) {
- MIXERCONTROLDETAILS_UNSIGNED controlDetailsUnsigned;
- controlDetailsUnsigned.dwValue = qBound(DWORD(0), DWORD(65535.0 * volume + 0.5), DWORD(65535));
- controlDetails.cMultipleItems = 0;
- controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
- controlDetails.paDetails = &controlDetailsUnsigned;
- mixerSetControlDetails(mixerID, &controlDetails, MIXER_SETCONTROLDETAILSF_VALUE);
+ if (m_deviceState == QAudio::StoppedState) {
+ m_format = fmt;
+ } else {
+ if (m_format != fmt) {
+ qWarning() << "Cannot set a new audio format, in the current state ("
+ << m_deviceState << ")";
}
}
}
-qreal QWindowsAudioSource::volume() const
+QAudioFormat QWindowsAudioSource::format() const
+{
+ return m_format;
+}
+
+void QWindowsAudioSource::deviceStateChange(QAudio::State state, QAudio::Error error)
{
- for (DWORD i = 0; i < mixerLineControls.cControls; i++) {
- if ((mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_FADER) &&
- (mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_VOLUME)) {
- continue;
+ if (state != m_deviceState) {
+ bool wasActive = m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState;
+ bool isActive = state == QAudio::ActiveState || state == QAudio::IdleState;
+
+ if (isActive && !wasActive) {
+ m_audioClient->Start();
+ qCDebug(qLcAudioSource) << "Audio client started";
+
+ } else if (wasActive && !isActive) {
+ m_timer->stop();
+ m_audioClient->Stop();
+ qCDebug(qLcAudioSource) << "Audio client stopped";
}
- MIXERCONTROLDETAILS controlDetails;
- controlDetails.cbStruct = sizeof(controlDetails);
- controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID;
- controlDetails.cChannels = 1;
- controlDetails.cMultipleItems = 0;
- controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
- MIXERCONTROLDETAILS_UNSIGNED detailsUnsigned;
- controlDetails.paDetails = &detailsUnsigned;
- memset(controlDetails.paDetails, 0, controlDetails.cbDetails);
-
- MMRESULT result = mixerGetControlDetails(mixerID, &controlDetails, MIXER_GETCONTROLDETAILSF_VALUE);
- if (result != MMSYSERR_NOERROR)
- continue;
- if (controlDetails.cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED))
- continue;
- return detailsUnsigned.dwValue / 65535.0;
+ m_deviceState = state;
+ emit stateChanged(m_deviceState);
}
- return qFuzzyCompare(cachedVolume, qreal(-1.0f)) ? 1.0f : cachedVolume;
+ if (error != m_errorState) {
+ m_errorState = error;
+ emit errorChanged(error);
+ }
}
-void QWindowsAudioSource::setFormat(const QAudioFormat& fmt)
+QByteArray QWindowsAudioSource::readCaptureClientBuffer()
{
- if (deviceState == QAudio::StoppedState)
- settings = fmt;
-}
+ UINT32 actualFrames = 0;
+ BYTE *data = nullptr;
+ DWORD flags = 0;
+ HRESULT hr = m_captureClient->GetBuffer(&data, &actualFrames, &flags, nullptr, nullptr);
+ if (FAILED(hr)) {
+ qWarning() << "IAudioCaptureClient::GetBuffer failed" << errorString(hr);
+ deviceStateChange(QAudio::IdleState, QAudio::IOError);
+ return {};
+ }
-QAudioFormat QWindowsAudioSource::format() const
-{
- return settings;
+ if (actualFrames == 0)
+ return {};
+
+ QByteArray out;
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
+ out.resize(m_resampler.outputFormat().bytesForDuration(
+ m_resampler.inputFormat().framesForDuration(actualFrames)),
+ 0);
+ } else {
+ out = m_resampler.resample(
+ { data, m_resampler.inputFormat().bytesForFrames(actualFrames) });
+ QAudioHelperInternal::qMultiplySamples(m_volume, m_resampler.outputFormat(), out.data(), out.data(), out.size());
+ }
+
+ hr = m_captureClient->ReleaseBuffer(actualFrames);
+ if (FAILED(hr)) {
+ qWarning() << "IAudioCaptureClient::ReleaseBuffer failed" << errorString(hr);
+ deviceStateChange(QAudio::IdleState, QAudio::IOError);
+ return {};
+ }
+
+ return out;
}
-void QWindowsAudioSource::start(QIODevice* device)
+void QWindowsAudioSource::schedulePull()
{
- if(deviceState != QAudio::StoppedState)
- close();
+ auto allocated = QWindowsAudioUtils::audioClientFramesAllocated(m_audioClient.Get());
+ auto inUse = QWindowsAudioUtils::audioClientFramesInUse(m_audioClient.Get());
- if(!pullMode && audioSource)
- delete audioSource;
+ if (!allocated || !inUse) {
+ deviceStateChange(QAudio::IdleState, QAudio::IOError);
+ } else {
+ // Schedule the next audio pull immediately if the audio buffer is more
+ // than half-full or wait until the audio source fills at least half of it.
+ if (*inUse > *allocated / 2) {
+ m_timer->start(0);
+ } else {
+ auto timeToHalfBuffer = m_resampler.inputFormat().durationForFrames(*allocated / 2 - *inUse);
+ m_timer->start(timeToHalfBuffer / 1000);
+ }
+ }
+}
- pullMode = true;
- audioSource = device;
+void QWindowsAudioSource::pullCaptureClient()
+{
+ qCDebug(qLcAudioSource) << "Pull captureClient";
+ while (true) {
+ auto out = readCaptureClientBuffer();
+ if (out.isEmpty())
+ break;
- deviceState = QAudio::ActiveState;
+ if (m_clientSink) {
+ qint64 written = m_clientSink->write(out.data(), out.size());
+ if (written != out.size())
+ qCDebug(qLcAudioSource) << "Did not write all data to the output";
- if(!open())
- return;
+ } else {
+ m_clientBufferResidue += out;
+ emit m_ourSink->readyRead();
+ }
+ }
- emit stateChanged(deviceState);
+ schedulePull();
}
-QIODevice* QWindowsAudioSource::start()
+void QWindowsAudioSource::start(QIODevice* device)
{
- if(deviceState != QAudio::StoppedState)
+ qCDebug(qLcAudioSource) << "start(ioDevice)";
+ if (m_deviceState != QAudio::StoppedState)
close();
- if(!pullMode && audioSource)
- delete audioSource;
+ if (device == nullptr)
+ return;
- pullMode = false;
- audioSource = new InputPrivate(this);
- audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+ if (!open()) {
+ m_errorState = QAudio::OpenError;
+ emit errorChanged(QAudio::OpenError);
+ return;
+ }
- deviceState = QAudio::IdleState;
+ m_clientSink = device;
+ schedulePull();
+ deviceStateChange(QAudio::ActiveState, QAudio::NoError);
+}
- if(!open())
- return 0;
+QIODevice* QWindowsAudioSource::start()
+{
+ qCDebug(qLcAudioSource) << "start()";
+ if (m_deviceState != QAudio::StoppedState)
+ close();
- emit stateChanged(deviceState);
+ if (!open()) {
+ m_errorState = QAudio::OpenError;
+ emit errorChanged(QAudio::OpenError);
+ return nullptr;
+ }
- return audioSource;
+ schedulePull();
+ deviceStateChange(QAudio::IdleState, QAudio::NoError);
+ return m_ourSink;
}
void QWindowsAudioSource::stop()
{
- if(deviceState == QAudio::StoppedState)
+ if (m_deviceState == QAudio::StoppedState)
return;
close();
- emit stateChanged(deviceState);
}
bool QWindowsAudioSource::open()
{
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
-#endif
- header = 0;
-
- period_size = 0;
-
- if (!QWindowsAudioUtils::formatToWaveFormatExtensible(settings, wfx)) {
- qWarning("QAudioSource: open error, invalid format.");
- } else if (buffer_size == 0) {
- period_size = settings.sampleRate() / 25 * settings.bytesPerFrame();
- buffer_size = period_size * 5;
- } else {
- if (int bpf = settings.bytesPerFrame())
- period_size = bpf * (buffer_size / 5 / bpf);
+ HRESULT hr = m_device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
+ nullptr, (void**)m_audioClient.GetAddressOf());
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioSource) << "Failed to activate audio device" << errorString(hr);
+ return false;
}
- if (period_size == 0) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
+ QComTaskResource<WAVEFORMATEX> pwfx;
+ hr = m_audioClient->GetMixFormat(pwfx.address());
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioSource) << "Format unsupported" << errorString(hr);
return false;
}
- elapsedTimeOffset = 0;
-
- if (waveInOpen(&hWaveIn, UINT_PTR(m_deviceId), &wfx.Format,
- (DWORD_PTR)&waveInProc,
- (DWORD_PTR) this,
- CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- qWarning("QAudioSource: failed to open audio device");
+ if (!m_resampler.setup(QWindowsAudioUtils::waveFormatExToFormat(*pwfx), m_format)) {
+ qCWarning(qLcAudioSource) << "Failed to set up resampler";
return false;
}
- waveBlocks = allocateBlocks(period_size, buffer_size/period_size);
- waveBlockOffset = 0;
-
- if(waveBlocks == 0) {
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- qWarning("QAudioSource: failed to allocate blocks. open failed");
+
+ if (m_bufferSize == 0)
+ m_bufferSize = m_format.sampleRate() * m_format.bytesPerFrame() / 5; // 200ms
+
+ REFERENCE_TIME requestedDuration = m_format.durationForBytes(m_bufferSize);
+
+ hr = m_audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, requestedDuration, 0, pwfx.get(),
+ nullptr);
+
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioSource) << "Failed to initialize audio client" << errorString(hr);
return false;
}
- mutex.lock();
- waveFreeBlockCount = buffer_size/period_size;
- mutex.unlock();
-
- for(int i=0; i<buffer_size/period_size; i++) {
- result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
- if(result != MMSYSERR_NOERROR) {
- qWarning("QAudioSource: failed to setup block %d,err=%d",i,result);
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return false;
- }
+ auto framesAllocated = QWindowsAudioUtils::audioClientFramesAllocated(m_audioClient.Get());
+ if (!framesAllocated) {
+ qCWarning(qLcAudioSource) << "Failed to get audio client buffer size";
+ return false;
}
- result = waveInStart(hWaveIn);
- if(result) {
- qWarning("QAudioSource: failed to start audio input");
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
+
+ m_bufferSize = m_format.bytesForDuration(
+ m_resampler.inputFormat().durationForFrames(*framesAllocated));
+
+ hr = m_audioClient->GetService(__uuidof(IAudioCaptureClient), (void**)m_captureClient.GetAddressOf());
+ if (FAILED(hr)) {
+ qCWarning(qLcAudioSource) << "Failed to obtain audio client rendering service" << errorString(hr);
return false;
}
- elapsedTimeOffset = 0;
- totalTimeValue = 0;
- errorState = QAudio::NoError;
- initMixer();
+
return true;
}
void QWindowsAudioSource::close()
{
- if(deviceState == QAudio::StoppedState)
+ qCDebug(qLcAudioSource) << "close()";
+ if (m_deviceState == QAudio::StoppedState)
return;
- deviceState = QAudio::StoppedState;
- waveInReset(hWaveIn);
+ deviceStateChange(QAudio::StoppedState, QAudio::NoError);
- mutex.lock();
- for (int i=0; i<waveFreeBlockCount; i++)
- waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR));
- freeBlocks(waveBlocks);
- mutex.unlock();
-
- waveInClose(hWaveIn);
- closeMixer();
-
- int count = 0;
- while(!finished && count < 500) {
- count++;
- Sleep(10);
- }
+ m_clientBufferResidue.clear();
+ m_captureClient.Reset();
+ m_audioClient.Reset();
+ m_clientSink = nullptr;
}
-void QWindowsAudioSource::initMixer()
+qsizetype QWindowsAudioSource::bytesReady() const
{
- // Get the Mixer ID from the Sound Device ID
- UINT mixerIntID = 0;
- if (mixerGetID(reinterpret_cast<HMIXEROBJ>(hWaveIn),
- &mixerIntID, MIXER_OBJECTF_HWAVEIN) != MMSYSERR_NOERROR)
- return;
- mixerID = reinterpret_cast<HMIXEROBJ>(quintptr(mixerIntID));
+ if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState)
+ return 0;
- // Get the Destination (Recording) Line Information
- MIXERLINE mixerLine;
- mixerLine.cbStruct = sizeof(MIXERLINE);
- mixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
- if (mixerGetLineInfo(mixerID, &mixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR)
- return;
+ auto frames = QWindowsAudioUtils::audioClientFramesInUse(m_audioClient.Get());
+ if (frames) {
+ auto clientBufferSize = m_resampler.outputFormat().bytesForDuration(
+ m_resampler.inputFormat().durationForFrames(*frames));
- // Set all the Destination (Recording) Line Controls
- if (mixerLine.cControls > 0) {
- mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
- mixerLineControls.dwLineID = mixerLine.dwLineID;
- mixerLineControls.cControls = mixerLine.cControls;
- mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
- mixerLineControls.pamxctrl = new MIXERCONTROL[mixerLineControls.cControls];
- if (mixerGetLineControls(mixerID, &mixerLineControls, MIXER_GETLINECONTROLSF_ALL) != MMSYSERR_NOERROR)
- closeMixer();
- else if (!qFuzzyCompare(cachedVolume, qreal(-1.0f)))
- setVolume(cachedVolume);
+ return clientBufferSize + m_clientBufferResidue.size();
+
+ } else {
+ return 0;
}
}
-void QWindowsAudioSource::closeMixer()
+qint64 QWindowsAudioSource::read(char* data, qint64 len)
{
- delete[] mixerLineControls.pamxctrl;
- memset(&mixerLineControls, 0, sizeof(mixerLineControls));
-}
+ deviceStateChange(QAudio::ActiveState, QAudio::NoError);
+ schedulePull();
-qsizetype QWindowsAudioSource::bytesReady() const
-{
- if (period_size == 0 || buffer_size == 0)
- return 0;
- if (deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
- return 0;
+ if (data == nullptr || len < 0)
+ return -1;
- int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size;
- if(buf < 0)
- buf = 0;
- return buf;
-}
+ auto offset = 0;
+ if (!m_clientBufferResidue.isEmpty()) {
+ auto copyLen = qMin(m_clientBufferResidue.size(), len);
+ memcpy(data, m_clientBufferResidue.data(), copyLen);
+ len -= copyLen;
+ offset += copyLen;
+ }
-qint64 QWindowsAudioSource::read(char* data, qint64 len)
-{
- bool done = false;
-
- char* p = data;
- qint64 l = 0;
- qint64 written = 0;
- while(!done) {
- // Read in some audio data
- if(waveBlocks[header].dwBytesRecorded > 0 && waveBlocks[header].dwFlags & WHDR_DONE) {
- if(pullMode) {
- l = audioSource->write(waveBlocks[header].lpData + waveBlockOffset,
- waveBlocks[header].dwBytesRecorded - waveBlockOffset);
-#ifdef DEBUG_AUDIO
- qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
-#endif
- if(l < 0) {
- // error
- qWarning("QAudioSource: IOError");
- errorState = QAudio::IOError;
-
- } else if(l == 0) {
- // cant write to IODevice
- qWarning("QAudioSource: IOError, can't write to QIODevice");
- errorState = QAudio::IOError;
-
- } else {
- totalTimeValue += l;
- errorState = QAudio::NoError;
- if (deviceState != QAudio::ActiveState) {
- deviceState = QAudio::ActiveState;
- emit stateChanged(deviceState);
- }
- resuming = false;
- }
- } else {
- l = qMin<qint64>(len, waveBlocks[header].dwBytesRecorded - waveBlockOffset);
- // push mode
- memcpy(p, waveBlocks[header].lpData + waveBlockOffset, l);
-
- len -= l;
-
-#ifdef DEBUG_AUDIO
- qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
-#endif
- totalTimeValue += l;
- errorState = QAudio::NoError;
- if (deviceState != QAudio::ActiveState) {
- deviceState = QAudio::ActiveState;
- emit stateChanged(deviceState);
- }
- resuming = false;
- }
- } else {
- //no data, not ready yet, next time
- break;
- }
+ m_clientBufferResidue = QByteArray{ m_clientBufferResidue.data() + offset,
+ m_clientBufferResidue.size() - offset };
- if (l < waveBlocks[header].dwBytesRecorded - waveBlockOffset) {
- waveBlockOffset += l;
- done = true;
- } else {
- waveBlockOffset = 0;
-
- waveInUnprepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
-
- mutex.lock();
- waveFreeBlockCount++;
- mutex.unlock();
-
- waveBlocks[header].dwBytesRecorded=0;
- waveBlocks[header].dwFlags = 0L;
- result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
- if(result != MMSYSERR_NOERROR) {
- result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
- qWarning("QAudioSource: failed to prepare block %d,err=%d",header,result);
- errorState = QAudio::IOError;
-
- mutex.lock();
- waveFreeBlockCount--;
- mutex.unlock();
-
- return 0;
- }
- result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR));
- if(result != MMSYSERR_NOERROR) {
- qWarning("QAudioSource: failed to setup block %d,err=%d",header,result);
- errorState = QAudio::IOError;
-
- mutex.lock();
- waveFreeBlockCount--;
- mutex.unlock();
-
- return 0;
- }
- header++;
- if(header >= buffer_size/period_size)
- header = 0;
- p+=l;
-
- mutex.lock();
- if(!pullMode) {
- if(len < period_size || waveFreeBlockCount == buffer_size/period_size)
- done = true;
- } else {
- if(waveFreeBlockCount == buffer_size/period_size)
- done = true;
- }
- mutex.unlock();
- }
+ if (len > 0) {
+ auto out = readCaptureClientBuffer();
+ if (!out.isEmpty()) {
+ qsizetype copyLen = qMin(out.size(), len);
+ memcpy(data + offset, out.data(), copyLen);
+ offset += copyLen;
- written+=l;
+ m_clientBufferResidue = QByteArray{ out.data() + copyLen, out.size() - copyLen };
+ }
}
-#ifdef DEBUG_AUDIO
- qDebug()<<"read in len="<<written;
-#endif
- return written;
+
+ return offset;
}
void QWindowsAudioSource::resume()
{
- if (deviceState == QAudio::SuspendedState) {
- deviceState = QAudio::ActiveState;
- for(int i=0; i<buffer_size/period_size; i++) {
- result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
- if(result != MMSYSERR_NOERROR) {
- qWarning("QAudioSource: failed to setup block %d,err=%d",i,result);
- errorState = QAudio::OpenError;
- deviceState = QAudio::StoppedState;
- emit stateChanged(deviceState);
- return;
- }
- }
-
- mutex.lock();
- waveFreeBlockCount = buffer_size/period_size;
- mutex.unlock();
-
- header = 0;
- resuming = true;
- waveBlockOffset = 0;
- waveInStart(hWaveIn);
- QTimer::singleShot(20,this,SLOT(feedback()));
- emit stateChanged(deviceState);
+ qCDebug(qLcAudioSource) << "resume()";
+ if (m_deviceState == QAudio::SuspendedState) {
+ deviceStateChange(QAudio::ActiveState, QAudio::NoError);
+ pullCaptureClient();
}
}
void QWindowsAudioSource::setBufferSize(qsizetype value)
{
- buffer_size = value;
+ m_bufferSize = value;
}
qsizetype QWindowsAudioSource::bufferSize() const
{
- return buffer_size;
+ return m_bufferSize;
}
qint64 QWindowsAudioSource::processedUSecs() const
{
- if (deviceState == QAudio::StoppedState)
+ if (m_deviceState == QAudio::StoppedState)
return 0;
- qint64 result = qint64(1000000) * totalTimeValue /
- settings.bytesPerFrame() / settings.sampleRate();
- return result;
+ return m_resampler.outputFormat().durationForBytes(m_resampler.totalOutputBytes());
}
void QWindowsAudioSource::suspend()
{
- if(deviceState == QAudio::ActiveState) {
- deviceState = QAudio::SuspendedState;
- waveInReset(hWaveIn);
- emit stateChanged(deviceState);
- }
-}
-
-void QWindowsAudioSource::feedback()
-{
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT "<<this;
-#endif
- if(deviceState != QAudio::StoppedState && deviceState != QAudio::SuspendedState)
- QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
-}
-
-bool QWindowsAudioSource::deviceReady()
-{
- bytesAvailable = bytesReady();
-#ifdef DEBUG_AUDIO
- QTime now(QTime::currentTime());
- qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT" << bytesReady();
-#endif
- if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
- return true;
-
- if(pullMode) {
- // reads some audio data and writes it to QIODevice
- read(0, period_size * (buffer_size / period_size));
- } else {
- // emits readyRead() so user will call read() on QIODevice to get some audio data
- InputPrivate* a = qobject_cast<InputPrivate*>(audioSource);
- a->trigger();
- }
-
- return true;
+ qCDebug(qLcAudioSource) << "suspend";
+ if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState)
+ deviceStateChange(QAudio::SuspendedState, QAudio::NoError);
}
void QWindowsAudioSource::reset()
{
stop();
- if (period_size > 0)
- waveFreeBlockCount = buffer_size / period_size;
-}
-
-InputPrivate::InputPrivate(QWindowsAudioSource* audio)
-{
- audioDevice = qobject_cast<QWindowsAudioSource*>(audio);
-}
-
-InputPrivate::~InputPrivate() {}
-
-qint64 InputPrivate::readData( char* data, qint64 len)
-{
- // push mode, user read() called
- if(audioDevice->deviceState != QAudio::ActiveState &&
- audioDevice->deviceState != QAudio::IdleState)
- return 0;
- // Read in some audio data
- return audioDevice->read(data,len);
-}
-
-qint64 InputPrivate::writeData(const char* data, qint64 len)
-{
- Q_UNUSED(data);
- Q_UNUSED(len);
-
- emit readyRead();
- return 0;
-}
-
-void InputPrivate::trigger()
-{
- emit readyRead();
}
QT_END_NAMESPACE
-
-#include "moc_qwindowsaudiosource_p.cpp"
diff --git a/src/multimedia/windows/qwindowsaudiosource_p.h b/src/multimedia/windows/qwindowsaudiosource_p.h
index 17bbdaaee..68d57bbe1 100644
--- a/src/multimedia/windows/qwindowsaudiosource_p.h
+++ b/src/multimedia/windows/qwindowsaudiosource_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -60,29 +24,26 @@
#include <QtCore/qstringlist.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qmutex.h>
+#include <QtCore/qbytearray.h>
#include <QtMultimedia/qaudio.h>
#include <QtMultimedia/qaudiodevice.h>
#include <private/qaudiosystem_p.h>
+#include <qwindowsresampler_p.h>
-QT_BEGIN_NAMESPACE
-
+struct IMMDevice;
+struct IAudioClient;
+struct IAudioCaptureClient;
-// For compat with 4.6
-#if !defined(QT_WIN_CALLBACK)
-# if defined(Q_CC_MINGW)
-# define QT_WIN_CALLBACK CALLBACK __attribute__ ((force_align_arg_pointer))
-# else
-# define QT_WIN_CALLBACK CALLBACK
-# endif
-#endif
+QT_BEGIN_NAMESPACE
+class QTimer;
class QWindowsAudioSource : public QPlatformAudioSource
{
Q_OBJECT
public:
- QWindowsAudioSource(int deviceId);
+ QWindowsAudioSource(ComPtr<IMMDevice> device, QObject *parent);
~QWindowsAudioSource();
qint64 read(char* data, qint64 len);
@@ -104,65 +65,30 @@ public:
void setVolume(qreal volume) override;
qreal volume() const override;
- QIODevice* audioSource;
- QAudioFormat settings;
- QAudio::Error errorState;
- QAudio::State deviceState;
-
private:
- qint32 buffer_size;
- qint32 period_size;
- qint32 header;
- int m_deviceId;
- int bytesAvailable;
- qint64 elapsedTimeOffset;
- qint64 totalTimeValue;
- bool pullMode;
- bool resuming;
- WAVEFORMATEXTENSIBLE wfx;
- HWAVEIN hWaveIn;
- MMRESULT result;
- WAVEHDR* waveBlocks;
- volatile bool finished;
- volatile int waveFreeBlockCount;
- int waveBlockOffset;
-
- QMutex mutex;
- static void QT_WIN_CALLBACK waveInProc( HWAVEIN hWaveIn, UINT uMsg,
- DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 );
-
- WAVEHDR* allocateBlocks(int size, int count);
- void freeBlocks(WAVEHDR* blockArray);
+ void deviceStateChange(QAudio::State state, QAudio::Error error);
+ void pullCaptureClient();
+ void schedulePull();
+ QByteArray readCaptureClientBuffer();
+
+ QTimer *m_timer = nullptr;
+ ComPtr<IMMDevice> m_device;
+ ComPtr<IAudioClient> m_audioClient;
+ ComPtr<IAudioCaptureClient> m_captureClient;
+ QWindowsResampler m_resampler;
+ int m_bufferSize = 0;
+ qreal m_volume = 1.0;
+
+ QIODevice* m_ourSink = nullptr;
+ QIODevice* m_clientSink = nullptr;
+ QAudioFormat m_format;
+ QAudio::Error m_errorState = QAudio::NoError;
+ QAudio::State m_deviceState = QAudio::StoppedState;
+
+ QByteArray m_clientBufferResidue;
+
bool open();
void close();
-
- void initMixer();
- void closeMixer();
- HMIXEROBJ mixerID;
- MIXERLINECONTROLS mixerLineControls;
- qreal cachedVolume;
-
-private slots:
- void feedback();
- bool deviceReady();
-
-signals:
- void processMore();
-};
-
-class InputPrivate : public QIODevice
-{
- Q_OBJECT
-public:
- InputPrivate(QWindowsAudioSource* audio);
- ~InputPrivate();
-
- qint64 readData( char* data, qint64 len) override;
- qint64 writeData(const char* data, qint64 len) override;
-
- void trigger();
-private:
- QWindowsAudioSource *audioDevice;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsaudioutils.cpp b/src/multimedia/windows/qwindowsaudioutils.cpp
index af7f6c775..dce4a2135 100644
--- a/src/multimedia/windows/qwindowsaudioutils.cpp
+++ b/src/multimedia/windows/qwindowsaudioutils.cpp
@@ -1,47 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsaudioutils_p.h"
+#include "qwindowsmediafoundation_p.h"
#include "qdebug.h"
#include "ks.h"
#include "ksmedia.h"
+#include <audioclient.h>
+
QT_BEGIN_NAMESPACE
static QAudioFormat::AudioChannelPosition channelFormatMap[] =
@@ -190,14 +157,14 @@ QAudioFormat QWindowsAudioUtils::mediaTypeToFormat(IMFMediaType *mediaType)
return format;
}
-QWindowsIUPointer<IMFMediaType> QWindowsAudioUtils::formatToMediaType(const QAudioFormat &format)
+ComPtr<IMFMediaType> QWindowsAudioUtils::formatToMediaType(QWindowsMediaFoundation &wmf, const QAudioFormat &format)
{
- QWindowsIUPointer<IMFMediaType> mediaType;
+ ComPtr<IMFMediaType> mediaType;
if (!format.isValid())
return mediaType;
- MFCreateMediaType(mediaType.address());
+ wmf.mfCreateMediaType(mediaType.GetAddressOf());
mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
if (format.sampleFormat() == QAudioFormat::Float) {
@@ -220,4 +187,22 @@ QWindowsIUPointer<IMFMediaType> QWindowsAudioUtils::formatToMediaType(const QAud
return mediaType;
}
+std::optional<quint32> QWindowsAudioUtils::audioClientFramesInUse(IAudioClient *client)
+{
+ Q_ASSERT(client);
+ UINT32 framesPadding = 0;
+ if (SUCCEEDED(client->GetCurrentPadding(&framesPadding)))
+ return framesPadding;
+ return {};
+}
+
+std::optional<quint32> QWindowsAudioUtils::audioClientFramesAllocated(IAudioClient *client)
+{
+ Q_ASSERT(client);
+ UINT32 bufferFrameCount = 0;
+ if (SUCCEEDED(client->GetBufferSize(&bufferFrameCount)))
+ return bufferFrameCount;
+ return {};
+}
+
QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsaudioutils_p.h b/src/multimedia/windows/qwindowsaudioutils_p.h
index 09ab605dd..9382f372f 100644
--- a/src/multimedia/windows/qwindowsaudioutils_p.h
+++ b/src/multimedia/windows/qwindowsaudioutils_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSAUDIOUTILS_H
#define QWINDOWSAUDIOUTILS_H
@@ -53,22 +17,27 @@
#include <qaudioformat.h>
#include <QtCore/qt_windows.h>
-#include <private/qwindowsiupointer_p.h>
-#include <mfapi.h>
-#include <mmsystem.h>
+#include <private/qcomptr_p.h>
#include <mmreg.h>
+#include <optional>
+
+struct IAudioClient;
struct IMFMediaType;
QT_BEGIN_NAMESPACE
+class QWindowsMediaFoundation;
+
namespace QWindowsAudioUtils
{
bool formatToWaveFormatExtensible(const QAudioFormat &format, WAVEFORMATEXTENSIBLE &wfx);
QAudioFormat waveFormatExToFormat(const WAVEFORMATEX &in);
Q_MULTIMEDIA_EXPORT QAudioFormat mediaTypeToFormat(IMFMediaType *mediaType);
- QWindowsIUPointer<IMFMediaType> formatToMediaType(const QAudioFormat &format);
+ ComPtr<IMFMediaType> formatToMediaType(QWindowsMediaFoundation &, const QAudioFormat &format);
QAudioFormat::ChannelConfig maskToChannelConfig(UINT32 mask, int count);
+ std::optional<quint32> audioClientFramesInUse(IAudioClient *client);
+ std::optional<quint32> audioClientFramesAllocated(IAudioClient *client);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsiupointer_p.h b/src/multimedia/windows/qwindowsiupointer_p.h
deleted file mode 100644
index 9e9cf6894..000000000
--- a/src/multimedia/windows/qwindowsiupointer_p.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QWINDOWSIUPOINTER_H
-#define QWINDOWSIUPOINTER_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-template <class T>
-class QWindowsIUPointer
-{
-public:
- explicit QWindowsIUPointer(T *ptr = nullptr) : m_ptr(ptr) {}
- QWindowsIUPointer(const QWindowsIUPointer<T> &uiPtr) : m_ptr(uiPtr.m_ptr) { if (m_ptr) m_ptr->AddRef(); }
- QWindowsIUPointer(QWindowsIUPointer<T> &&uiPtr) : m_ptr(uiPtr.m_ptr) { uiPtr.m_ptr = nullptr; }
- ~QWindowsIUPointer() { if (m_ptr) m_ptr->Release(); }
-
- QWindowsIUPointer& operator=(const QWindowsIUPointer<T> &rhs) {
- if (this != &rhs) {
- if (m_ptr)
- m_ptr->Release();
- m_ptr = rhs.m_ptr;
- m_ptr->AddRef();
- }
- return *this;
- }
-
- QWindowsIUPointer& operator=(QWindowsIUPointer<T> &&rhs) noexcept {
- if (m_ptr)
- m_ptr->Release();
- m_ptr = rhs.m_ptr;
- rhs.m_ptr = nullptr;
- return *this;
- }
-
- explicit operator bool() const { return m_ptr != nullptr; }
- T *operator->() const { return m_ptr; }
-
- T **address() { Q_ASSERT(m_ptr == nullptr); return &m_ptr; }
- void reset(T *ptr = nullptr) { if (m_ptr) m_ptr->Release(); m_ptr = ptr; }
- T *release() { T *ptr = m_ptr; m_ptr = nullptr; return ptr; }
- T *get() const { return m_ptr; }
-
-private:
- T *m_ptr;
-};
-
-#endif
diff --git a/src/multimedia/windows/qwindowsmediadevices.cpp b/src/multimedia/windows/qwindowsmediadevices.cpp
index ab223c0d7..c622a721c 100644
--- a/src/multimedia/windows/qwindowsmediadevices.cpp
+++ b/src/multimedia/windows/qwindowsmediadevices.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsmediadevices_p.h"
#include "qmediadevices.h"
@@ -44,68 +8,37 @@
#include "qwindowsaudiosource_p.h"
#include "qwindowsaudiosink_p.h"
#include "qwindowsaudiodevice_p.h"
+#include "qcomtaskresource_p.h"
#include <mmsystem.h>
#include <mmddk.h>
#include <mfobjects.h>
#include <mfidl.h>
-#include <Mferror.h>
+#include <mferror.h>
#include <mmdeviceapi.h>
#include <qwindowsmfdefs_p.h>
#include <QtCore/qmap.h>
+#include <private/qcomobject_p.h>
+#include <private/qsystemerror_p.h>
QT_BEGIN_NAMESPACE
-class CMMNotificationClient : public IMMNotificationClient
+class CMMNotificationClient : public QComObject<IMMNotificationClient>
{
- LONG m_cRef;
- QWindowsIUPointer<IMMDeviceEnumerator> m_enumerator;
+ ComPtr<IMMDeviceEnumerator> m_enumerator;
QWindowsMediaDevices *m_windowsMediaDevices;
QMap<QString, DWORD> m_deviceState;
public:
CMMNotificationClient(QWindowsMediaDevices *windowsMediaDevices,
- QWindowsIUPointer<IMMDeviceEnumerator> enumerator,
- QMap<QString, DWORD> &&deviceState) :
- m_cRef(1),
- m_enumerator(enumerator),
- m_windowsMediaDevices(windowsMediaDevices),
- m_deviceState(deviceState)
+ ComPtr<IMMDeviceEnumerator> enumerator,
+ QMap<QString, DWORD> &&deviceState)
+ : m_enumerator(enumerator),
+ m_windowsMediaDevices(windowsMediaDevices),
+ m_deviceState(deviceState)
{}
- virtual ~CMMNotificationClient() {}
-
- // IUnknown methods -- AddRef, Release, and QueryInterface
- ULONG STDMETHODCALLTYPE AddRef() override
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- ULONG STDMETHODCALLTYPE Release() override
- {
- ULONG ulRef = InterlockedDecrement(&m_cRef);
- if (0 == ulRef) {
- delete this;
- }
- return ulRef;
- }
-
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) override
- {
- if (IID_IUnknown == riid) {
- AddRef();
- *ppvInterface = (IUnknown*)this;
- } else if (__uuidof(IMMNotificationClient) == riid) {
- AddRef();
- *ppvInterface = (IMMNotificationClient*)this;
- } else {
- *ppvInterface = NULL;
- return E_NOINTERFACE;
- }
- return S_OK;
- }
-
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR) override
{
if (role == ERole::eMultimedia)
@@ -160,94 +93,107 @@ public:
{
// windowsMediaDevice may be deleted as we are executing the callback
if (flow == EDataFlow::eCapture) {
- m_windowsMediaDevices->audioInputsChanged();
+ emit m_windowsMediaDevices->audioInputsChanged();
} else if (flow == EDataFlow::eRender) {
- m_windowsMediaDevices->audioOutputsChanged();
+ emit m_windowsMediaDevices->audioOutputsChanged();
}
}
void emitAudioDevicesChanged(LPCWSTR deviceID)
{
- QWindowsIUPointer<IMMDevice> device;
- QWindowsIUPointer<IMMEndpoint> endpoint;
+ ComPtr<IMMDevice> device;
+ ComPtr<IMMEndpoint> endpoint;
EDataFlow flow;
- if (SUCCEEDED(m_enumerator->GetDevice(deviceID, device.address()))
- && SUCCEEDED(device->QueryInterface(__uuidof(IMMEndpoint), (void**)endpoint.address()))
+ if (SUCCEEDED(m_enumerator->GetDevice(deviceID, device.GetAddressOf()))
+ && SUCCEEDED(device->QueryInterface(__uuidof(IMMEndpoint), (void**)endpoint.GetAddressOf()))
&& SUCCEEDED(endpoint->GetDataFlow(&flow)))
{
emitAudioDevicesChanged(flow);
}
}
+
+private:
+ // Destructor is not public. Caller should call Release.
+ ~CMMNotificationClient() override = default;
};
QWindowsMediaDevices::QWindowsMediaDevices()
: QPlatformMediaDevices()
{
- CoInitialize(nullptr);
-
auto hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
CLSCTX_INPROC_SERVER,__uuidof(IMMDeviceEnumerator),
(void**)&m_deviceEnumerator);
- if (SUCCEEDED(hr)) {
- QMap<QString, DWORD> devState;
- QWindowsIUPointer<IMMDeviceCollection> devColl;
- UINT count = 0;
+ if (FAILED(hr)) {
+ qWarning("Failed to instantiate IMMDeviceEnumerator (%s)."
+ "Audio device change notification will be disabled",
+ qPrintable(QSystemError::windowsComString(hr)));
+ return;
+ }
- if (SUCCEEDED(m_deviceEnumerator->EnumAudioEndpoints(EDataFlow::eAll, DEVICE_STATEMASK_ALL, devColl.address()))
- && SUCCEEDED(devColl->GetCount(&count)))
- {
- for (UINT i = 0; i < count; i++) {
- QWindowsIUPointer<IMMDevice> device;
- DWORD state = 0;
- LPWSTR id = nullptr;
-
- if (SUCCEEDED(devColl->Item(i, device.address()))
- && SUCCEEDED(device->GetState(&state))
- && SUCCEEDED(device->GetId(&id)))
- {
- devState.insert(QString::fromWCharArray(id), state);
- CoTaskMemFree(id);
- }
+ QMap<QString, DWORD> devState;
+ ComPtr<IMMDeviceCollection> devColl;
+ UINT count = 0;
+
+ if (SUCCEEDED(m_deviceEnumerator->EnumAudioEndpoints(EDataFlow::eAll, DEVICE_STATEMASK_ALL, devColl.GetAddressOf()))
+ && SUCCEEDED(devColl->GetCount(&count)))
+ {
+ for (UINT i = 0; i < count; i++) {
+ ComPtr<IMMDevice> device;
+ DWORD state = 0;
+ QComTaskResource<WCHAR> id;
+
+ if (SUCCEEDED(devColl->Item(i, device.GetAddressOf()))
+ && SUCCEEDED(device->GetState(&state))
+ && SUCCEEDED(device->GetId(id.address()))) {
+ devState.insert(QString::fromWCharArray(id.get()), state);
}
}
+ }
- m_notificationClient.reset(new CMMNotificationClient(this, m_deviceEnumerator, std::move(devState)));
- m_deviceEnumerator->RegisterEndpointNotificationCallback(m_notificationClient.get());
-
- } else {
- qWarning() << "Audio device change notification disabled";
- }
+ m_notificationClient = makeComObject<CMMNotificationClient>(this, m_deviceEnumerator, std::move(devState));
+ m_deviceEnumerator->RegisterEndpointNotificationCallback(m_notificationClient.Get());
}
QWindowsMediaDevices::~QWindowsMediaDevices()
{
if (m_deviceEnumerator) {
- m_deviceEnumerator->UnregisterEndpointNotificationCallback(m_notificationClient.get());
+ // Note: Calling UnregisterEndpointNotificationCallback after CoUninitialize
+ // will abruptly terminate application, preventing remaining destructors from
+ // being called (QTBUG-120198).
+ m_deviceEnumerator->UnregisterEndpointNotificationCallback(m_notificationClient.Get());
+ }
+ if (m_warmUpAudioClient) {
+ HRESULT hr = m_warmUpAudioClient->Stop();
+ if (FAILED(hr)) {
+ qWarning() << "Failed to stop audio engine" << hr;
+ }
}
- m_deviceEnumerator.reset();
- m_notificationClient.reset();
-
- CoUninitialize();
+ m_deviceEnumerator.Reset();
+ m_notificationClient.Reset();
+ m_warmUpAudioClient.Reset();
}
QList<QAudioDevice> QWindowsMediaDevices::availableDevices(QAudioDevice::Mode mode) const
{
+ if (!m_deviceEnumerator)
+ return {};
+
const auto audioOut = mode == QAudioDevice::Output;
const auto defaultAudioDeviceID = [this, audioOut]{
const auto dataFlow = audioOut ? EDataFlow::eRender : EDataFlow::eCapture;
- QWindowsIUPointer<IMMDevice> dev;
- LPWSTR id = nullptr;
+ ComPtr<IMMDevice> dev;
+ QComTaskResource<WCHAR> id;
QString sid;
- if (SUCCEEDED(m_deviceEnumerator->GetDefaultAudioEndpoint(dataFlow, ERole::eMultimedia, dev.address()))
- && SUCCEEDED(dev->GetId(&id))) {
- sid = QString::fromWCharArray(id);
- CoTaskMemFree(id);
+ if (SUCCEEDED(m_deviceEnumerator->GetDefaultAudioEndpoint(dataFlow, ERole::eMultimedia, dev.GetAddressOf()))) {
+ if (dev && SUCCEEDED(dev->GetId(id.address()))) {
+ sid = QString::fromWCharArray(id.get());
+ }
}
return sid.toUtf8();
}();
@@ -271,10 +217,10 @@ QList<QAudioDevice> QWindowsMediaDevices::availableDevices(QAudioDevice::Mode mo
if (waveMessage(DRV_QUERYFUNCTIONINSTANCEID, id.data(), len) != MMSYSERR_NOERROR)
continue;
- QWindowsIUPointer<IMMDevice> device;
- QWindowsIUPointer<IPropertyStore> props;
- if (FAILED(m_deviceEnumerator->GetDevice(id.data(), device.address()))
- || FAILED(device->OpenPropertyStore(STGM_READ, props.address()))) {
+ ComPtr<IMMDevice> device;
+ ComPtr<IPropertyStore> props;
+ if (FAILED(m_deviceEnumerator->GetDevice(id.data(), device.GetAddressOf()))
+ || FAILED(device->OpenPropertyStore(STGM_READ, props.GetAddressOf()))) {
continue;
}
@@ -306,16 +252,94 @@ QList<QAudioDevice> QWindowsMediaDevices::audioOutputs() const
return availableDevices(QAudioDevice::Output);
}
-QPlatformAudioSource *QWindowsMediaDevices::createAudioSource(const QAudioDevice &deviceInfo)
+QPlatformAudioSource *QWindowsMediaDevices::createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
const auto *devInfo = static_cast<const QWindowsAudioDeviceInfo *>(deviceInfo.handle());
- return new QWindowsAudioSource(devInfo->waveId());
+ return new QWindowsAudioSource(devInfo->immDev(), parent);
}
-QPlatformAudioSink *QWindowsMediaDevices::createAudioSink(const QAudioDevice &deviceInfo)
+QPlatformAudioSink *QWindowsMediaDevices::createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent)
{
const auto *devInfo = static_cast<const QWindowsAudioDeviceInfo *>(deviceInfo.handle());
- return new QWindowsAudioSink(devInfo->immDev());
+ return new QWindowsAudioSink(devInfo->immDev(), parent);
+}
+
+static bool isPrepareAudioEnabled()
+{
+ static bool isDisableAudioPrepareSet = false;
+ static const int disableAudioPrepare =
+ qEnvironmentVariableIntValue("QT_DISABLE_AUDIO_PREPARE", &isDisableAudioPrepareSet);
+
+ return !isDisableAudioPrepareSet || disableAudioPrepare == 0;
+}
+
+void QWindowsMediaDevices::prepareAudio()
+{
+ if (!isPrepareAudioEnabled())
+ return;
+
+ if (m_isAudioClientWarmedUp.exchange(true))
+ return;
+
+ ComPtr<IMMDeviceEnumerator> deviceEnumerator;
+ HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
+ __uuidof(IMMDeviceEnumerator),
+ reinterpret_cast<void **>(deviceEnumerator.GetAddressOf()));
+ if (FAILED(hr)) {
+ qWarning() << "Failed to create device enumerator" << hr;
+ return;
+ }
+
+ ComPtr<IMMDevice> device;
+ hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, device.GetAddressOf());
+ if (FAILED(hr)) {
+ if (hr != E_NOTFOUND)
+ qWarning() << "Failed to retrieve default audio endpoint" << hr;
+ return;
+ }
+
+ hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_ALL, nullptr,
+ reinterpret_cast<void **>(m_warmUpAudioClient.GetAddressOf()));
+ if (FAILED(hr)) {
+ qWarning() << "Failed to activate audio engine" << hr;
+ return;
+ }
+
+ QComTaskResource<WAVEFORMATEX> deviceFormat;
+ UINT32 currentPeriodInFrames = 0;
+ hr = m_warmUpAudioClient->GetCurrentSharedModeEnginePeriod(deviceFormat.address(),
+ &currentPeriodInFrames);
+ if (FAILED(hr)) {
+ qWarning() << "Failed to retrieve the current format and periodicity of the audio engine"
+ << hr;
+ return;
+ }
+
+ UINT32 defaultPeriodInFrames = 0;
+ UINT32 fundamentalPeriodInFrames = 0;
+ UINT32 minPeriodInFrames = 0;
+ UINT32 maxPeriodInFrames = 0;
+ hr = m_warmUpAudioClient->GetSharedModeEnginePeriod(deviceFormat.get(), &defaultPeriodInFrames,
+ &fundamentalPeriodInFrames,
+ &minPeriodInFrames, &maxPeriodInFrames);
+ if (FAILED(hr)) {
+ qWarning() << "Failed to retrieve the range of periodicities supported by the audio engine"
+ << hr;
+ return;
+ }
+
+ hr = m_warmUpAudioClient->InitializeSharedAudioStream(
+ AUDCLNT_SHAREMODE_SHARED, minPeriodInFrames, deviceFormat.get(), nullptr);
+ if (FAILED(hr)) {
+ qWarning() << "Failed to initialize audio engine stream" << hr;
+ return;
+ }
+
+ hr = m_warmUpAudioClient->Start();
+ if (FAILED(hr))
+ qWarning() << "Failed to start audio engine" << hr;
}
QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsmediadevices_p.h b/src/multimedia/windows/qwindowsmediadevices_p.h
index da5aaf18c..c82be8a0d 100644
--- a/src/multimedia/windows/qwindowsmediadevices_p.h
+++ b/src/multimedia/windows/qwindowsmediadevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSMEDIADEVICES_H
#define QWINDOWSMEDIADEVICES_H
@@ -52,10 +16,13 @@
//
#include <private/qplatformmediadevices_p.h>
-#include <private/qwindowsiupointer_p.h>
+#include <private/qcomptr_p.h>
+#include <QtCore/private/qfunctions_win_p.h>
+#include <private/qwindowsmediafoundation_p.h>
#include <qaudiodevice.h>
+struct IAudioClient3;
struct IMMDeviceEnumerator;
QT_BEGIN_NAMESPACE
@@ -71,14 +38,24 @@ public:
QList<QAudioDevice> audioInputs() const override;
QList<QAudioDevice> audioOutputs() const override;
- QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override;
- QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override;
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo,
+ QObject *parent) override;
+
+ void prepareAudio() override;
private:
+ QComHelper m_comRuntime;
+ QMFRuntimeInit m_wmfRuntime{ QWindowsMediaFoundation::instance() };
QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode) const;
- QWindowsIUPointer<IMMDeviceEnumerator> m_deviceEnumerator;
- QWindowsIUPointer<CMMNotificationClient> m_notificationClient;
+ ComPtr<IMMDeviceEnumerator> m_deviceEnumerator;
+ ComPtr<CMMNotificationClient> m_notificationClient;
+ // The "warm-up" audio client is required to run in the background in order to keep audio engine
+ // ready for audio output immediately after creating any other subsequent audio client.
+ ComPtr<IAudioClient3> m_warmUpAudioClient;
+ std::atomic_bool m_isAudioClientWarmedUp = false;
friend CMMNotificationClient;
};
diff --git a/src/multimedia/windows/qwindowsmediafoundation.cpp b/src/multimedia/windows/qwindowsmediafoundation.cpp
new file mode 100644
index 000000000..7094c9551
--- /dev/null
+++ b/src/multimedia/windows/qwindowsmediafoundation.cpp
@@ -0,0 +1,70 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwindowsmediafoundation_p.h"
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+Q_GLOBAL_STATIC(QWindowsMediaFoundation, s_wmf);
+
+template <typename T>
+bool setProcAddress(QSystemLibrary &lib, T &f, const char name[])
+{
+ f = reinterpret_cast<T>(lib.resolve(name));
+ return static_cast<bool>(f);
+}
+
+} // namespace
+
+QWindowsMediaFoundation *QWindowsMediaFoundation::instance()
+{
+ if (s_wmf->valid())
+ return s_wmf;
+
+ return nullptr;
+}
+
+QWindowsMediaFoundation::QWindowsMediaFoundation()
+{
+ if (!m_mfplat.load(false))
+ return;
+
+ m_valid = setProcAddress(m_mfplat, mfStartup, "MFStartup")
+ && setProcAddress(m_mfplat, mfShutdown, "MFShutdown")
+ && setProcAddress(m_mfplat, mfCreateMediaType, "MFCreateMediaType")
+ && setProcAddress(m_mfplat, mfCreateMemoryBuffer, "MFCreateMemoryBuffer")
+ && setProcAddress(m_mfplat, mfCreateSample, "MFCreateSample");
+
+ Q_ASSERT(m_valid); // If it is not valid at this point, we have a programming bug
+}
+
+QWindowsMediaFoundation::~QWindowsMediaFoundation() = default;
+
+bool QWindowsMediaFoundation::valid() const
+{
+ return m_valid;
+}
+
+QMFRuntimeInit::QMFRuntimeInit(QWindowsMediaFoundation *wmf)
+ : m_wmf{ wmf }, m_initResult{ wmf ? m_wmf->mfStartup(MF_VERSION, MFSTARTUP_FULL) : E_FAIL }
+{
+ if (m_initResult != S_OK)
+ qErrnoWarning(m_initResult, "Failed to initialize Windows Media Foundation");
+}
+
+QMFRuntimeInit::~QMFRuntimeInit()
+{
+ // According to documentation MFShutdown should be called
+ // also when MFStartup failed. This is wrong.
+ if (FAILED(m_initResult))
+ return;
+
+ const HRESULT hr = m_wmf->mfShutdown();
+ if (hr != S_OK)
+ qErrnoWarning(hr, "Failed to shut down Windows Media Foundation");
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsmediafoundation_p.h b/src/multimedia/windows/qwindowsmediafoundation_p.h
new file mode 100644
index 000000000..6dabc261e
--- /dev/null
+++ b/src/multimedia/windows/qwindowsmediafoundation_p.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWINDOWSMEDIAFOUNDATION_H
+#define QWINDOWSMEDIAFOUNDATION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtmultimediaglobal_p.h>
+#include <QtCore/private/qsystemlibrary_p.h>
+#include <mfapi.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsMediaFoundation
+{
+public:
+ static QWindowsMediaFoundation *instance();
+
+ QWindowsMediaFoundation();
+ ~QWindowsMediaFoundation();
+
+ bool valid() const;
+
+ decltype(&::MFStartup) mfStartup = nullptr;
+ decltype(&::MFShutdown) mfShutdown = nullptr;
+ decltype(&::MFCreateMediaType) mfCreateMediaType = nullptr;
+ decltype(&::MFCreateMemoryBuffer) mfCreateMemoryBuffer = nullptr;
+ decltype(&::MFCreateSample) mfCreateSample = nullptr;
+
+private:
+ QSystemLibrary m_mfplat{ QStringLiteral("Mfplat.dll") };
+ bool m_valid = false;
+};
+
+class QMFRuntimeInit
+{
+ Q_DISABLE_COPY_MOVE(QMFRuntimeInit)
+public:
+ QMFRuntimeInit(QWindowsMediaFoundation *wmf);
+ ~QMFRuntimeInit();
+
+private:
+ QWindowsMediaFoundation *m_wmf;
+ HRESULT m_initResult;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSMEDIAFOUNDATION_H
diff --git a/src/multimedia/windows/qwindowsmfdefs.cpp b/src/multimedia/windows/qwindowsmfdefs.cpp
index 97eae9743..ef8c99922 100644
--- a/src/multimedia/windows/qwindowsmfdefs.cpp
+++ b/src/multimedia/windows/qwindowsmfdefs.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsmfdefs_p.h"
@@ -55,7 +19,7 @@ const GUID QMM_MF_SD_STREAM_NAME = {0x4f1b099d, 0xd314, 0x41e5, {0xa7, 0x81, 0x7
const GUID QMM_MF_SD_LANGUAGE = {0xaf2180, 0xbdc2, 0x423c, {0xab, 0xca, 0xf5, 0x3, 0x59, 0x3b, 0xc1, 0x21}};
const GUID QMM_KSCATEGORY_VIDEO_CAMERA = {0xe5323777, 0xf976, 0x4f5b, {0x9b, 0x55, 0xb9, 0x46, 0x99, 0xc4, 0x6e, 0x44}};
-
+const GUID QMM_KSCATEGORY_SENSOR_CAMERA = {0x24e552d7, 0x6523, 0x47f7, {0xa6, 0x47, 0xd3, 0x46, 0x5b, 0xf1, 0xf5, 0xca}};
const GUID QMM_MR_POLICY_VOLUME_SERVICE = {0x1abaa2ac, 0x9d3b, 0x47c6, {0xab, 0x48, 0xc5, 0x95, 0x6, 0xde, 0x78, 0x4d}};
const PROPERTYKEY QMM_PKEY_Device_FriendlyName = {{0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}}, 14};
diff --git a/src/multimedia/windows/qwindowsmfdefs_p.h b/src/multimedia/windows/qwindowsmfdefs_p.h
index 5bae90d18..4bb90dbe3 100644
--- a/src/multimedia/windows/qwindowsmfdefs_p.h
+++ b/src/multimedia/windows/qwindowsmfdefs_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSMFDEFS_P_H
#define QWINDOWSMFDEFS_P_H
@@ -74,6 +38,7 @@ Q_MULTIMEDIA_EXPORT extern const GUID QMM_MF_SD_STREAM_NAME;
Q_MULTIMEDIA_EXPORT extern const GUID QMM_MF_SD_LANGUAGE;
Q_MULTIMEDIA_EXPORT extern const GUID QMM_KSCATEGORY_VIDEO_CAMERA;
+Q_MULTIMEDIA_EXPORT extern const GUID QMM_KSCATEGORY_SENSOR_CAMERA;
Q_MULTIMEDIA_EXPORT extern const GUID QMM_MR_POLICY_VOLUME_SERVICE;
@@ -83,6 +48,7 @@ extern "C" HRESULT WINAPI MFCreateDeviceSource(IMFAttributes *pAttributes, IMFMe
#define QMM_MFSESSION_GETFULLTOPOLOGY_CURRENT 1
#define QMM_PRESENTATION_CURRENT_POSITION 0x7fffffffffffffff
+#define QMM_WININET_E_CANNOT_CONNECT ((HRESULT)0x80072EFDL)
#ifndef __IMFVideoProcessor_INTERFACE_DEFINED__
#define __IMFVideoProcessor_INTERFACE_DEFINED__
diff --git a/src/multimedia/windows/qwindowsmultimediautils.cpp b/src/multimedia/windows/qwindowsmultimediautils.cpp
index b83233893..258cb3e96 100644
--- a/src/multimedia/windows/qwindowsmultimediautils.cpp
+++ b/src/multimedia/windows/qwindowsmultimediautils.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#if defined(WINVER) && WINVER < _WIN32_WINNT_WIN10
# undef WINVER
@@ -46,9 +10,11 @@
#include "qwindowsmultimediautils_p.h"
+#include <initguid.h>
#include <mfapi.h>
#include <mfidl.h>
#include <qwindowsmfdefs_p.h>
+#include <system_error>
QT_BEGIN_NAMESPACE
@@ -86,6 +52,8 @@ QVideoFrameFormat::PixelFormat QWindowsMultimediaUtils::pixelFormatFromMediaSubt
return QVideoFrameFormat::Format_Y8;
if (subtype == MFVideoFormat_L16)
return QVideoFrameFormat::Format_Y16;
+ if (subtype == MFVideoFormat_MJPG)
+ return QVideoFrameFormat::Format_Jpeg;
return QVideoFrameFormat::Format_Invalid;
}
@@ -237,4 +205,11 @@ GUID QWindowsMultimediaUtils::containerForAudioFileFormat(QMediaFormat::FileForm
}
}
+QString QWindowsMultimediaUtils::errorString(HRESULT hr)
+{
+ return QStringLiteral("%1 %2")
+ .arg(quint32(hr), 8, 16)
+ .arg(QString::fromStdString(std::system_category().message(hr)));
+}
+
QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsmultimediautils_p.h b/src/multimedia/windows/qwindowsmultimediautils_p.h
index 3821a058e..58ecd425f 100644
--- a/src/multimedia/windows/qwindowsmultimediautils_p.h
+++ b/src/multimedia/windows/qwindowsmultimediautils_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSMULTIMEDIATUTILS_P_H
#define QWINDOWSMULTIMEDIATUTILS_P_H
@@ -55,6 +19,7 @@
#include <private/qplatformmediaformatinfo_p.h>
#include <qvideoframeformat.h>
#include <guiddef.h>
+#include <qstring.h>
QT_BEGIN_NAMESPACE
@@ -73,6 +38,8 @@ namespace QWindowsMultimediaUtils {
Q_MULTIMEDIA_EXPORT GUID containerForVideoFileFormat(QMediaFormat::FileFormat format);
Q_MULTIMEDIA_EXPORT GUID containerForAudioFileFormat(QMediaFormat::FileFormat format);
+
+ Q_MULTIMEDIA_EXPORT QString errorString(HRESULT hr);
}
QT_END_NAMESPACE
diff --git a/src/multimedia/windows/qwindowsresampler.cpp b/src/multimedia/windows/qwindowsresampler.cpp
index 5b09830cc..3c50c0c19 100644
--- a/src/multimedia/windows/qwindowsresampler.cpp
+++ b/src/multimedia/windows/qwindowsresampler.cpp
@@ -1,59 +1,26 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsresampler_p.h"
#include <qwindowsaudioutils_p.h>
#include <qloggingcategory.h>
+#include <QUuid>
-#include <Wmcodecdsp.h>
+#include <wmcodecdsp.h>
#include <mftransform.h>
-#include <mfapi.h>
#include <mferror.h>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcAudioResampler, "qt.multimedia.audioresampler")
+QUuid qIID_IMFTransform(0xbf94c121, 0x5b05, 0x4e6f, 0x80,0x00, 0xba,0x59,0x89,0x61,0x41,0x4d);
+QUuid qCLSID_CResamplerMediaObject("f447b69e-1884-4a7e-8055-346f74d6edb3");
+
+static Q_LOGGING_CATEGORY(qLcAudioResampler, "qt.multimedia.audioresampler")
QWindowsResampler::QWindowsResampler()
{
- CoCreateInstance(CLSID_CResamplerMediaObject, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMFTransform, (LPVOID*)(m_resampler.address()));
+ CoCreateInstance(qCLSID_CResamplerMediaObject, nullptr, CLSCTX_INPROC_SERVER,
+ qIID_IMFTransform, (LPVOID*)(m_resampler.GetAddressOf()));
if (m_resampler)
m_resampler->AddInputStreams(1, &m_inputStreamID);
}
@@ -78,13 +45,13 @@ quint64 QWindowsResampler::inputBufferSize(quint64 outputBufferSize) const
HRESULT QWindowsResampler::processInput(const QByteArrayView &in)
{
- QWindowsIUPointer<IMFSample> sample;
- HRESULT hr = MFCreateSample(sample.address());
+ ComPtr<IMFSample> sample;
+ HRESULT hr = m_wmf->mfCreateSample(sample.GetAddressOf());
if (FAILED(hr))
return hr;
- QWindowsIUPointer<IMFMediaBuffer> buffer;
- hr = MFCreateMemoryBuffer(in.size(), buffer.address());
+ ComPtr<IMFMediaBuffer> buffer;
+ hr = m_wmf->mfCreateMemoryBuffer(in.size(), buffer.GetAddressOf());
if (FAILED(hr))
return hr;
@@ -105,29 +72,29 @@ HRESULT QWindowsResampler::processInput(const QByteArrayView &in)
if (FAILED(hr))
return hr;
- hr = sample->AddBuffer(buffer.get());
+ hr = sample->AddBuffer(buffer.Get());
if (FAILED(hr))
return hr;
- return m_resampler->ProcessInput(m_inputStreamID, sample.get(), 0);
+ return m_resampler->ProcessInput(m_inputStreamID, sample.Get(), 0);
}
HRESULT QWindowsResampler::processOutput(QByteArray &out)
{
- QWindowsIUPointer<IMFSample> sample;
- QWindowsIUPointer<IMFMediaBuffer> buffer;
+ ComPtr<IMFSample> sample;
+ ComPtr<IMFMediaBuffer> buffer;
if (m_resamplerNeedsSampleBuffer) {
- HRESULT hr = MFCreateSample(sample.address());
+ HRESULT hr = m_wmf->mfCreateSample(sample.GetAddressOf());
if (FAILED(hr))
return hr;
auto expectedOutputSize = outputBufferSize(m_totalInputBytes) - m_totalOutputBytes;
- hr = MFCreateMemoryBuffer(expectedOutputSize, buffer.address());
+ hr = m_wmf->mfCreateMemoryBuffer(expectedOutputSize, buffer.GetAddressOf());
if (FAILED(hr))
return hr;
- hr = sample->AddBuffer(buffer.get());
+ hr = sample->AddBuffer(buffer.Get());
if (FAILED(hr))
return hr;
}
@@ -139,12 +106,12 @@ HRESULT QWindowsResampler::processOutput(QByteArray &out)
do {
outputDataBuffer.pEvents = nullptr;
outputDataBuffer.dwStatus = 0;
- outputDataBuffer.pSample = m_resamplerNeedsSampleBuffer ? sample.get() : nullptr;
+ outputDataBuffer.pSample = m_resamplerNeedsSampleBuffer ? sample.Get() : nullptr;
DWORD status = 0;
hr = m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status);
if (SUCCEEDED(hr)) {
- QWindowsIUPointer<IMFMediaBuffer> outputBuffer;
- outputDataBuffer.pSample->ConvertToContiguousBuffer(outputBuffer.address());
+ ComPtr<IMFMediaBuffer> outputBuffer;
+ outputDataBuffer.pSample->ConvertToContiguousBuffer(outputBuffer.GetAddressOf());
DWORD len = 0;
BYTE *data = nullptr;
hr = outputBuffer->Lock(&data, nullptr, &len);
@@ -169,7 +136,7 @@ QByteArray QWindowsResampler::resample(const QByteArrayView &in)
return {in.data(), in.size()};
} else {
- Q_ASSERT(m_resampler);
+ Q_ASSERT(m_resampler && m_wmf);
QByteArray out;
HRESULT hr = processInput(in);
@@ -198,8 +165,8 @@ QByteArray QWindowsResampler::resample(IMFSample *sample)
QByteArray out;
if (m_inputFormat == m_outputFormat) {
- QWindowsIUPointer<IMFMediaBuffer> outputBuffer;
- sample->ConvertToContiguousBuffer(outputBuffer.address());
+ ComPtr<IMFMediaBuffer> outputBuffer;
+ sample->ConvertToContiguousBuffer(outputBuffer.GetAddressOf());
DWORD len = 0;
BYTE *data = nullptr;
hr = outputBuffer->Lock(&data, nullptr, &len);
@@ -208,7 +175,7 @@ QByteArray QWindowsResampler::resample(IMFSample *sample)
outputBuffer->Unlock();
} else {
- Q_ASSERT(m_resampler);
+ Q_ASSERT(m_resampler && m_wmf);
hr = m_resampler->ProcessInput(m_inputStreamID, sample, 0);
if (SUCCEEDED(hr))
@@ -237,18 +204,19 @@ bool QWindowsResampler::setup(const QAudioFormat &fin, const QAudioFormat &fout)
return true;
}
- Q_ASSERT(m_resampler);
+ if (!m_resampler || !m_wmf)
+ return false;
- QWindowsIUPointer<IMFMediaType> min = QWindowsAudioUtils::formatToMediaType(fin);
- QWindowsIUPointer<IMFMediaType> mout = QWindowsAudioUtils::formatToMediaType(fout);
+ ComPtr<IMFMediaType> min = QWindowsAudioUtils::formatToMediaType(*m_wmf, fin);
+ ComPtr<IMFMediaType> mout = QWindowsAudioUtils::formatToMediaType(*m_wmf, fout);
- HRESULT hr = m_resampler->SetInputType(m_inputStreamID, min.get(), 0);
+ HRESULT hr = m_resampler->SetInputType(m_inputStreamID, min.Get(), 0);
if (FAILED(hr)) {
qCWarning(qLcAudioResampler) << "Failed to set input type" << hr;
return false;
}
- hr = m_resampler->SetOutputType(0, mout.get(), 0);
+ hr = m_resampler->SetOutputType(0, mout.Get(), 0);
if (FAILED(hr)) {
qCWarning(qLcAudioResampler) << "Failed to set output type" << hr;
return false;
diff --git a/src/multimedia/windows/qwindowsresampler_p.h b/src/multimedia/windows/qwindowsresampler_p.h
index ce2be58ac..5885bffa6 100644
--- a/src/multimedia/windows/qwindowsresampler_p.h
+++ b/src/multimedia/windows/qwindowsresampler_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QT_QWINDOWSRESAMPLER_H
@@ -55,15 +19,19 @@
#include <qbytearray.h>
#include <qbytearrayview.h>
#include <qaudioformat.h>
-#include <private/qwindowsiupointer_p.h>
+#include <private/qcomptr_p.h>
+#include <private/qwindowsmediafoundation_p.h>
#include <qt_windows.h>
#include <mftransform.h>
+#include <QtCore/private/qfunctions_win_p.h>
struct IMFSample;
struct IMFTransform;
QT_BEGIN_NAMESPACE
+class QWindowsMediaFoundation;
+
class Q_MULTIMEDIA_EXPORT QWindowsResampler
{
public:
@@ -88,7 +56,10 @@ private:
HRESULT processInput(const QByteArrayView &in);
HRESULT processOutput(QByteArray &out);
- QWindowsIUPointer<IMFTransform> m_resampler;
+ QComHelper m_comRuntime;
+ QWindowsMediaFoundation *m_wmf{ QWindowsMediaFoundation::instance() };
+ QMFRuntimeInit m_wmfRuntime{ m_wmf };
+ ComPtr<IMFTransform> m_resampler;
bool m_resamplerNeedsSampleBuffer = false;
quint64 m_totalInputBytes = 0;
diff --git a/src/multimediaquick/CMakeLists.txt b/src/multimediaquick/CMakeLists.txt
index 530841a6c..eceef4e06 100644
--- a/src/multimediaquick/CMakeLists.txt
+++ b/src/multimediaquick/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#####################################################################
## MultimediaQuickPrivate Module:
#####################################################################
@@ -21,13 +24,14 @@ qt_internal_add_qml_module(MultimediaQuickPrivate
qquickimagecapture.cpp qquickimagecapture_p.h
qquickimagepreviewprovider.cpp qquickimagepreviewprovider_p.h
# qquickplaylist.cpp qquickplaylist_p.h
- qquickmediaplayer_p.h
- qquicksoundeffect_p.h
+ qquickmediaplayer.cpp qquickmediaplayer_p.h
+ qquickscreencapture.cpp qquickscreencapture_p.h
+ qquicksoundeffect.cpp qquicksoundeffect_p.h
qquickvideooutput.cpp qquickvideooutput_p.h
qsgvideonode_p.cpp qsgvideonode_p.h
qsgvideotexture.cpp qsgvideotexture_p.h
qtmultimediaquickglobal_p.h
- qtmultimediaquicktypes_p.h
+ qtmultimediaquicktypes.cpp qtmultimediaquicktypes_p.h
QML_FILES
${qml_files}
PUBLIC_LIBRARIES
diff --git a/src/multimediaquick/Video.qml b/src/multimediaquick/Video.qml
index 3b94c7321..80d28c805 100644
--- a/src/multimediaquick/Video.qml
+++ b/src/multimediaquick/Video.qml
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import QtQuick
import QtMultimedia
@@ -114,6 +78,8 @@ import QtMultimedia
Item {
id: video
+ implicitWidth: videoOut.implicitWidth
+ implicitHeight: videoOut.implicitHeight
/*** Properties of VideoOutput ***/
/*!
@@ -286,6 +252,15 @@ Item {
property alias source: player.source
/*!
+ \since 6.7
+ \qmlproperty bool Video::autoPlay
+
+ This property controls whether the media begins to play automatically after it gets loaded.
+ Defaults to \c false.
+ */
+ property alias autoPlay: player.autoPlay
+
+ /*!
\qmlproperty real Video::volume
This property holds the audio volume.
@@ -298,7 +273,7 @@ Item {
UI volume controls should usually be scaled nonlinearly. For example,
using a logarithmic scale will produce linear changes in perceived
loudness, which is what a user would normally expect from a volume
- control. See \l {QAudio::convertVolume()} for more details.
+ control. See \l {QtAudio::convertVolume()} for more details.
*/
property alias volume: audioOutput.volume
diff --git a/src/multimediaquick/multimedia_plugin.cpp b/src/multimediaquick/multimedia_plugin.cpp
index df12044c0..9509b4b5f 100644
--- a/src/multimediaquick/multimedia_plugin.cpp
+++ b/src/multimediaquick/multimedia_plugin.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
diff --git a/src/multimediaquick/qquickimagecapture.cpp b/src/multimediaquick/qquickimagecapture.cpp
index 95519ef28..b7e56d18d 100644
--- a/src/multimediaquick/qquickimagecapture.cpp
+++ b/src/multimediaquick/qquickimagecapture.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickimagecapture_p.h"
#include "qquickimagepreviewprovider_p.h"
@@ -92,7 +56,7 @@ QT_BEGIN_NAMESPACE
QQuickImageCapture::QQuickImageCapture(QObject *parent)
: QImageCapture(parent)
{
- connect(this, SIGNAL(imageCaptured(int,QImage)), this, SLOT(_q_imageCaptured(int,QImage)));
+ connect(this, &QImageCapture::imageCaptured, this, &QQuickImageCapture::_q_imageCaptured);
}
QQuickImageCapture::~QQuickImageCapture() = default;
@@ -176,46 +140,45 @@ void QQuickImageCapture::saveToFile(const QUrl &location) const
void QQuickImageCapture::_q_imageCaptured(int id, const QImage &preview)
{
- QString previewId = QString::fromLatin1("preview_%1").arg(id);
+ QString previewId = QStringLiteral("preview_%1").arg(id);
QQuickImagePreviewProvider::registerPreview(previewId, preview);
- m_capturedImagePath = QString::fromLatin1("image://camera/%2").arg(previewId);
+ m_capturedImagePath = QStringLiteral("image://camera/%2").arg(previewId);
m_lastImage = preview;
emit previewChanged();
}
/*!
- \qmlsignal QtMultimedia::ImageCapture::errorOccurred(requestId, error, message)
+ \qmlsignal QtMultimedia::ImageCapture::errorOccurred(id, error, errorString)
- This signal is emitted when an error occurs during capture with \a requestId.
+ This signal is emitted when an error occurs during capture with requested \a id.
\a error is an enumeration of type ImageCapture::Error.
- A descriptive message is available in \a message.
+ A descriptive message is available in \a errorString.
*/
/*!
- \qmlsignal QtMultimedia::ImageCapture::imageCaptured(requestId, previewImage)
+ \qmlsignal QtMultimedia::ImageCapture::imageCaptured(requestId, preview)
- This signal is emitted when an image with \a requestId has been captured
- but not yet saved to the filesystem. The \a previewImage
+ This signal is emitted when an image with requested id \a requestId has been captured
+ but not yet saved to the filesystem. The \a preview
parameter is the captured image.
\sa imageSaved, preview
*/
/*!
- \qmlsignal QtMultimedia::ImageCapture::imageSaved(requestId, path)
+ \qmlsignal QtMultimedia::ImageCapture::imageSaved(id, fileName)
- This signal is emitted after the image with \a requestId has been written to the filesystem.
- The \a path is a local file path, not a URL.
+ This signal is emitted after the image with requested \a id has been written to the filesystem.
+ The \a fileName is a local file path, not a URL.
\sa imageCaptured
*/
/*!
- \qmlsignal QtMultimedia::ImageCapture::imageMetadataAvailable(requestId, key, value)
+ \qmlsignal QtMultimedia::ImageCapture::imageMetadataAvailable(id, metaData)
- This signal is emitted when the image with \a requestId has new metadata
- available with the key \a key and value \a value.
+ This signal is emitted when the image with requested \a id has new \a metaData.
\sa imageCaptured
*/
diff --git a/src/multimediaquick/qquickimagecapture_p.h b/src/multimediaquick/qquickimagecapture_p.h
index 30cd984ac..01f21af75 100644
--- a/src/multimediaquick/qquickimagecapture_p.h
+++ b/src/multimediaquick/qquickimagecapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKIMAGECAPTURE_H
#define QQUICKIMAGECAPTURE_H
diff --git a/src/multimediaquick/qquickimagepreviewprovider.cpp b/src/multimediaquick/qquickimagepreviewprovider.cpp
index 2f57f90d1..3b40c1c88 100644
--- a/src/multimediaquick/qquickimagepreviewprovider.cpp
+++ b/src/multimediaquick/qquickimagepreviewprovider.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickimagepreviewprovider_p.h"
#include <QtCore/qmutex.h>
diff --git a/src/multimediaquick/qquickimagepreviewprovider_p.h b/src/multimediaquick/qquickimagepreviewprovider_p.h
index 6aa3a0499..3ffd55eff 100644
--- a/src/multimediaquick/qquickimagepreviewprovider_p.h
+++ b/src/multimediaquick/qquickimagepreviewprovider_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKIMAGEPREVIEWPROVIDER_H
#define QQUICKIMAGEPREVIEWPROVIDER_H
diff --git a/src/multimediaquick/qquickmediaplayer.cpp b/src/multimediaquick/qquickmediaplayer.cpp
new file mode 100644
index 000000000..6b49c827e
--- /dev/null
+++ b/src/multimediaquick/qquickmediaplayer.cpp
@@ -0,0 +1,95 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmediaplayer_p.h"
+#include <QtQml/qqmlcontext.h>
+
+QT_BEGIN_NAMESPACE
+
+QQuickMediaPlayer::QQuickMediaPlayer(QObject *parent) : QMediaPlayer(parent)
+{
+ connect(this, &QMediaPlayer::positionChanged, this, &QQuickMediaPlayer::onPositionChanged);
+ connect(this, &QMediaPlayer::durationChanged, this, &QQuickMediaPlayer::onDurationChanged);
+ connect(this, &QMediaPlayer::mediaStatusChanged, this,
+ &QQuickMediaPlayer::onMediaStatusChanged);
+}
+
+void QQuickMediaPlayer::qmlSetSource(const QUrl &source)
+{
+ if (m_source == source)
+ return;
+ m_source = source;
+ m_wasMediaLoaded = false;
+ const QQmlContext *context = qmlContext(this);
+ setSource(context ? context->resolvedUrl(source) : source);
+ emit qmlSourceChanged(source);
+}
+
+QUrl QQuickMediaPlayer::qmlSource() const
+{
+ return m_source;
+}
+
+void QQuickMediaPlayer::setQmlPosition(int position)
+{
+ setPosition(static_cast<qint64>(position));
+}
+
+int QQuickMediaPlayer::qmlPosition() const
+{
+ return static_cast<int>(position());
+}
+
+int QQuickMediaPlayer::qmlDuration() const
+{
+ return static_cast<int>(duration());
+}
+
+void QQuickMediaPlayer::onPositionChanged(qint64 position)
+{
+ emit qmlPositionChanged(static_cast<int>(position));
+}
+
+void QQuickMediaPlayer::onDurationChanged(qint64 duration)
+{
+ emit qmlDurationChanged(static_cast<int>(duration));
+}
+
+void QQuickMediaPlayer::onMediaStatusChanged(QMediaPlayer::MediaStatus status)
+{
+ if (status != QMediaPlayer::LoadedMedia || std::exchange(m_wasMediaLoaded, true))
+ return;
+
+ // run with QueuedConnection to make the user able to handle the media status change
+ // by themselves, otherwise play() might change the status in the handler.
+ auto tryAutoPlay = [this]() {
+ if (m_autoPlay && mediaStatus() == QMediaPlayer::LoadedMedia)
+ play();
+ };
+
+ if (m_autoPlay)
+ QMetaObject::invokeMethod(this, tryAutoPlay, Qt::QueuedConnection);
+}
+
+/*!
+ \since 6.7
+ \qmlproperty bool QtMultimedia::MediaPlayer::autoPlay
+
+ This property controls whether the media begins to play automatically after it gets loaded.
+ Defaults to \c false.
+*/
+
+bool QQuickMediaPlayer::autoPlay() const
+{
+ return m_autoPlay;
+}
+
+void QQuickMediaPlayer::setAutoPlay(bool autoPlay)
+{
+ if (std::exchange(m_autoPlay, autoPlay) != autoPlay)
+ emit autoPlayChanged(autoPlay);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickmediaplayer_p.cpp"
diff --git a/src/multimediaquick/qquickmediaplayer_p.h b/src/multimediaquick/qquickmediaplayer_p.h
index 1c2d553d9..be2ad6b12 100644
--- a/src/multimediaquick/qquickmediaplayer_p.h
+++ b/src/multimediaquick/qquickmediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKMEDIAPLAYER_H
#define QQUICKMEDIAPLAYER_H
@@ -53,8 +17,8 @@
#include <QMediaPlayer>
#include <QtQml/qqml.h>
-#include <QtQml/qqmlcontext.h>
#include <qtmultimediaquickexports.h>
+#include <qurl.h>
#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -62,30 +26,48 @@ QT_BEGIN_NAMESPACE
class Q_MULTIMEDIAQUICK_EXPORT QQuickMediaPlayer : public QMediaPlayer
{
Q_OBJECT
- Q_PROPERTY(QUrl source READ qmlSource WRITE qmlSetSource NOTIFY sourceChanged)
+ Q_PROPERTY(QUrl source READ qmlSource WRITE qmlSetSource NOTIFY qmlSourceChanged FINAL)
+
+ // qml doesn't support qint64, so we have to convert to the supported type.
+ // Int is expected to be enough for actual purposes.
+ Q_PROPERTY(int duration READ qmlDuration NOTIFY qmlDurationChanged FINAL)
+ Q_PROPERTY(int position READ qmlPosition WRITE setQmlPosition NOTIFY qmlPositionChanged FINAL)
+ Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged FINAL)
+
QML_NAMED_ELEMENT(MediaPlayer)
public:
- QQuickMediaPlayer(QObject *parent = nullptr) : QMediaPlayer(parent) {}
+ QQuickMediaPlayer(QObject *parent = nullptr);
+
+ void qmlSetSource(const QUrl &source);
+
+ QUrl qmlSource() const;
- void qmlSetSource(const QUrl &source)
- {
- if (m_source == source)
- return;
+ void setQmlPosition(int position);
- m_source = source;
- const QQmlContext *context = qmlContext(this);
- setSource(context ? context->resolvedUrl(source) : source);
- emit sourceChanged(source);
- }
+ int qmlPosition() const;
- QUrl qmlSource() const { return m_source; }
+ int qmlDuration() const;
+
+ bool autoPlay() const;
+
+ void setAutoPlay(bool autoPlay);
+
+private:
+ void onPositionChanged(qint64 position);
+ void onDurationChanged(qint64 position);
+ void onMediaStatusChanged(QMediaPlayer::MediaStatus status);
Q_SIGNALS:
- void sourceChanged(const QUrl &source);
+ void qmlSourceChanged(const QUrl &source);
+ void qmlPositionChanged(int position);
+ void qmlDurationChanged(int duration);
+ void autoPlayChanged(bool autoPlay);
private:
QUrl m_source;
+ bool m_autoPlay = false;
+ bool m_wasMediaLoaded = false;
};
QT_END_NAMESPACE
diff --git a/src/multimediaquick/qquickplaylist.cpp b/src/multimediaquick/qquickplaylist.cpp
index 3c5f4852a..986191647 100644
--- a/src/multimediaquick/qquickplaylist.cpp
+++ b/src/multimediaquick/qquickplaylist.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickplaylist_p.h"
diff --git a/src/multimediaquick/qquickplaylist_p.h b/src/multimediaquick/qquickplaylist_p.h
index 1e0f91220..4003cefe7 100644
--- a/src/multimediaquick/qquickplaylist_p.h
+++ b/src/multimediaquick/qquickplaylist_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKPLAYLIST_P_H
#define QQUICKPLAYLIST_P_H
diff --git a/src/multimediaquick/qquickscreencapture.cpp b/src/multimediaquick/qquickscreencapture.cpp
new file mode 100644
index 000000000..9c2b0ef6f
--- /dev/null
+++ b/src/multimediaquick/qquickscreencapture.cpp
@@ -0,0 +1,27 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickscreencapture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQuickScreenCatpure::QQuickScreenCatpure(QObject *parent) : QScreenCapture(parent)
+{
+ connect(this, &QScreenCapture::screenChanged, this, [this](QScreen *screen) {
+ emit QQuickScreenCatpure::screenChanged(new QQuickScreenInfo(this, screen));
+ });
+}
+
+void QQuickScreenCatpure::qmlSetScreen(const QQuickScreenInfo *info)
+{
+ setScreen(info ? info->wrappedScreen() : nullptr);
+}
+
+QQuickScreenInfo *QQuickScreenCatpure::qmlScreen()
+{
+ return new QQuickScreenInfo(this, screen());
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickscreencapture_p.cpp"
diff --git a/src/multimediaquick/qquickscreencapture_p.h b/src/multimediaquick/qquickscreencapture_p.h
new file mode 100644
index 000000000..2c46493f5
--- /dev/null
+++ b/src/multimediaquick/qquickscreencapture_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKSCREENCAPTURE_H
+#define QQUICKSCREENCAPTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QScreenCapture>
+#include <qtmultimediaquickexports.h>
+#include <private/qglobal_p.h>
+#include <private/qquickscreen_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_MULTIMEDIAQUICK_EXPORT QQuickScreenCatpure : public QScreenCapture
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickScreenInfo *screen READ qmlScreen WRITE qmlSetScreen NOTIFY screenChanged)
+ QML_NAMED_ELEMENT(ScreenCapture)
+
+public:
+ QQuickScreenCatpure(QObject *parent = nullptr);
+
+ void qmlSetScreen(const QQuickScreenInfo *info);
+
+ QQuickScreenInfo *qmlScreen();
+
+Q_SIGNALS:
+ void screenChanged(QQuickScreenInfo *);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimediaquick/qquicksoundeffect.cpp b/src/multimediaquick/qquicksoundeffect.cpp
new file mode 100644
index 000000000..89530f07f
--- /dev/null
+++ b/src/multimediaquick/qquicksoundeffect.cpp
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquicksoundeffect_p.h"
+#include <QtQml/qqmlcontext.h>
+
+QT_BEGIN_NAMESPACE
+
+QQuickSoundEffect::QQuickSoundEffect(QObject *parent) : QSoundEffect(parent) { }
+
+void QQuickSoundEffect::qmlSetSource(const QUrl &source)
+{
+ if (m_source == source)
+ return;
+
+ m_source = source;
+ const QQmlContext *context = qmlContext(this);
+ setSource(context ? context->resolvedUrl(source) : source);
+ emit sourceChanged(source);
+}
+
+QUrl QQuickSoundEffect::qmlSource() const
+{
+ return m_source;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquicksoundeffect_p.cpp"
diff --git a/src/multimediaquick/qquicksoundeffect_p.h b/src/multimediaquick/qquicksoundeffect_p.h
index ee1af3843..8a7246420 100644
--- a/src/multimediaquick/qquicksoundeffect_p.h
+++ b/src/multimediaquick/qquicksoundeffect_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSOUNDEFFECT_H
#define QQUICKSOUNDEFFECT_H
@@ -53,8 +17,8 @@
#include <QSoundEffect>
#include <QtQml/qqml.h>
-#include <QtQml/qqmlcontext.h>
#include <qtmultimediaquickexports.h>
+#include <qurl.h>
QT_BEGIN_NAMESPACE
@@ -65,20 +29,11 @@ class Q_MULTIMEDIAQUICK_EXPORT QQuickSoundEffect : public QSoundEffect
QML_NAMED_ELEMENT(SoundEffect)
public:
- QQuickSoundEffect(QObject *parent = nullptr) : QSoundEffect(parent) {}
+ QQuickSoundEffect(QObject *parent = nullptr);
- void qmlSetSource(const QUrl &source)
- {
- if (m_source == source)
- return;
+ void qmlSetSource(const QUrl &source);
- m_source = source;
- const QQmlContext *context = qmlContext(this);
- setSource(context ? context->resolvedUrl(source) : source);
- emit sourceChanged(source);
- }
-
- QUrl qmlSource() const { return m_source; }
+ QUrl qmlSource() const;
Q_SIGNALS:
void sourceChanged(const QUrl &source);
diff --git a/src/multimediaquick/qquickvideooutput.cpp b/src/multimediaquick/qquickvideooutput.cpp
index fe2119e61..8af974759 100644
--- a/src/multimediaquick/qquickvideooutput.cpp
+++ b/src/multimediaquick/qquickvideooutput.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickvideooutput_p.h"
#include <private/qvideooutputorientationhandler_p.h>
@@ -47,11 +11,13 @@
#include <qvideosink.h>
#include <QtQuick/QQuickWindow>
#include <private/qquickwindow_p.h>
+#include <private/qmultimediautils_p.h>
#include <qsgvideonode_p.h>
+#include <QtCore/qrunnable.h>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcVideo, "qt.multimedia.video")
+static Q_LOGGING_CATEGORY(qLcVideo, "qt.multimedia.video")
namespace {
@@ -141,8 +107,12 @@ QQuickVideoOutput::QQuickVideoOutput(QQuickItem *parent) :
m_sink = new QVideoSink(this);
qRegisterMetaType<QVideoFrameFormat>();
- QObject::connect(m_sink, SIGNAL(videoFrameChanged(const QVideoFrame &)),
- this, SLOT(_q_newFrame(const QVideoFrame &)), Qt::QueuedConnection);
+ connect(m_sink, &QVideoSink::videoFrameChanged, this,
+ [this](const QVideoFrame &frame) {
+ setFrame(frame);
+ QMetaObject::invokeMethod(this, &QQuickVideoOutput::_q_newFrame, frame.size());
+ },
+ Qt::DirectConnection);
initRhiForSink();
}
@@ -197,13 +167,11 @@ void QQuickVideoOutput::setFillMode(FillMode mode)
emit fillModeChanged(mode);
}
-void QQuickVideoOutput::_q_newFrame(const QVideoFrame &frame)
+void QQuickVideoOutput::_q_newFrame(QSize size)
{
- present(frame);
- QSize size = frame.size();
- if (!qIsDefaultAspect(m_orientation + m_frameOrientation)) {
- size.transpose();
- }
+ update();
+
+ size = qRotatedFrameSize(size, m_orientation + m_frameOrientation);
if (m_nativeSize != size) {
m_nativeSize = size;
@@ -356,7 +324,7 @@ QRectF QQuickVideoOutput::sourceRect() const
if (!size.isValid())
return {};
- if (!qIsDefaultAspect(m_orientation))
+ if (!qIsDefaultAspect(m_orientation + m_frameOrientation))
size.transpose();
@@ -425,23 +393,23 @@ void QQuickVideoOutput::itemChange(QQuickItem::ItemChange change,
if (m_window) {
// We want to receive the signals in the render thread
- QObject::connect(m_window, &QQuickWindow::sceneGraphInitialized, this, &QQuickVideoOutput::_q_sceneGraphInitialized,
- Qt::DirectConnection);
- QObject::connect(m_window, &QQuickWindow::sceneGraphInvalidated,
- this, &QQuickVideoOutput::_q_invalidateSceneGraph, Qt::DirectConnection);
+ connect(m_window, &QQuickWindow::sceneGraphInitialized, this,
+ &QQuickVideoOutput::_q_sceneGraphInitialized, Qt::DirectConnection);
+ connect(m_window, &QQuickWindow::sceneGraphInvalidated, this,
+ &QQuickVideoOutput::_q_invalidateSceneGraph, Qt::DirectConnection);
}
initRhiForSink();
}
QSize QQuickVideoOutput::nativeSize() const
{
- return m_surfaceFormat.viewport().size();
+ return m_videoFormat.viewport().size();
}
void QQuickVideoOutput::updateGeometry()
{
- const QRectF viewport = m_surfaceFormat.viewport();
- const QSizeF frameSize = m_surfaceFormat.frameSize();
+ const QRectF viewport = m_videoFormat.viewport();
+ const QSizeF frameSize = m_videoFormat.frameSize();
const QRectF normalizedViewport(viewport.x() / frameSize.width(),
viewport.y() / frameSize.height(),
viewport.width() / frameSize.width(),
@@ -482,13 +450,13 @@ void QQuickVideoOutput::updateGeometry()
}
}
- if (m_surfaceFormat.scanLineDirection() == QVideoFrameFormat::BottomToTop) {
+ if (m_videoFormat.scanLineDirection() == QVideoFrameFormat::BottomToTop) {
qreal top = m_sourceTextureRect.top();
m_sourceTextureRect.setTop(m_sourceTextureRect.bottom());
m_sourceTextureRect.setBottom(top);
}
- if (m_surfaceFormat.isMirrored()) {
+ if (m_videoFormat.isMirrored()) {
qreal left = m_sourceTextureRect.left();
m_sourceTextureRect.setLeft(m_sourceTextureRect.right());
m_sourceTextureRect.setRight(left);
@@ -522,7 +490,7 @@ QSGNode *QQuickVideoOutput::updatePaintNode(QSGNode *oldNode,
// Get a node that supports our frame. The surface is irrelevant, our
// QSGVideoItemSurface supports (logically) anything.
updateGeometry();
- videoNode = new QSGVideoNode(this, m_surfaceFormat);
+ videoNode = new QSGVideoNode(this, m_videoFormat);
qCDebug(qLcVideo) << "updatePaintNode: Video node created. Handle type:" << m_frame.handleType();
}
}
@@ -536,6 +504,8 @@ QSGNode *QQuickVideoOutput::updatePaintNode(QSGNode *oldNode,
if (m_frameChanged) {
videoNode->setCurrentFrame(m_frame);
+ updateHdr(videoNode);
+
//don't keep the frame for more than really necessary
m_frameChanged = false;
m_frame = QVideoFrame();
@@ -548,26 +518,47 @@ QSGNode *QQuickVideoOutput::updatePaintNode(QSGNode *oldNode,
return videoNode;
}
-QRectF QQuickVideoOutput::adjustedViewport() const
+void QQuickVideoOutput::updateHdr(QSGVideoNode *videoNode)
{
- return m_surfaceFormat.viewport();
+ auto *videoOutputWindow = window();
+ if (!videoOutputWindow)
+ return;
+
+ auto *swapChain = videoOutputWindow->swapChain();
+ if (!swapChain)
+ return;
+
+ const auto requiredSwapChainFormat = qGetRequiredSwapChainFormat(m_frame.surfaceFormat());
+ if (qShouldUpdateSwapChainFormat(swapChain, requiredSwapChainFormat)) {
+ auto *recreateSwapChainJob = QRunnable::create([swapChain, requiredSwapChainFormat]() {
+ swapChain->destroy();
+ swapChain->setFormat(requiredSwapChainFormat);
+ swapChain->createOrResize();
+ });
+
+ // Even though the 'recreate swap chain' job is scheduled for the current frame the
+ // effect will be visible only starting from the next frame since the recreation would
+ // happen after the actual swap.
+ videoOutputWindow->scheduleRenderJob(recreateSwapChainJob, QQuickWindow::AfterSwapStage);
+ }
+
+ videoNode->setSurfaceFormat(swapChain->format());
+ videoNode->setHdrInfo(swapChain->hdrInfo());
}
-void QQuickVideoOutput::present(const QVideoFrame &frame)
+QRectF QQuickVideoOutput::adjustedViewport() const
{
- m_frameMutex.lock();
- m_surfaceFormat = frame.surfaceFormat();
- m_frame = frame;
- m_frameOrientation = frame.rotationAngle();
- m_frameChanged = true;
- m_frameMutex.unlock();
-
- update();
+ return m_videoFormat.viewport();
}
-void QQuickVideoOutput::stop()
+void QQuickVideoOutput::setFrame(const QVideoFrame &frame)
{
- present(QVideoFrame());
+ QMutexLocker lock(&m_frameMutex);
+
+ m_videoFormat = frame.surfaceFormat();
+ m_frame = frame;
+ m_frameOrientation = static_cast<int>(frame.rotation());
+ m_frameChanged = true;
}
QT_END_NAMESPACE
diff --git a/src/multimediaquick/qquickvideooutput_p.h b/src/multimediaquick/qquickvideooutput_p.h
index ff58a9963..d71051939 100644
--- a/src/multimediaquick/qquickvideooutput_p.h
+++ b/src/multimediaquick/qquickvideooutput_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKVIDEOOUTPUT_P_H
#define QQUICKVIDEOOUTPUT_P_H
@@ -67,6 +31,7 @@ QT_BEGIN_NAMESPACE
class QQuickVideoBackend;
class QVideoOutputOrientationHandler;
class QVideoSink;
+class QSGVideoNode;
class Q_MULTIMEDIAQUICK_EXPORT QQuickVideoOutput : public QQuickItem
{
@@ -123,16 +88,15 @@ private:
void updateGeometry();
QRectF adjustedViewport() const;
- friend class QSGVideoItemSurface;
- void present(const QVideoFrame &frame);
- void stop();
+ void setFrame(const QVideoFrame &frame);
void invalidateSceneGraph();
void initRhiForSink();
+ void updateHdr(QSGVideoNode *videoNode);
private Q_SLOTS:
- void _q_newFrame(const QVideoFrame &);
+ void _q_newFrame(QSize);
void _q_updateGeometry();
void _q_invalidateSceneGraph();
void _q_sceneGraphInitialized();
@@ -149,8 +113,9 @@ private:
QPointer<QQuickWindow> m_window;
QVideoSink *m_sink = nullptr;
- QVideoFrameFormat m_surfaceFormat;
+ QVideoFrameFormat m_videoFormat;
+ QList<QVideoFrame> m_videoFrameQueue;
QVideoFrame m_frame;
bool m_frameChanged = false;
QMutex m_frameMutex;
diff --git a/src/multimediaquick/qsgvideonode_p.cpp b/src/multimediaquick/qsgvideonode_p.cpp
index 7fc940fec..5dc107337 100644
--- a/src/multimediaquick/qsgvideonode_p.cpp
+++ b/src/multimediaquick/qsgvideonode_p.cpp
@@ -1,49 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsgvideonode_p.h"
+#include "private/qmultimediautils_p.h"
#include <QtQuick/qsgmaterial.h>
#include "qsgvideotexture_p.h"
#include <QtMultimedia/private/qvideotexturehelper_p.h>
-#include <private/qquicktextnode_p.h>
+#include <private/qsginternaltextnode_p.h>
+#include <private/qquickitem_p.h>
#include <private/qquickvideooutput_p.h>
-#include <qmutex.h>
+#include <private/qabstractvideobuffer_p.h>
QT_BEGIN_NAMESPACE
@@ -75,12 +41,17 @@ class QSGVideoMaterial;
class QSGVideoMaterialRhiShader : public QSGMaterialShader
{
public:
- QSGVideoMaterialRhiShader(const QSGVideoMaterial *material, const QVideoFrameFormat &format)
- : m_material(material),
- m_format(format)
+ QSGVideoMaterialRhiShader(const QVideoFrameFormat &videoFormat,
+ const QRhiSwapChain::Format surfaceFormat,
+ const QRhiSwapChainHdrInfo &hdrInfo)
+ : m_videoFormat(videoFormat)
+ , m_surfaceFormat(surfaceFormat)
+ , m_hdrInfo(hdrInfo)
{
- setShaderFileName(VertexStage, m_format.vertexShaderFileName());
- setShaderFileName(FragmentStage, m_format.fragmentShaderFileName());
+ setShaderFileName(VertexStage, QVideoTextureHelper::vertexShaderFileName(m_videoFormat));
+ setShaderFileName(
+ FragmentStage,
+ QVideoTextureHelper::fragmentShaderFileName(m_videoFormat, m_surfaceFormat));
}
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
@@ -90,34 +61,34 @@ public:
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
protected:
- const QSGVideoMaterial *m_material = nullptr;
- QVideoFrameFormat m_format;
- float m_planeWidth[3] = {0, 0, 0};
- QMatrix4x4 m_colorMatrix;
+ QVideoFrameFormat m_videoFormat;
+ QRhiSwapChain::Format m_surfaceFormat;
+ QRhiSwapChainHdrInfo m_hdrInfo;
};
class QSGVideoMaterial : public QSGMaterial
{
public:
- QSGVideoMaterial(const QVideoFrameFormat &format);
+ QSGVideoMaterial(const QVideoFrameFormat &videoFormat);
[[nodiscard]] QSGMaterialType *type() const override {
- static QSGMaterialType type[QVideoFrameFormat::NPixelFormats];
- return &type[m_format.pixelFormat()];
+ static constexpr int NFormats = QRhiSwapChain::HDRExtendedDisplayP3Linear + 1;
+ static QSGMaterialType type[QVideoFrameFormat::NPixelFormats][NFormats];
+ return &type[m_videoFormat.pixelFormat()][m_surfaceFormat];
}
[[nodiscard]] QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override {
- return new QSGVideoMaterialRhiShader(this, m_format);
+ return new QSGVideoMaterialRhiShader(m_videoFormat, m_surfaceFormat, m_hdrInfo);
}
int compare(const QSGMaterial *other) const override {
const QSGVideoMaterial *m = static_cast<const QSGVideoMaterial *>(other);
- qint64 diff = m_textures[0]->comparisonKey() - m->m_textures[0]->comparisonKey();
+ qint64 diff = m_textures[0].comparisonKey() - m->m_textures[0].comparisonKey();
if (!diff)
- diff = m_textures[1]->comparisonKey() - m->m_textures[1]->comparisonKey();
+ diff = m_textures[1].comparisonKey() - m->m_textures[1].comparisonKey();
if (!diff)
- diff = m_textures[2]->comparisonKey() - m->m_textures[2]->comparisonKey();
+ diff = m_textures[2].comparisonKey() - m->m_textures[2].comparisonKey();
return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
}
@@ -128,29 +99,38 @@ public:
}
void setCurrentFrame(const QVideoFrame &frame) {
- QMutexLocker lock(&m_frameMutex);
m_currentFrame = frame;
m_texturesDirty = true;
}
+ void setSurfaceFormat(const QRhiSwapChain::Format surfaceFormat)
+ {
+ m_surfaceFormat = surfaceFormat;
+ }
+
+ void setHdrInfo(const QRhiSwapChainHdrInfo &hdrInfo)
+ {
+ m_hdrInfo = hdrInfo;
+ }
+
void updateTextures(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates);
- QVideoFrameFormat m_format;
- float m_planeWidth[3];
- float m_opacity;
+ QVideoFrameFormat m_videoFormat;
+ QRhiSwapChain::Format m_surfaceFormat = QRhiSwapChain::SDR;
+ float m_opacity = 1.0f;
+ QRhiSwapChainHdrInfo m_hdrInfo;
- QMutex m_frameMutex;
bool m_texturesDirty = false;
QVideoFrame m_currentFrame;
enum { NVideoFrameSlots = 4 };
QVideoFrame m_videoFrameSlots[NVideoFrameSlots];
- QScopedPointer<QSGVideoTexture> m_textures[3];
+ std::array<QSGVideoTexture, 3> m_textures;
+ std::unique_ptr<QVideoFrameTextures> m_videoFrameTextures;
};
void QSGVideoMaterial::updateTextures(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
{
- QMutexLocker locker(&m_frameMutex);
if (!m_texturesDirty)
return;
@@ -159,14 +139,13 @@ void QSGVideoMaterial::updateTextures(QRhi *rhi, QRhiResourceUpdateBatch *resour
m_videoFrameSlots[rhi->currentFrameSlot()] = m_currentFrame;
// update and upload all textures
- for (int plane = 0; plane < 3; ++plane) {
- QSGVideoTexture *sgTex = m_textures[plane].get();
- if (sgTex) {
- std::unique_ptr<QRhiTexture> tex(sgTex->releaseTexture());
- QVideoTextureHelper::updateRhiTexture(m_currentFrame, rhi, resourceUpdates, plane, tex);
- sgTex->setRhiTexture(tex.release());
- }
- }
+ m_videoFrameTextures = QVideoTextureHelper::createTextures(m_currentFrame, rhi, resourceUpdates, std::move(m_videoFrameTextures));
+ if (!m_videoFrameTextures)
+ return;
+
+ for (int plane = 0; plane < 3; ++plane)
+ m_textures[plane].setRhiTexture(m_videoFrameTextures->texture(plane));
+ m_texturesDirty = false;
}
@@ -190,8 +169,16 @@ bool QSGVideoMaterialRhiShader::updateUniformData(RenderState &state, QSGMateria
// updated by this function and we need that already in updateUniformData.
m->updateTextures(state.rhi(), state.resourceUpdateBatch());
- m_format.updateUniformData(state.uniformData(), m->m_currentFrame,
- state.combinedMatrix(), state.opacity());
+ float maxNits = 100; // Default to de-facto SDR nits
+ if (m_surfaceFormat == QRhiSwapChain::HDRExtendedSrgbLinear) {
+ if (m_hdrInfo.limitsType == QRhiSwapChainHdrInfo::ColorComponentValue)
+ maxNits = 100 * m_hdrInfo.limits.colorComponentValue.maxColorComponentValue;
+ else
+ maxNits = m_hdrInfo.limits.luminanceInNits.maxLuminance;
+ }
+
+ QVideoTextureHelper::updateUniformData(state.uniformData(), m_videoFormat,
+ m->m_currentFrame, state.combinedMatrix(), state.opacity(), maxNits);
return true;
}
@@ -205,28 +192,21 @@ void QSGVideoMaterialRhiShader::updateSampledImage(RenderState &state, int bindi
return;
auto m = static_cast<QSGVideoMaterial *>(newMaterial);
- *texture = m->m_textures[binding - 1].data();
+ *texture = &m->m_textures[binding - 1];
}
-QSGVideoMaterial::QSGVideoMaterial(const QVideoFrameFormat &format) :
- m_format(format),
- m_opacity(1.0)
+QSGVideoMaterial::QSGVideoMaterial(const QVideoFrameFormat &videoFormat)
+ : m_videoFormat(videoFormat)
{
- m_textures[0].reset(new QSGVideoTexture);
- m_textures[1].reset(new QSGVideoTexture);
- m_textures[2].reset(new QSGVideoTexture);
-
setFlag(Blending, false);
}
-QSGVideoNode::QSGVideoNode(QQuickVideoOutput *parent, const QVideoFrameFormat &format)
- : m_parent(parent),
- m_orientation(-1),
- m_format(format)
+QSGVideoNode::QSGVideoNode(QQuickVideoOutput *parent, const QVideoFrameFormat &videoFormat)
+ : m_parent(parent), m_videoFormat(videoFormat)
{
setFlag(QSGNode::OwnsMaterial);
setFlag(QSGNode::OwnsGeometry);
- m_material = new QSGVideoMaterial(format);
+ m_material = new QSGVideoMaterial(videoFormat);
setMaterial(m_material);
}
@@ -242,13 +222,26 @@ void QSGVideoNode::setCurrentFrame(const QVideoFrame &frame)
updateSubtitle(frame);
}
+void QSGVideoNode::setSurfaceFormat(const QRhiSwapChain::Format surfaceFormat)
+{
+ m_material->setSurfaceFormat(surfaceFormat);
+ markDirty(DirtyMaterial);
+}
+
+void QSGVideoNode::setHdrInfo(const QRhiSwapChainHdrInfo &hdrInfo)
+{
+ m_material->setHdrInfo(hdrInfo);
+ markDirty(DirtyMaterial);
+}
+
void QSGVideoNode::updateSubtitle(const QVideoFrame &frame)
{
QSize subtitleFrameSize = m_rect.size().toSize();
if (subtitleFrameSize.isEmpty())
return;
- if (m_orientation % 180)
- subtitleFrameSize.transpose();
+
+ subtitleFrameSize = qRotatedFrameSize(subtitleFrameSize, m_orientation);
+
if (!m_subtitleLayout.update(subtitleFrameSize, frame.subtitleText()))
return;
@@ -257,11 +250,14 @@ void QSGVideoNode::updateSubtitle(const QVideoFrame &frame)
if (frame.subtitleText().isEmpty())
return;
- m_subtitleTextNode = new QQuickTextNode(m_parent);
+ QQuickItemPrivate *parent_d = QQuickItemPrivate::get(m_parent);
+
+ m_subtitleTextNode = parent_d->sceneGraphContext()->createInternalTextNode(parent_d->sceneGraphRenderContext());
+ m_subtitleTextNode->setColor(Qt::white);
QColor bgColor = Qt::black;
bgColor.setAlpha(128);
m_subtitleTextNode->addRectangleNode(m_subtitleLayout.bounds, bgColor);
- m_subtitleTextNode->addTextLayout(m_subtitleLayout.layout.position(), &m_subtitleLayout.layout, Qt::white);
+ m_subtitleTextNode->addTextLayout(m_subtitleLayout.layout.position(), &m_subtitleLayout.layout);
appendChildNode(m_subtitleTextNode);
setSubtitleGeometry();
}
@@ -297,15 +293,26 @@ void QSGVideoNode::setSubtitleGeometry()
/* Update the vertices and texture coordinates. Orientation must be in {0,90,180,270} */
void QSGVideoNode::setTexturedRectGeometry(const QRectF &rect, const QRectF &textureRect, int orientation)
{
- if (rect == m_rect && textureRect == m_textureRect && orientation == m_orientation)
+ const auto currentFrameOrientation = m_material ? static_cast<int>(m_material->m_currentFrame.rotation()) : 0;
+ bool frameChanged = false;
+ if (m_material) {
+ if (currentFrameOrientation != m_frameOrientation
+ || m_material->m_currentFrame.mirrored() != m_frameMirrored) {
+ frameChanged = true;
+ }
+ }
+ if (rect == m_rect && textureRect == m_textureRect && orientation == m_orientation
+ && !frameChanged)
return;
m_rect = rect;
m_textureRect = textureRect;
m_orientation = orientation;
- int videoRotation = orientation;
- videoRotation += m_material ? m_material->m_currentFrame.rotationAngle() : 0;
- videoRotation %= 360;
+ if (m_material) {
+ m_frameOrientation = currentFrameOrientation;
+ m_frameMirrored = m_material->m_currentFrame.mirrored();
+ }
+ const int videoRotation = (orientation + currentFrameOrientation) % 360;
QSGGeometry *g = geometry();
diff --git a/src/multimediaquick/qsgvideonode_p.h b/src/multimediaquick/qsgvideonode_p.h
index d62248b7c..518fdfed2 100644
--- a/src/multimediaquick/qsgvideonode_p.h
+++ b/src/multimediaquick/qsgvideonode_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSGVIDEONODE_P_H
#define QSGVIDEONODE_P_H
@@ -63,18 +27,18 @@ QT_BEGIN_NAMESPACE
class QSGVideoMaterial;
class QQuickVideoOutput;
-class QQuickTextNode;
+class QSGInternalTextNode;
class QSGVideoNode : public QSGGeometryNode
{
public:
- QSGVideoNode(QQuickVideoOutput *parent, const QVideoFrameFormat &format);
+ QSGVideoNode(QQuickVideoOutput *parent, const QVideoFrameFormat &videoFormat);
~QSGVideoNode();
- QVideoFrameFormat::PixelFormat pixelFormat() const {
- return m_format.pixelFormat();
- }
+ QVideoFrameFormat::PixelFormat pixelFormat() const { return m_videoFormat.pixelFormat(); }
void setCurrentFrame(const QVideoFrame &frame);
+ void setSurfaceFormat(const QRhiSwapChain::Format surfaceFormat);
+ void setHdrInfo(const QRhiSwapChainHdrInfo &hdrInfo);
void setTexturedRectGeometry(const QRectF &boundingRect, const QRectF &textureRect, int orientation);
@@ -85,13 +49,15 @@ private:
QQuickVideoOutput *m_parent = nullptr;
QRectF m_rect;
QRectF m_textureRect;
- int m_orientation;
+ int m_orientation = -1;
+ int m_frameOrientation = -1;
+ bool m_frameMirrored = false;
- QVideoFrameFormat m_format;
- QSGVideoMaterial *m_material;
+ QVideoFrameFormat m_videoFormat;
+ QSGVideoMaterial *m_material = nullptr;
QVideoTextureHelper::SubtitleLayout m_subtitleLayout;
- QQuickTextNode *m_subtitleTextNode = nullptr;
+ QSGInternalTextNode *m_subtitleTextNode = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/multimediaquick/qsgvideotexture.cpp b/src/multimediaquick/qsgvideotexture.cpp
index 9c84d178c..aa3da9b07 100644
--- a/src/multimediaquick/qsgvideotexture.cpp
+++ b/src/multimediaquick/qsgvideotexture.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsgvideotexture_p.h"
#include <QtQuick/qsgtexturematerial.h>
@@ -46,17 +10,13 @@ QT_BEGIN_NAMESPACE
class QSGVideoTexturePrivate
{
Q_DECLARE_PUBLIC(QSGVideoTexture)
-public:
- void updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates);
private:
QSGVideoTexture *q_ptr = nullptr;
QRhiTexture::Format m_format;
QSize m_size;
QByteArray m_data;
-
- std::unique_ptr<QRhiTexture> m_texture;
- quint64 m_nativeObject = 0;
+ QRhiTexture *m_texture = nullptr;
};
QSGVideoTexture::QSGVideoTexture()
@@ -73,11 +33,8 @@ QSGVideoTexture::~QSGVideoTexture() = default;
qint64 QSGVideoTexture::comparisonKey() const
{
Q_D(const QSGVideoTexture);
- if (d->m_nativeObject)
- return d->m_nativeObject;
-
if (d->m_texture)
- return qint64(qintptr(d->m_texture.get()));
+ return qint64(qintptr(d->m_texture));
// two textures (and so materials) with not-yet-created texture underneath are never equal
return qint64(qintptr(this));
@@ -85,7 +42,7 @@ qint64 QSGVideoTexture::comparisonKey() const
QRhiTexture *QSGVideoTexture::rhiTexture() const
{
- return d_func()->m_texture.get();
+ return d_func()->m_texture;
}
QSize QSGVideoTexture::textureSize() const
@@ -112,66 +69,9 @@ void QSGVideoTexture::setData(QRhiTexture::Format f, const QSize &s, const uchar
d->m_data = {reinterpret_cast<const char *>(data), bytes};
}
-void QSGVideoTexture::setNativeObject(quint64 obj, const QSize &s, QRhiTexture::Format f)
-{
- Q_D(QSGVideoTexture);
- setData(f, s, nullptr, 0);
- if (d->m_nativeObject != obj) {
- d->m_nativeObject = obj;
- d->m_texture.reset();
- }
-}
-
-void QSGVideoTexturePrivate::updateRhiTexture(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
-{
- Q_Q(QSGVideoTexture);
-
- bool needsRebuild = m_texture && m_texture->pixelSize() != m_size;
- if (!m_texture) {
- QRhiTexture::Flags flags;
- if (q->hasMipmaps())
- flags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
-
- m_texture.reset(rhi->newTexture(m_format, m_size, 1, flags));
- needsRebuild = true;
- }
-
- if (needsRebuild) {
- m_texture->setPixelSize(m_size);
- bool created = m_nativeObject
- ? m_texture->createFrom({m_nativeObject, 0})
- : m_texture->create();
- if (!created) {
- qWarning("Failed to build texture (size %dx%d)",
- m_size.width(), m_size.height());
- return;
- }
- }
-
- if (!m_data.isEmpty()) {
- QRhiTextureSubresourceUploadDescription subresDesc(m_data.constData(), m_data.size());
- subresDesc.setSourceSize(m_size);
- subresDesc.setDestinationTopLeft(QPoint(0, 0));
- QRhiTextureUploadEntry entry(0, 0, subresDesc);
- QRhiTextureUploadDescription desc({ entry });
- resourceUpdates->uploadTexture(m_texture.get(), desc);
- m_data.clear();
- }
-}
-
-void QSGVideoTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
-{
- d_func()->updateRhiTexture(rhi, resourceUpdates);
-}
-
-QRhiTexture *QSGVideoTexture::releaseTexture()
-{
- return d_func()->m_texture.release();
-}
-
void QSGVideoTexture::setRhiTexture(QRhiTexture *texture)
{
- d_func()->m_texture.reset(texture);
+ d_func()->m_texture = texture;
}
QT_END_NAMESPACE
diff --git a/src/multimediaquick/qsgvideotexture_p.h b/src/multimediaquick/qsgvideotexture_p.h
index d3d0beea4..f9a7377b8 100644
--- a/src/multimediaquick/qsgvideotexture_p.h
+++ b/src/multimediaquick/qsgvideotexture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSGVIDEOTEXTURE_H
#define QSGVIDEOTEXTURE_H
@@ -53,7 +17,7 @@
#include <QtQuick/QSGTexture>
#include <QImage>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <private/qtmultimediaquickglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -71,11 +35,8 @@ public:
QSize textureSize() const override;
bool hasAlphaChannel() const override;
bool hasMipmaps() const override;
- void commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override;
- QRhiTexture *releaseTexture();
void setRhiTexture(QRhiTexture *texture);
void setData(QRhiTexture::Format f, const QSize &s, const uchar *data, int bytes);
- void setNativeObject(quint64 obj, const QSize &s, QRhiTexture::Format f = QRhiTexture::RGBA8);
protected:
QScopedPointer<QSGVideoTexturePrivate> d_ptr;
diff --git a/src/multimediaquick/qtmultimediaquickglobal_p.h b/src/multimediaquick/qtmultimediaquickglobal_p.h
index 9b7be33d5..44eff1649 100644
--- a/src/multimediaquick/qtmultimediaquickglobal_p.h
+++ b/src/multimediaquick/qtmultimediaquickglobal_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
diff --git a/src/multimediaquick/qtmultimediaquicktypes.cpp b/src/multimediaquick/qtmultimediaquicktypes.cpp
new file mode 100644
index 000000000..7ba5711a5
--- /dev/null
+++ b/src/multimediaquick/qtmultimediaquicktypes.cpp
@@ -0,0 +1,12 @@
+// Copyright (C) 2022 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtmultimediaquicktypes_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// TODO...
+
+QT_END_NAMESPACE
+
+#include "moc_qtmultimediaquicktypes_p.cpp"
diff --git a/src/multimediaquick/qtmultimediaquicktypes_p.h b/src/multimediaquick/qtmultimediaquicktypes_p.h
index 18ee97bfa..80133c203 100644
--- a/src/multimediaquick/qtmultimediaquicktypes_p.h
+++ b/src/multimediaquick/qtmultimediaquicktypes_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTMULTIMEDIAQUICKTYPES_H
#define QTMULTIMEDIAQUICKTYPES_H
@@ -74,8 +38,22 @@ struct QCameraForeign
struct QImageCaptureForeign
{
Q_GADGET
+ QML_ANONYMOUS
QML_FOREIGN(QImageCapture)
- QML_NAMED_ELEMENT(ImageCapture)
+};
+
+struct QScreenCaptureForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QScreenCapture)
+};
+
+struct QScreenForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QScreen)
};
struct QMediaRecorderForeign
@@ -92,10 +70,17 @@ struct QMediaMetaDataForeign
QML_NAMED_ELEMENT(mediaMetaData)
};
+// To prevent the same type from being exported twice into qmltypes
+// (for value type and for the enums)
+struct QMediaMetaDataDerived : public QMediaMetaData
+{
+ Q_GADGET
+};
+
namespace QMediaMetaDataNamespaceForeign
{
Q_NAMESPACE
- QML_FOREIGN_NAMESPACE(QMediaMetaData)
+ QML_FOREIGN_NAMESPACE(QMediaMetaDataDerived)
QML_NAMED_ELEMENT(MediaMetaData)
};
@@ -127,10 +112,17 @@ struct QAudioDeviceForeign
QML_NAMED_ELEMENT(audioDevice)
};
+// To prevent the same type from being exported twice into qmltypes
+// (for value type and for the enums)
+struct QAudioDeviceDerived : public QAudioDevice
+{
+ Q_GADGET
+};
+
namespace QAudioDeviceNamespaceForeign
{
Q_NAMESPACE
- QML_FOREIGN_NAMESPACE(QAudioDevice)
+ QML_FOREIGN_NAMESPACE(QAudioDeviceDerived)
QML_NAMED_ELEMENT(AudioDevice)
};
@@ -141,10 +133,17 @@ struct QCameraDeviceForeign
QML_NAMED_ELEMENT(cameraDevice)
};
+// To prevent the same type from being exported twice into qmltypes
+// (for value type and for the enums)
+struct QCameraDeviceDerived : public QCameraDevice
+{
+ Q_GADGET
+};
+
namespace QCameraDeviceNamespaceForeign
{
Q_NAMESPACE
- QML_FOREIGN_NAMESPACE(QCameraDevice)
+ QML_FOREIGN_NAMESPACE(QCameraDeviceDerived)
QML_NAMED_ELEMENT(CameraDevice)
};
@@ -155,10 +154,17 @@ struct QMediaFormatForeign
QML_NAMED_ELEMENT(mediaFormat)
};
+// To prevent the same type from being exported twice into qmltypes
+// (for value type and for the enums)
+struct QMediaFormatDerived : public QMediaFormat
+{
+ Q_GADGET
+};
+
namespace QMediaFormatNamespaceForeign
{
Q_NAMESPACE
- QML_FOREIGN_NAMESPACE(QMediaFormat)
+ QML_FOREIGN_NAMESPACE(QMediaFormatDerived)
QML_NAMED_ELEMENT(MediaFormat)
};
@@ -169,6 +175,27 @@ struct QCameraFormatForeign
QML_NAMED_ELEMENT(cameraFormat)
};
+struct QVideoSinkForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QVideoSink)
+ QML_NAMED_ELEMENT(VideoSink)
+};
+
+struct QCapturableWindowForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QCapturableWindow)
+ QML_NAMED_ELEMENT(capturableWindow)
+};
+
+struct QWindowCaptureForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QWindowCapture)
+ QML_NAMED_ELEMENT(WindowCapture)
+};
+
QT_END_NAMESPACE
#endif
diff --git a/src/multimediaquick3d/qquick3dspatialaudio-qml-types.qdoc b/src/multimediaquick3d/qquick3dspatialaudio-qml-types.qdoc
deleted file mode 100644
index b09f5fb6b..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudio-qml-types.qdoc
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
-\qmlmodule QtQuick3D.SpatialAudio
-\title QtQuick3D.SpatialAudio QML Types
-\ingroup qmlmodules
-\brief Provides QML types for spatial audio in Qt Quick 3D.
-
-The QML types Spatial Audio support the basic use cases such as:
-\list
- \li have a person listening to sounds in 3D space,
- \li place sound sources in 3D space,
- \li support room acoustics with direct reflections and reverb,
- \li support stereo overlay sources (e.g. for sound tracks).
- \li support output to Headphones using binaural (virtual 3D) rendering of the sound field
- \li support output to stereo or surround speaker configurations
-\endlist
-
-\section1 QML Types
-
-Qt Quick3D Spatial Audio QML types can be imported into your application using the
-following import statement in your .qml file:
-
-\qml \QtMinorVersion
-import QtQuick3D.SpatialAudio
-\endqml
-
-\generatelist qmltypesbymodule QtQuick3D.SpatialAudio
-
-\noautolist
-*/
diff --git a/src/multimediaquick3d/qquick3dspatialaudioengine.cpp b/src/multimediaquick3d/qquick3dspatialaudioengine.cpp
deleted file mode 100644
index ebaff35cb..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudioengine.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qquick3dspatialaudioengine_p.h>
-#include <qaudiodevice.h>
-
-QT_BEGIN_NAMESPACE
-
-static QSpatialAudioEngine *globalEngine = nullptr;
-
-/*!
- \qmltype SpatialAudioEngine
- \inqmlmodule QtQuick3D.SpatialAudio
- \ingroup quick3d_spatialaudio
-
- \brief SpatialAudioEngine manages sound objects inside a 3D scene.
-
- SpatialAudioEngine manages sound objects inside a 3D scene. You can add
- SpatialAudioSoundSource objects to the scene to define sounds that happen
- at a specified location in 3D space. SpatialAudioStereoSource allows you to add
- a stereo overlay (for example voice over or a sound track).
-
- You can use SpatialAudioListener to define the position of the person listening
- to the sound field relative to the sound sources. Sound sources will be less audible
- if the listener is further away from source. They will also get mapped to the corresponding
- loudspeakers depending on the direction between listener and source. In many cases, the
- SpatialAudioListener object can simply be instantiated as a child object of the QtQuick3D.Camera
- object.
-
- Create SpatialAudioRoom objcects to simulate the sound (reflections and reverb) of a room with
- certain dimensions and different types of walls.
-
- SpatialAudioEngine does offer a mode where Qt is using simulating the effects of the ear
- using head related impulse reponse functions (see also https://en.wikipedia.org/wiki/Sound_localization)
- to localize the sound in 3D space when using headphones and create a spatial audio effect through
- headphones.
-*/
-
-
-QQuick3DSpatialAudioEngine::QQuick3DSpatialAudioEngine()
-{
- auto *e = getEngine();
- connect(e, &QSpatialAudioEngine::outputModeChanged, this, &QQuick3DSpatialAudioEngine::outputModeChanged);
- connect(e, &QSpatialAudioEngine::outputDeviceChanged, this, &QQuick3DSpatialAudioEngine::outputDeviceChanged);
- connect(e, &QSpatialAudioEngine::masterVolumeChanged, this, &QQuick3DSpatialAudioEngine::masterVolumeChanged);
-}
-
-QQuick3DSpatialAudioEngine::~QQuick3DSpatialAudioEngine()
-{
-}
-
-/*!
- \qmlproperty enumeration SpatialAudioEngine::outputMode
-
- Sets or retrieves the current output mode of the engine.
-
- \table
- \header \li Property value
- \li Description
- \row \li Normal
- \li Map the sounds to the loudspeaker configuration of the output device.
- This is normally a stereo or surround speaker setup.
- \row \li Headphone
- \li Use Headphone spatialization to create a 3D audio effect when listening
- to the sound field through headphones.
- \endtable
- */
-
-void QQuick3DSpatialAudioEngine::setOutputMode(OutputMode mode)
-{
- globalEngine->setOutputMode(QSpatialAudioEngine::OutputMode(mode));
-}
-
-QQuick3DSpatialAudioEngine::OutputMode QQuick3DSpatialAudioEngine::outputMode() const
-{
- return OutputMode(globalEngine->outputMode());
-}
-
-/*!
- \qmlproperty QtMultimedia.AudioDevice SpatialAudioEngine::outputDevice
-
- Sets or returns the device that is being used for outputting the sound field.
- */
-void QQuick3DSpatialAudioEngine::setOutputDevice(const QAudioDevice &device)
-{
- globalEngine->setOutputDevice(device);
-}
-
-QAudioDevice QQuick3DSpatialAudioEngine::outputDevice() const
-{
- return globalEngine->outputDevice();
-}
-
-/*!
- \qmlproperty float SpatialAudioEngine::masterVolume
-
- Sets or returns overall volume being used to render the sound field.
- */
-void QQuick3DSpatialAudioEngine::setMasterVolume(float volume)
-{
- globalEngine->setMasterVolume(volume);
-}
-
-float QQuick3DSpatialAudioEngine::masterVolume() const
-{
- return globalEngine->masterVolume();
-}
-
-QSpatialAudioEngine *QQuick3DSpatialAudioEngine::getEngine()
-{
- if (!globalEngine) {
- globalEngine = new QSpatialAudioEngine;
- globalEngine->start();
- }
- return globalEngine;
-}
-
-QT_END_NAMESPACE
-
-#include "moc_qquick3dspatialaudioengine_p.cpp"
diff --git a/src/multimediaquick3d/qquick3dspatialaudioengine_p.h b/src/multimediaquick3d/qquick3dspatialaudioengine_p.h
deleted file mode 100644
index 9862b337a..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudioengine_p.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QQUICK3DENGINE_H
-#define QQUICK3DENGINE_H
-
-#include <private/qquick3dnode_p.h>
-#include <QtGui/qvector3d.h>
-#include <qspatialaudioengine.h>
-
-QT_BEGIN_NAMESPACE
-
-class QQuick3DSpatialAudioSoundSource;
-
-class QQuick3DSpatialAudioEngine : public QObject
-{
- Q_OBJECT
- QML_NAMED_ELEMENT(SpatialAudioEngine)
- Q_PROPERTY(OutputMode outputMode READ outputMode WRITE setOutputMode NOTIFY outputModeChanged)
- Q_PROPERTY(QAudioDevice outputDevice READ outputDevice WRITE setOutputDevice NOTIFY outputDeviceChanged)
- Q_PROPERTY(float masterVolume READ masterVolume WRITE setMasterVolume NOTIFY masterVolumeChanged)
-
-public:
- // Keep in sync with QSpatialAudioEngine::OutputMode
- enum OutputMode {
- Normal,
- Headphone
- };
- Q_ENUM(OutputMode)
-
- QQuick3DSpatialAudioEngine();
- ~QQuick3DSpatialAudioEngine();
-
- void setOutputMode(OutputMode mode);
- OutputMode outputMode() const;
-
- void setOutputDevice(const QAudioDevice &device);
- QAudioDevice outputDevice() const;
-
- void setMasterVolume(float volume);
- float masterVolume() const;
-
- static QSpatialAudioEngine *getEngine();
-
-Q_SIGNALS:
- void outputModeChanged();
- void outputDeviceChanged();
- void masterVolumeChanged();
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimediaquick3d/qquick3dspatialaudiolistener.cpp b/src/multimediaquick3d/qquick3dspatialaudiolistener.cpp
deleted file mode 100644
index 98dc0fd45..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudiolistener.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qquick3dspatialaudiolistener_p.h>
-#include <qquick3dspatialaudiosoundsource_p.h>
-#include <qquick3dspatialaudioengine_p.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \qmltype SpatialAudioListener
- \inqmlmodule QtQuick3D.SpatialAudio
- \ingroup quick3d_spatialaudio
-
- \brief defines the position and orientation of the person listening to a sound field
- defined by a SpatialAudioEngine.
-
- A SpatialAudioEngine can have exactly one listener, that defines the position and orientation
- of the person listening to the sounds defined by the objects placed within the audio engine.
-
- In most cases, the SpatialAudioListener should simply be a child of the Camera element in QtQuick3D.
- This will ensure that the sound experience is aligned with the visual rendering of the scene.
- */
-
-QQuick3DSpatialAudioListener::QQuick3DSpatialAudioListener()
-{
- m_listener = new QSpatialAudioListener(QQuick3DSpatialAudioEngine::getEngine());
- connect(this, &QQuick3DNode::scenePositionChanged, this, &QQuick3DSpatialAudioListener::updatePosition);
- connect(this, &QQuick3DNode::sceneRotationChanged, this, &QQuick3DSpatialAudioListener::updateRotation);
- updatePosition();
- updateRotation();
-}
-
-QQuick3DSpatialAudioListener::~QQuick3DSpatialAudioListener()
-{
- delete m_listener;
-}
-
-void QQuick3DSpatialAudioListener::updatePosition()
-{
- m_listener->setPosition(scenePosition());
-}
-
-void QQuick3DSpatialAudioListener::updateRotation()
-{
- m_listener->setRotation(sceneRotation());
-}
-
-QT_END_NAMESPACE
diff --git a/src/multimediaquick3d/qquick3dspatialaudiolistener_p.h b/src/multimediaquick3d/qquick3dspatialaudiolistener_p.h
deleted file mode 100644
index a6575a0bd..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudiolistener_p.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QQUICK3DLISTENER_H
-#define QQUICK3DLISTENER_H
-
-#include <private/qquick3dnode_p.h>
-#include <QtGui/qvector3d.h>
-
-#include <qspatialaudiolistener.h>
-
-QT_BEGIN_NAMESPACE
-
-class QQuick3DSpatialAudioSoundSource;
-
-class QQuick3DSpatialAudioListener : public QQuick3DNode
-{
- Q_OBJECT
- QML_NAMED_ELEMENT(SpatialAudioListener)
-
-public:
- QQuick3DSpatialAudioListener();
- ~QQuick3DSpatialAudioListener();
-
- QSpatialAudioListener *listener() { return m_listener; }
-protected:
- QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *) override { return nullptr; }
-
-protected Q_SLOTS:
- void updatePosition();
- void updateRotation();
-
-private:
- QSpatialAudioListener *m_listener;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimediaquick3d/qquick3dspatialaudioroom.cpp b/src/multimediaquick3d/qquick3dspatialaudioroom.cpp
deleted file mode 100644
index 979ecc67d..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudioroom.cpp
+++ /dev/null
@@ -1,303 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qquick3dspatialaudioroom_p.h>
-#include <qquick3dspatialaudioengine_p.h>
-#include <qspatialaudioroom.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \qmltype SpatialAudioRoom
- \inqmlmodule QtQuick3D.SpatialAudio
- \ingroup quick3d_spatialaudio
-
- Defines a room for the spatial audio engine.
-
- If the listener is inside a room, first order sound reflections and reverb
- matching the rooms properties will get applied to the sound field.
-
- A room is always square and defined by it's center position, it's orientation and dimensions.
- Each of the 6 walls of the room can be made of different materials that will contribute
- to the computed reflections and reverb that the listener will experience while being inside
- the room.
-
- If multiple rooms cover the same position, the engine will use the room with the smallest
- volume.
- */
-
-QQuick3DSpatialAudioRoom::QQuick3DSpatialAudioRoom()
-{
- m_room = new QSpatialAudioRoom(QQuick3DSpatialAudioEngine::getEngine());
-
- connect(this, &QQuick3DNode::scenePositionChanged, this, &QQuick3DSpatialAudioRoom::updatePosition);
- connect(this, &QQuick3DNode::sceneRotationChanged, this, &QQuick3DSpatialAudioRoom::updateRotation);
- connect(m_room, &QSpatialAudioRoom::dimensionsChanged, this, &QQuick3DSpatialAudioRoom::dimensionsChanged);
- connect(m_room, &QSpatialAudioRoom::rotationChanged, this, &QQuick3DSpatialAudioRoom::rotationChanged);
- connect(m_room, &QSpatialAudioRoom::wallsChanged, this, &QQuick3DSpatialAudioRoom::wallsChanged);
- connect(m_room, &QSpatialAudioRoom::reflectionGainChanged, this, &QQuick3DSpatialAudioRoom::reflectionGainChanged);
- connect(m_room, &QSpatialAudioRoom::reverbGainChanged, this, &QQuick3DSpatialAudioRoom::reverbGainChanged);
- connect(m_room, &QSpatialAudioRoom::reverbTimeChanged, this, &QQuick3DSpatialAudioRoom::reverbTimeChanged);
- connect(m_room, &QSpatialAudioRoom::reverbBrightnessChanged, this, &QQuick3DSpatialAudioRoom::reverbBrightnessChanged);
-}
-
-QQuick3DSpatialAudioRoom::~QQuick3DSpatialAudioRoom()
-{
- delete m_room;
-}
-
-/*!
- \qmlproperty vector3D SpatialAudioRoom::dimensions
-
- Defines the dimensions of the room in 3D space. Units are in centimeters
- by default.
-
- \sa position, QSpatialAudioEngine::distanceScale
- */
-void QQuick3DSpatialAudioRoom::setDimensions(QVector3D dim)
-{
- m_room->setDimensions(dim);
-}
-
-QVector3D QQuick3DSpatialAudioRoom::dimensions() const
-{
- return m_room->dimensions();
-}
-
-/*!
- \qmlproperty SpatialAudioRoom::Material SpatialAudioRoom::left
- \qmlproperty SpatialAudioRoom::Material SpatialAudioRoom::right
- \qmlproperty SpatialAudioRoom::Material SpatialAudioRoom::front
- \qmlproperty SpatialAudioRoom::Material SpatialAudioRoom::back
- \qmlproperty SpatialAudioRoom::Material SpatialAudioRoom::floor
- \qmlproperty SpatialAudioRoom::Material SpatialAudioRoom::ceiling
-
- Sets the material to use for the different sides of the room. Properties correlate to
- coordinates as follows:
-
- \table
- \header
- \li Property
- \li Coordinate
- \row \li left \li Negative x
- \row \li right \li Positive x
- \row \li back \li Negative z
- \row \li front \li Positive z
- \row \li floor \li Negative y
- \row \li ceiling \li Positive y
- \endtable
-
- Valid values for the material are:
-
- \table
- \header
- \li Property value
- \li Description
- \row \li Transparent \li The side of the room is open and won't contribute to reflections or reverb.
- \row \li AcousticCeilingTiles \li Acoustic tiles that suppress most reflections and reverb.
- \row \li BrickBare \li A bare brick wall.
- \row \li BrickPainted \li A painted brick wall.
- \row \li ConcreteBlockCoarse \li A raw concrete wall
- \row \li ConcreteBlockPainted \li A painted concrete wall
- \row \li CurtainHeavy \li A heavy curtain. Will mostly reflect low frequencies
- \row \li FiberGlassInsulation \li Fiber glass insulation. Only reflects very low frequencies
- \row \li GlassThin \li A thin glass wall
- \row \li GlassThick \li A thick glass wall
- \row \li Grass \li Grass
- \row \li LinoleumOnConcrete \li A Linoleum floor
- \row \li Marble \li A marble floor
- \row \li Metal \li Metal
- \row \li ParquetOnConcrete \li Parquet wooden floor on concrete
- \row \li PlasterRough \li Rough plaster
- \row \li PlasterSmooth \li Smooth plaster
- \row \li PlywoodPanel \li Plywodden panel
- \row \li PolishedConcreteOrTile \li Polished concrete or tiles
- \row \li Sheetrock \li Rock
- \row \li WaterOrIceSurface \li Water or ice
- \row \li WoodCeiling \li A wooden ceiling
- \row \li WoodPanel \li Wooden panel
- \row \li Uniform \li Artificial material giving uniform reflections on all frequencies
- \endtable
- */
-void QQuick3DSpatialAudioRoom::setLeft(Material material)
-{
- m_room->setWallMaterial(QSpatialAudioRoom::LeftWall, QSpatialAudioRoom::Material(material));
-}
-
-QQuick3DSpatialAudioRoom::Material QQuick3DSpatialAudioRoom::left() const
-{
- return Material(m_room->wallMaterial(QSpatialAudioRoom::LeftWall));
-}
-
-void QQuick3DSpatialAudioRoom::setRight(Material material)
-{
- m_room->setWallMaterial(QSpatialAudioRoom::RightWall, QSpatialAudioRoom::Material(material));
-}
-
-QQuick3DSpatialAudioRoom::Material QQuick3DSpatialAudioRoom::right() const
-{
- return Material(m_room->wallMaterial(QSpatialAudioRoom::RightWall));
-}
-
-void QQuick3DSpatialAudioRoom::setFront(Material material)
-{
- m_room->setWallMaterial(QSpatialAudioRoom::FrontWall, QSpatialAudioRoom::Material(material));
-}
-
-QQuick3DSpatialAudioRoom::Material QQuick3DSpatialAudioRoom::front() const
-{
- return Material(m_room->wallMaterial(QSpatialAudioRoom::FrontWall));
-}
-
-void QQuick3DSpatialAudioRoom::setBack(Material material)
-{
- m_room->setWallMaterial(QSpatialAudioRoom::BackWall, QSpatialAudioRoom::Material(material));
-}
-
-QQuick3DSpatialAudioRoom::Material QQuick3DSpatialAudioRoom::back() const
-{
- return Material(m_room->wallMaterial(QSpatialAudioRoom::BackWall));
-}
-
-void QQuick3DSpatialAudioRoom::setFloor(Material material)
-{
- m_room->setWallMaterial(QSpatialAudioRoom::Floor, QSpatialAudioRoom::Material(material));
-}
-
-QQuick3DSpatialAudioRoom::Material QQuick3DSpatialAudioRoom::floor() const
-{
- return Material(m_room->wallMaterial(QSpatialAudioRoom::Floor));
-}
-
-void QQuick3DSpatialAudioRoom::setCeiling(Material material)
-{
- m_room->setWallMaterial(QSpatialAudioRoom::Ceiling, QSpatialAudioRoom::Material(material));
-}
-
-QQuick3DSpatialAudioRoom::Material QQuick3DSpatialAudioRoom::ceiling() const
-{
- return Material(m_room->wallMaterial(QSpatialAudioRoom::Ceiling));
-}
-
-/*!
- \qmlproperty float SpatialAudioRoom::reflectionGain
-
- A gain factor for reflections generated in this room. A value
- from 0 to 1 will dampen reflections, while a value larger than 1
- will apply a gain to reflections, making them louder.
-
- The default is 1, a factor of 0 disables reflections. Negative
- values are mapped to 0.
- */
-void QQuick3DSpatialAudioRoom::setReflectionGain(float factor)
-{
- m_room->setReflectionGain(factor);
-}
-
-float QQuick3DSpatialAudioRoom::reflectionGain() const
-{
- return m_room->reflectionGain();
-}
-
-/*!
- \qmlproperty float SpatialAudioRoom::reverbGain
-
- A gain factor for reverb generated in this room. A value
- from 0 to 1 will dampen reverb, while a value larger than 1
- will apply a gain to the reverb, making it louder.
-
- The default is 1, a factor of 0 disables reverb. Negative
- values are mapped to 0.
- */
-void QQuick3DSpatialAudioRoom::setReverbGain(float factor)
-{
- m_room->setReverbGain(factor);
-}
-
-float QQuick3DSpatialAudioRoom::reverbGain() const
-{
- return m_room->reverbGain();
-}
-
-/*!
- \qmlproperty float SpatialAudioRoom::reverbTime
-
- A factor to be applies to all reverb timings generated for this room.
- Larger values will lead to longer reverb timings, making the room sound
- larger.
-
- The default is 1. Negative values are mapped to 0.
- */
-void QQuick3DSpatialAudioRoom::setReverbTime(float factor)
-{
- m_room->setReverbTime(factor);
-}
-
-float QQuick3DSpatialAudioRoom::reverbTime() const
-{
- return m_room->reverbTime();
-}
-
-/*!
- \qmlproperty float SpatialAudioRoom::reverbBrightness
-
- A brightness factor to be applied to the generated reverb.
- A positive value will increase reverb for higher frequencies and
- dampen lower frequencies, a negative value does the reverse.
-
- The default is 0.
- */
-void QQuick3DSpatialAudioRoom::setReverbBrightness(float factor)
-{
- m_room->setReverbBrightness(factor);
-}
-
-float QQuick3DSpatialAudioRoom::reverbBrightness() const
-{
- return m_room->reverbBrightness();
-}
-
-void QQuick3DSpatialAudioRoom::updatePosition()
-{
- m_room->setPosition(scenePosition());
-}
-
-void QQuick3DSpatialAudioRoom::updateRotation()
-{
- m_room->setRotation(sceneRotation());
-}
-
-QT_END_NAMESPACE
diff --git a/src/multimediaquick3d/qquick3dspatialaudioroom_p.h b/src/multimediaquick3d/qquick3dspatialaudioroom_p.h
deleted file mode 100644
index 184365c29..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudioroom_p.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QQUICK3DSPATIALAUDIOROOM_H
-#define QQUICK3DSPATIALAUDIOROOM_H
-
-#include <private/qquick3dnode_p.h>
-#include <QtGui/qvector3d.h>
-#include <qspatialaudioroom.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioEngine;
-class QSpatialAudioRoomPrivate;
-
-class QQuick3DSpatialAudioRoom : public QQuick3DNode
-{
- Q_OBJECT
- Q_PROPERTY(QVector3D position READ position WRITE setPosition NOTIFY positionChanged)
- Q_PROPERTY(QVector3D dimensions READ dimensions WRITE setDimensions NOTIFY dimensionsChanged)
- Q_PROPERTY(QQuaternion rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
- Q_PROPERTY(Material left READ left WRITE setLeft NOTIFY wallsChanged)
- Q_PROPERTY(Material right READ right WRITE setRight NOTIFY wallsChanged)
- Q_PROPERTY(Material front READ front WRITE setFront NOTIFY wallsChanged)
- Q_PROPERTY(Material back READ back WRITE setBack NOTIFY wallsChanged)
- Q_PROPERTY(Material floor READ floor WRITE setFloor NOTIFY wallsChanged)
- Q_PROPERTY(Material ceiling READ ceiling WRITE setCeiling NOTIFY wallsChanged)
- Q_PROPERTY(float reflectionGain READ reflectionGain WRITE setReflectionGain NOTIFY reflectionGainChanged)
- Q_PROPERTY(float reverbGain READ reverbGain WRITE setReverbGain NOTIFY reverbGainChanged)
- Q_PROPERTY(float reverbTime READ reverbTime WRITE setReverbTime NOTIFY reverbTimeChanged)
- Q_PROPERTY(float reverbBrightness READ reverbBrightness WRITE setReverbBrightness NOTIFY reverbBrightnessChanged)
- QML_NAMED_ELEMENT(SpatialAudioRoom)
-public:
- QQuick3DSpatialAudioRoom();
- ~QQuick3DSpatialAudioRoom();
-
- enum Material {
- Transparent,
- AcousticCeilingTiles,
- BrickBare,
- BrickPainted,
- ConcreteBlockCoarse,
- ConcreteBlockPainted,
- CurtainHeavy,
- FiberGlassInsulation,
- GlassThin,
- GlassThick,
- Grass,
- LinoleumOnConcrete,
- Marble,
- Metal,
- ParquetOnConcrete,
- PlasterRough,
- PlasterSmooth,
- PlywoodPanel,
- PolishedConcreteOrTile,
- Sheetrock,
- WaterOrIceSurface,
- WoodCeiling,
- WoodPanel,
- Uniform,
- };
- Q_ENUM(Material)
-
- void setDimensions(QVector3D pos);
- QVector3D dimensions() const;
-
- void setLeft(Material material);
- Material left() const;
-
- void setRight(Material material);
- Material right() const;
-
- void setFront(Material material);
- Material front() const;
-
- void setBack(Material material);
- Material back() const;
-
- void setFloor(Material material);
- Material floor() const;
-
- void setCeiling(Material material);
- Material ceiling() const;
-
- void setReflectionGain(float factor);
- float reflectionGain() const;
-
- void setReverbGain(float factor);
- float reverbGain() const;
-
- void setReverbTime(float factor);
- float reverbTime() const;
-
- void setReverbBrightness(float factor);
- float reverbBrightness() const;
-
-Q_SIGNALS:
- void positionChanged();
- void dimensionsChanged();
- void rotationChanged();
- void wallsChanged();
- void reflectionGainChanged();
- void reverbGainChanged();
- void reverbTimeChanged();
- void reverbBrightnessChanged();
-
-protected Q_SLOTS:
- void updatePosition();
- void updateRotation();
-
-private:
- QSpatialAudioRoom *m_room;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimediaquick3d/qquick3dspatialaudiosoundsource.cpp b/src/multimediaquick3d/qquick3dspatialaudiosoundsource.cpp
deleted file mode 100644
index 2409871dd..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudiosoundsource.cpp
+++ /dev/null
@@ -1,346 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "qquick3dspatialaudiosoundsource_p.h"
-#include "qquick3dspatialaudioengine_p.h"
-#include "qspatialaudiosoundsource.h"
-#include <QAudioFormat>
-#include <qdir.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \qmltype SpatialAudioSoundSource
- \inqmlmodule QtQuick3D.SpatialAudio
- \ingroup quick3d_spatialaudio
-
- \brief A sound object in 3D space.
-
- A SpatialAudioSoundSource represents an audible object in 3D space. You can define
- it's position and orientation in space, set the sound it is playing and define a
- volume for the object.
-
- The object can have different attenuation behavior, emit sound mainly in one direction
- or spherically, and behave as if occluded by some other object.
- */
-
-QQuick3DSpatialAudioSoundSource::QQuick3DSpatialAudioSoundSource()
-{
- m_sound = new QSpatialAudioSoundSource(QQuick3DSpatialAudioEngine::getEngine());
-
- connect(this, &QQuick3DNode::scenePositionChanged, this, &QQuick3DSpatialAudioSoundSource::updatePosition);
- connect(this, &QQuick3DNode::sceneRotationChanged, this, &QQuick3DSpatialAudioSoundSource::updateRotation);
- connect(m_sound, &QSpatialAudioSoundSource::sourceChanged, this, &QQuick3DSpatialAudioSoundSource::sourceChanged);
- connect(m_sound, &QSpatialAudioSoundSource::volumeChanged, this, &QQuick3DSpatialAudioSoundSource::volumeChanged);
- connect(m_sound, &QSpatialAudioSoundSource::distanceModelChanged, this, &QQuick3DSpatialAudioSoundSource::distanceModelChanged);
- connect(m_sound, &QSpatialAudioSoundSource::sizeChanged, this, &QQuick3DSpatialAudioSoundSource::sizeChanged);
- connect(m_sound, &QSpatialAudioSoundSource::distanceCutoffChanged, this, &QQuick3DSpatialAudioSoundSource::distanceCutoffChanged);
- connect(m_sound, &QSpatialAudioSoundSource::manualAttenuationChanged, this, &QQuick3DSpatialAudioSoundSource::manualAttenuationChanged);
- connect(m_sound, &QSpatialAudioSoundSource::occlusionIntensityChanged, this, &QQuick3DSpatialAudioSoundSource::occlusionIntensityChanged);
- connect(m_sound, &QSpatialAudioSoundSource::directivityChanged, this, &QQuick3DSpatialAudioSoundSource::directivityChanged);
- connect(m_sound, &QSpatialAudioSoundSource::directivityOrderChanged, this, &QQuick3DSpatialAudioSoundSource::directivityOrderChanged);
- connect(m_sound, &QSpatialAudioSoundSource::nearFieldGainChanged, this, &QQuick3DSpatialAudioSoundSource::nearFieldGainChanged);
- connect(m_sound, &QSpatialAudioSoundSource::loopsChanged, this, &QQuick3DSpatialAudioSoundSource::loopsChanged);
- connect(m_sound, &QSpatialAudioSoundSource::autoPlayChanged, this, &QQuick3DSpatialAudioSoundSource::autoPlayChanged);
-}
-
-QQuick3DSpatialAudioSoundSource::~QQuick3DSpatialAudioSoundSource()
-{
- delete m_sound;
-}
-
-/*!
- \qmlproperty url QSpatialAudioSoundSource::source
-
- The source file for the sound to be played.
- */
-QUrl QQuick3DSpatialAudioSoundSource::source() const
-{
- return m_sound->source();
-}
-
-void QQuick3DSpatialAudioSoundSource::setSource(QUrl source)
-{
- QUrl url = QUrl::fromLocalFile(QDir::currentPath() + u"/");
- url = url.resolved(source);
-
- m_sound->setSource(url);
-}
-
-/*!
- \qmlproperty float QSpatialAudioSoundSource::volume
-
- Defines an overall volume for this sound source.
- */
-void QQuick3DSpatialAudioSoundSource::setVolume(float volume)
-{
- m_sound->setVolume(volume);
-}
-
-float QQuick3DSpatialAudioSoundSource::volume() const
-{
- return m_sound->volume();
-}
-
-/*!
- \qmlproperty enumeration SpatialAudioSoundSource::distanceModel
-
- Defines how the volume of the sound scales with distance to the listener.
- The volume starts scaling down
- from \l size to \l distanceCutoff. The volume is constant for distances smaller
- than size and zero for distances larger than the cutoff distance.
-
- \table
- \header \li Property value
- \li Description
- \row \li Logarithmic
- \li Volume decreases logarithmically with distance.
- \row \li Linear
- \li Volume decreases linearly with distance.
- \row \li ManualAttenutation
- \li Attenuation is defined manually using the \l manualAttenuation property.
- \endtable
- */
-void QQuick3DSpatialAudioSoundSource::setDistanceModel(DistanceModel model)
-{
- m_sound->setDistanceModel(QSpatialAudioSoundSource::DistanceModel(model));
-}
-
-QQuick3DSpatialAudioSoundSource::DistanceModel QQuick3DSpatialAudioSoundSource::distanceModel() const
-{
- return DistanceModel(m_sound->distanceModel());
-}
-
-/*!
- \qmlproperty float SpatialAudioSoundSource::size
-
- Defines the size of the sound source. If the listener is closer to the sound
- object than the size, volume will stay constant. The size is also used to for
- occlusion calculations, where large sources can be partially occluded by a wall.
- */
-void QQuick3DSpatialAudioSoundSource::setSize(float min)
-{
- m_sound->setSize(min);
-}
-
-float QQuick3DSpatialAudioSoundSource::size() const
-{
- return m_sound->size();
-}
-
-/*!
- \qmlproperty float SpatialAudioSoundSource::distanceCutoff
-
- Defines a distance beyond which sound coming from the source will cutoff.
- If the listener is further away from the sound object than the cutoff
- distance it won't be audible anymore.
- */
-void QQuick3DSpatialAudioSoundSource::setDistanceCutoff(float max)
-{
- m_sound->setDistanceCutoff(max);
-}
-
-float QQuick3DSpatialAudioSoundSource::distanceCutoff() const
-{
- return m_sound->distanceCutoff();
-}
-
-/*!
- \qmlproperty float SpatialAudioSoundSource::manualAttenuation
-
- Defines a manual attenuation factor if \l distanceModel is set to
- SpatialAudioSoundSource.ManualAttenutation.
- */
-void QQuick3DSpatialAudioSoundSource::setManualAttenuation(float attenuation)
-{
- m_sound->setManualAttenuation(attenuation);
-}
-
-float QQuick3DSpatialAudioSoundSource::manualAttenuation() const
-{
- return m_sound->manualAttenuation();
-}
-
-/*!
- \qmlproperty float SpatialAudioSoundSource::occlusionIntensity
-
- Defines how much the object is occluded. 0 implies the object is
- not occluded at all, while a large number implies a large occlusion.
-
- The default is 0.
- */
-void QQuick3DSpatialAudioSoundSource::setOcclusionIntensity(float occlusion)
-{
- m_sound->setOcclusionIntensity(occlusion);
-}
-
-float QQuick3DSpatialAudioSoundSource::occlusionIntensity() const
-{
- return m_sound->occlusionIntensity();
-}
-
-/*!
- \qmlproperty float SpatialAudioSoundSource::directivity
-
- Defines the directivity of the sound source. A value of 0 implies that the sound is
- emitted equally in all directions, while a value of 1 implies that the source mainly
- emits sound in the forward direction.
-
- Valid values are between 0 and 1, the default is 0.
- */
-void QQuick3DSpatialAudioSoundSource::setDirectivity(float alpha)
-{
- m_sound->setDirectivity(alpha);
-}
-
-float QQuick3DSpatialAudioSoundSource::directivity() const
-{
- return m_sound->directivity();
-}
-
-/*!
- \qmlproperty float SpatialAudioSoundSource::directivityOrder
-
- Defines the order of the directivity of the sound source. A higher order
- implies a sharper localization of the sound cone.
-
- The minimum value and default for this property is 1.
- */
-void QQuick3DSpatialAudioSoundSource::setDirectivityOrder(float alpha)
-{
- m_sound->setDirectivityOrder(alpha);
-}
-
-float QQuick3DSpatialAudioSoundSource::directivityOrder() const
-{
- return m_sound->directivityOrder();
-}
-
-/*!
- \qmlproperty float SpatialAudioSoundSource::nearFieldGain
-
- Defines the near field gain for the sound source. Valid values are between 0 and 1.
- A near field gain of 1 will raise the volume of the sound signal by approx 20 dB for
- distances very close to the listener.
- */
-void QQuick3DSpatialAudioSoundSource::setNearFieldGain(float gain)
-{
- m_sound->setNearFieldGain(gain);
-}
-
-float QQuick3DSpatialAudioSoundSource::nearFieldGain() const
-{
- return m_sound->nearFieldGain();
-}
-
-void QQuick3DSpatialAudioSoundSource::updatePosition()
-{
- m_sound->setPosition(scenePosition());
-}
-
-void QQuick3DSpatialAudioSoundSource::updateRotation()
-{
- m_sound->setRotation(sceneRotation());
-}
-
-/*!
- \qmlproperty int QSpatialAudioSoundSource::loops
-
- Determines how often the sound is played before the player stops.
- Set to SpatialAudioSoundSource::Infinite to loop the current sound forever.
-
- The default value is \c 1.
- */
-int QQuick3DSpatialAudioSoundSource::loops() const
-{
- return m_sound->loops();
-}
-
-void QQuick3DSpatialAudioSoundSource::setLoops(int loops)
-{
- m_sound->setLoops(loops);
-}
-
-/*!
- \qmlproperty bool SpatialAudioSoundSource::autoPlay
-
- Determines whether the sound should automatically start playing when a source
- gets specified.
-
- The default value is \c true.
- */
-bool QQuick3DSpatialAudioSoundSource::autoPlay() const
-{
- return m_sound->autoPlay();
-}
-
-void QQuick3DSpatialAudioSoundSource::setAutoPlay(bool autoPlay)
-{
- m_sound->setAutoPlay(autoPlay);
-}
-
-/*!
- \qmlmethod SpatialAudioSoundSource::play()
-
- Starts playing back the sound. Does nothing if the sound is already playing.
- */
-void QQuick3DSpatialAudioSoundSource::play()
-{
- m_sound->play();
-}
-
-/*!
- \qmlmethod SpatialAudioSoundSource::pause()
-
- Pauses sound playback at the current position. Calling play() will continue playback.
- */
-void QQuick3DSpatialAudioSoundSource::pause()
-{
- m_sound->pause();
-}
-
-/*!
- \qmlmethod SpatialAudioSoundSource::stop()
-
- Stops sound playback and resets the current position and loop count to 0. Calling play() will
- begin playback at the beginning of the sound file.
- */
-void QQuick3DSpatialAudioSoundSource::stop()
-{
- m_sound->stop();
-}
-
-QT_END_NAMESPACE
diff --git a/src/multimediaquick3d/qquick3dspatialaudiostereosource.cpp b/src/multimediaquick3d/qquick3dspatialaudiostereosource.cpp
deleted file mode 100644
index 55efde12a..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudiostereosource.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "qquick3dspatialaudiostereosource_p.h"
-#include "qquick3dspatialaudioengine_p.h"
-#include "qspatialaudiostereosource.h"
-#include <QAudioFormat>
-#include <qdir.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \qmltype SpatialAudioStereoSource
- \inqmlmodule QtQuick3D.SpatialAudio
- \ingroup quick3d_spatialaudio
-
- \brief A stereo overlay sound.
-
- A SpatialAudioStereoSource represents a position and orientation independent sound.
- It's commonly used for background sounds (e.g. music) that is supposed to be independent
- of the listeners position and orientation.
- */
-
-QQuick3DSpatialAudioStereoSource::QQuick3DSpatialAudioStereoSource()
-{
- m_sound = new QSpatialAudioStereoSource(QQuick3DSpatialAudioEngine::getEngine());
-
- connect(m_sound, &QSpatialAudioStereoSource::sourceChanged, this, &QQuick3DSpatialAudioStereoSource::sourceChanged);
- connect(m_sound, &QSpatialAudioStereoSource::volumeChanged, this, &QQuick3DSpatialAudioStereoSource::volumeChanged);
- connect(m_sound, &QSpatialAudioStereoSource::loopsChanged, this, &QQuick3DSpatialAudioStereoSource::loopsChanged);
- connect(m_sound, &QSpatialAudioStereoSource::autoPlayChanged, this, &QQuick3DSpatialAudioStereoSource::autoPlayChanged);
-}
-
-QQuick3DSpatialAudioStereoSource::~QQuick3DSpatialAudioStereoSource()
-{
- delete m_sound;
-}
-
-/*!
- \qmlproperty url QSpatialAudioStereoSource::source
-
- The source file for the sound to be played.
- */
-QUrl QQuick3DSpatialAudioStereoSource::source() const
-{
- return m_sound->source();
-}
-
-void QQuick3DSpatialAudioStereoSource::setSource(QUrl source)
-{
- QUrl url = QUrl::fromLocalFile(QDir::currentPath() + u"/");
- url = url.resolved(source);
-
- m_sound->setSource(url);
-}
-
-/*!
- \qmlproperty float QSpatialAudioStereoSource::volume
-
- Defines an overall volume for this sound source.
- */
-void QQuick3DSpatialAudioStereoSource::setVolume(float volume)
-{
- m_sound->setVolume(volume);
-}
-
-float QQuick3DSpatialAudioStereoSource::volume() const
-{
- return m_sound->volume();
-}
-
-/*!
- \qmlproperty int QSpatialAudioStereoSource::loops
-
- Determines how often the sound is played before the player stops.
- Set to SpatialAudioSoundSource::Infinite to loop the current sound forever.
-
- The default value is \c 1.
- */
-int QQuick3DSpatialAudioStereoSource::loops() const
-{
- return m_sound->loops();
-}
-
-void QQuick3DSpatialAudioStereoSource::setLoops(int loops)
-{
- m_sound->setLoops(loops);
-}
-
-/*!
- \qmlproperty bool SpatialAudioStereoSource::autoPlay
-
- Determines whether the sound should automatically start playing when a source
- gets specified.
-
- The default value is \c true.
- */
-bool QQuick3DSpatialAudioStereoSource::autoPlay() const
-{
- return m_sound->autoPlay();
-}
-
-void QQuick3DSpatialAudioStereoSource::setAutoPlay(bool autoPlay)
-{
- m_sound->setAutoPlay(autoPlay);
-}
-
-/*!
- \qmlmethod SpatialAudioStereoSource::play()
-
- Starts playing back the sound. Does nothing if the sound is already playing.
- */
-void QQuick3DSpatialAudioStereoSource::play()
-{
- m_sound->play();
-}
-
-/*!
- \qmlmethod SpatialAudioStereoSource::pause()
-
- Pauses sound playback at the current position. Calling play() will continue playback.
- */
-void QQuick3DSpatialAudioStereoSource::pause()
-{
- m_sound->pause();
-}
-
-/*!
- \qmlmethod SpatialAudioStereoSource::stop()
-
- Stops sound playback and resets the current position and loop count to 0. Calling play() will
- begin playback at the beginning of the sound file.
- */
-void QQuick3DSpatialAudioStereoSource::stop()
-{
- m_sound->stop();
-}
-
-QT_END_NAMESPACE
diff --git a/src/multimediaquick3d/qquick3dspatialaudiostereosource_p.h b/src/multimediaquick3d/qquick3dspatialaudiostereosource_p.h
deleted file mode 100644
index 25df87732..000000000
--- a/src/multimediaquick3d/qquick3dspatialaudiostereosource_p.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QQUICK3DSPATIALAUDIOSTEREOSOUND_H
-#define QQUICK3DSPATIALAUDIOSTEREOSOUND_H
-
-#include <private/qquick3dnode_p.h>
-#include <QUrl>
-#include <qvector3d.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioStereoSource;
-
-class QQuick3DSpatialAudioStereoSource : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
- Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)
- Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged)
- Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged)
- QML_NAMED_ELEMENT(SpatialAudioStereoSource)
-
-public:
- QQuick3DSpatialAudioStereoSource();
- ~QQuick3DSpatialAudioStereoSource();
-
- void setSource(QUrl source);
- QUrl source() const;
-
- void setVolume(float volume);
- float volume() const;
-
- enum Loops
- {
- Infinite = -1,
- Once = 1
- };
- Q_ENUM(Loops)
-
- int loops() const;
- void setLoops(int loops);
-
- bool autoPlay() const;
- void setAutoPlay(bool autoPlay);
-
-public Q_SLOTS:
- void play();
- void pause();
- void stop();
-
-Q_SIGNALS:
- void sourceChanged();
- void volumeChanged();
- void loopsChanged();
- void autoPlayChanged();
-
-private:
- QSpatialAudioStereoSource *m_sound = nullptr;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimediaquick3d/qtquick3dsoundglobal_p.h b/src/multimediaquick3d/qtquick3dsoundglobal_p.h
deleted file mode 100644
index de017039b..000000000
--- a/src/multimediaquick3d/qtquick3dsoundglobal_p.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#ifndef QMULTIMEDIAQUICKDEFS_P_H
-#define QMULTIMEDIAQUICKDEFS_P_H
-
-#include <QtCore/qglobal.h>
-#include <QtMultimediaQuick/qtmultimediaquickexports.h>
-
-QT_BEGIN_NAMESPACE
-
-void Q_MULTIMEDIAQUICK_EXPORT qml_register_types_QtQuick3D_SpatialAudio();
-
-QT_END_NAMESPACE
-
-#endif // QMULTIMEDIAQUICKDEFS_P_H
-
diff --git a/src/multimediaquick3d/qtquick3dsoundtypes_p.h b/src/multimediaquick3d/qtquick3dsoundtypes_p.h
deleted file mode 100644
index 7d5a75109..000000000
--- a/src/multimediaquick3d/qtquick3dsoundtypes_p.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QTMULTIMEDIAQUICKTYPES_H
-#define QTMULTIMEDIAQUICKTYPES_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtQml/qqml.h>
-#include <private/qtquick3dsoundglobal_p.h>
-
-QT_BEGIN_NAMESPACE
-
-// Nothing for now
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/multimediaquick3d/quick3dspatialaudio_plugin.cpp b/src/multimediaquick3d/quick3dspatialaudio_plugin.cpp
deleted file mode 100644
index 0a04f78bf..000000000
--- a/src/multimediaquick3d/quick3dspatialaudio_plugin.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtQml/qqmlextensionplugin.h>
-#include <QtQml/qqml.h>
-#include <QtQml/qqmlengine.h>
-#include <QtQml/qqmlcomponent.h>
-#include "qtquick3dsoundglobal_p.h"
-#include "qquick3dspatialaudiolistener_p.h"
-#include "qquick3dspatialaudiosoundsource_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class QSpatialAudioQuickModule : public QQmlEngineExtensionPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
-
-public:
- QSpatialAudioQuickModule(QObject *parent = nullptr)
- : QQmlEngineExtensionPlugin(parent)
- {
- volatile auto registration = qml_register_types_QtQuick3D_SpatialAudio;
- Q_UNUSED(registration);
- }
-
- void initializeEngine(QQmlEngine *engine, const char *uri) override
- {
- Q_UNUSED(engine);
- Q_UNUSED(uri);
- }
-};
-
-QT_END_NAMESPACE
-
-#include "quick3dspatialaudio_plugin.moc"
-
diff --git a/src/multimediawidgets/CMakeLists.txt b/src/multimediawidgets/CMakeLists.txt
index f605c3a11..8425d3bac 100644
--- a/src/multimediawidgets/CMakeLists.txt
+++ b/src/multimediawidgets/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from multimediawidgets.pro.
#####################################################################
diff --git a/src/multimediawidgets/doc/snippets/doc_src_qtmultimediawidgets.cpp b/src/multimediawidgets/doc/snippets/doc_src_qtmultimediawidgets.cpp
deleted file mode 100644
index 0598cfab9..000000000
--- a/src/multimediawidgets/doc/snippets/doc_src_qtmultimediawidgets.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//! [0]
-#include <QtMultimediaWidgets>
-//! [0]
diff --git a/src/multimediawidgets/doc/snippets/multimedia-snippets/camera.cpp b/src/multimediawidgets/doc/snippets/multimedia-snippets/camera.cpp
index 20c6ad7ec..a92c51e69 100644
--- a/src/multimediawidgets/doc/snippets/multimedia-snippets/camera.cpp
+++ b/src/multimediawidgets/doc/snippets/multimedia-snippets/camera.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// Camera snippets
// Extracted from src/multimedia/doc/snippets/multimedia-snippets/camera.cpp
diff --git a/src/multimediawidgets/doc/snippets/multimedia-snippets/video.cpp b/src/multimediawidgets/doc/snippets/multimedia-snippets/video.cpp
index 434d7b8c6..609aa121f 100644
--- a/src/multimediawidgets/doc/snippets/multimedia-snippets/video.cpp
+++ b/src/multimediawidgets/doc/snippets/multimedia-snippets/video.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// Video related snippets
// Extracted from src/multimedia/doc/snippets/multimedia-snippets/video.cpp
diff --git a/src/multimediawidgets/doc/src/qtmultimediawidgets-index.qdoc b/src/multimediawidgets/doc/src/qtmultimediawidgets-index.qdoc
index 9e9d78233..18e6d8deb 100644
--- a/src/multimediawidgets/doc/src/qtmultimediawidgets-index.qdoc
+++ b/src/multimediawidgets/doc/src/qtmultimediawidgets-index.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtmultimediawidgets-index.html
@@ -36,12 +12,6 @@ controls. The classes expand the capabilities of the \l{Qt Multimedia} and
\section1 Getting Started
-To enable Qt Multimedia Widgets in a project, add this directive into the
-C++ files:
-\code
-#include <QtMultimediaWidgets>
-\endcode
-
To link against the C++ libraries, add the following to your project's
\c CMakeLists.txt file. Substitute \c my_project with the name of your project.
diff --git a/src/multimediawidgets/doc/src/qtmultimediawidgets.qdoc b/src/multimediawidgets/doc/src/qtmultimediawidgets.qdoc
index a9699f4d5..c67efcb23 100644
--- a/src/multimediawidgets/doc/src/qtmultimediawidgets.qdoc
+++ b/src/multimediawidgets/doc/src/qtmultimediawidgets.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\module QtMultimediaWidgets
diff --git a/src/multimediawidgets/qgraphicsvideoitem.cpp b/src/multimediawidgets/qgraphicsvideoitem.cpp
index 19ed28213..c82c2da3c 100644
--- a/src/multimediawidgets/qgraphicsvideoitem.cpp
+++ b/src/multimediawidgets/qgraphicsvideoitem.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgraphicsvideoitem.h"
#include "qvideosink.h"
@@ -235,12 +199,6 @@ QSizeF QGraphicsVideoItem::nativeSize() const
}
/*!
- \fn QGraphicsVideoItem::nativeSizeChanged(const QSizeF &size)
-
- Signals that the native \a size of the video has changed.
-*/
-
-/*!
\reimp
*/
QRectF QGraphicsVideoItem::boundingRect() const
@@ -262,6 +220,22 @@ void QGraphicsVideoItem::paint(QPainter *painter, const QStyleOptionGraphicsItem
}
/*!
+ \fn int QGraphicsVideoItem::type() const
+ \reimp
+
+ Returns an int representing the type of the video item.
+*/
+/*!
+ \variable QGraphicsVideoItem::d_ptr
+ \internal
+*/
+/*!
+ \enum QGraphicsVideoItem::anonymous
+ \internal
+
+ \omitvalue Type
+*/
+/*!
\reimp
\internal
@@ -282,3 +256,5 @@ void QGraphicsVideoItem::timerEvent(QTimerEvent *event)
QT_END_NAMESPACE
#include "moc_qgraphicsvideoitem.cpp"
+
+
diff --git a/src/multimediawidgets/qgraphicsvideoitem.h b/src/multimediawidgets/qgraphicsvideoitem.h
index 96c67c876..ce9e863a2 100644
--- a/src/multimediawidgets/qgraphicsvideoitem.h
+++ b/src/multimediawidgets/qgraphicsvideoitem.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGRAPHICSVIDEOITEM_H
#define QGRAPHICSVIDEOITEM_H
diff --git a/src/multimediawidgets/qtmultimediawidgetsglobal.h b/src/multimediawidgets/qtmultimediawidgetsglobal.h
index b6f0df9a6..b7091ea40 100644
--- a/src/multimediawidgets/qtmultimediawidgetsglobal.h
+++ b/src/multimediawidgets/qtmultimediawidgetsglobal.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtMultimediaWidgets module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTMULTIMEDIAWIDGETSGLOBAL_H
#define QTMULTIMEDIAWIDGETSGLOBAL_H
diff --git a/src/multimediawidgets/qvideowidget.cpp b/src/multimediawidgets/qvideowidget.cpp
index 511489dd9..1b8c41980 100644
--- a/src/multimediawidgets/qvideowidget.cpp
+++ b/src/multimediawidgets/qvideowidget.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <private/qtmultimediaglobal_p.h>
#include "qvideowidget_p.h"
@@ -85,7 +49,10 @@ QT_BEGIN_NAMESPACE
\sa QCamera, QMediaPlayer, QGraphicsVideoItem
*/
-
+/*!
+ \variable QVideoWidget::d_ptr
+ \internal
+*/
/*!
Constructs a new video widget.
@@ -114,6 +81,9 @@ QVideoWidget::~QVideoWidget()
delete d_ptr;
}
+/*!
+ Returns the QVideoSink instance.
+*/
QVideoSink *QVideoWidget::videoSink() const
{
return d_ptr->videoWindow->videoSink();
@@ -142,6 +112,8 @@ void QVideoWidget::setAspectRatioMode(Qt::AspectRatioMode mode)
void QVideoWidget::setFullScreen(bool fullScreen)
{
Q_D(QVideoWidget);
+ if (isFullScreen() == fullScreen)
+ return;
Qt::WindowFlags flags = windowFlags();
@@ -167,18 +139,9 @@ void QVideoWidget::setFullScreen(bool fullScreen)
move(d_ptr->nonFullscreenPos);
d_ptr->nonFullscreenPos = {};
}
- d->wasFullScreen = fullScreen;
}
/*!
- \fn QVideoWidget::fullScreenChanged(bool fullScreen)
-
- Signals that the \a fullScreen mode of a video widget has changed.
-
- \sa isFullScreen()
-*/
-
-/*!
Returns the size hint for the current back end,
if there is one, or else the size hint from QWidget.
*/
@@ -201,12 +164,10 @@ bool QVideoWidget::event(QEvent *event)
Q_D(QVideoWidget);
if (event->type() == QEvent::WindowStateChange) {
- if (windowState() & Qt::WindowFullScreen) {
- if (!d->wasFullScreen)
- emit fullScreenChanged(d->wasFullScreen = true);
- } else {
- if (d->wasFullScreen)
- emit fullScreenChanged(d->wasFullScreen = false);
+ bool fullScreen = bool(windowState() & Qt::WindowFullScreen);
+ if (fullScreen != d->wasFullScreen) {
+ emit fullScreenChanged(fullScreen);
+ d->wasFullScreen = fullScreen;
}
}
diff --git a/src/multimediawidgets/qvideowidget.h b/src/multimediawidgets/qvideowidget.h
index b5d9144f5..2cd83ff47 100644
--- a/src/multimediawidgets/qvideowidget.h
+++ b/src/multimediawidgets/qvideowidget.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOWIDGET_H
#define QVIDEOWIDGET_H
diff --git a/src/multimediawidgets/qvideowidget_p.h b/src/multimediawidgets/qvideowidget_p.h
index 0a651aca5..838bcfe7a 100644
--- a/src/multimediawidgets/qvideowidget_p.h
+++ b/src/multimediawidgets/qvideowidget_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVIDEOWIDGET_P_H
#define QVIDEOWIDGET_P_H
diff --git a/src/plugins/multimedia/CMakeLists.txt b/src/plugins/multimedia/CMakeLists.txt
index 270a5ec6d..5bc39c1f8 100644
--- a/src/plugins/multimedia/CMakeLists.txt
+++ b/src/plugins/multimedia/CMakeLists.txt
@@ -1,18 +1,24 @@
-if (QT_FEATURE_ffmpeg)
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(QT_FEATURE_ffmpeg)
add_subdirectory(ffmpeg)
-endif ()
-if (QT_FEATURE_gstreamer)
+endif()
+if(QT_FEATURE_gstreamer)
add_subdirectory(gstreamer)
-endif ()
-if (ANDROID)
+endif()
+if(ANDROID)
add_subdirectory(android)
-endif ()
-if (APPLE AND NOT WATCHOS)
+endif()
+if(WASM)
+ add_subdirectory(wasm)
+endif()
+if(APPLE AND NOT WATCHOS)
add_subdirectory(darwin)
-endif ()
-if (QT_FEATURE_wmf)
+endif()
+if(QT_FEATURE_wmf)
add_subdirectory(windows)
-endif ()
-if (QT_FEATURE_mmrenderer)
+endif()
+if(QT_FEATURE_mmrenderer)
add_subdirectory(qnx)
-endif ()
+endif()
diff --git a/src/plugins/multimedia/android/CMakeLists.txt b/src/plugins/multimedia/android/CMakeLists.txt
index ae3678be2..31a94ff4f 100644
--- a/src/plugins/multimedia/android/CMakeLists.txt
+++ b/src/plugins/multimedia/android/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_internal_add_plugin(QAndroidMediaPlugin
OUTPUT_NAME androidmediaplugin
PLUGIN_TYPE multimedia
@@ -26,6 +29,12 @@ qt_internal_add_plugin(QAndroidMediaPlugin
wrappers/jni/androidmultimediautils.cpp wrappers/jni/androidmultimediautils_p.h
wrappers/jni/androidsurfacetexture.cpp wrappers/jni/androidsurfacetexture_p.h
wrappers/jni/androidsurfaceview.cpp wrappers/jni/androidsurfaceview_p.h
+ NO_UNITY_BUILD_SOURCES
+ # Resolves two problems:
+ # - Collision of `rwLock` with wrappers/jni/androidmediaplayer.cpp
+ # - and redefinition of `notifyFrameAvailable` with different signature
+ # with wrappers/jni/androidsurfacetexture.cpp
+ wrappers/jni/androidcamera.cpp
INCLUDE_DIRECTORIES
audio
common
@@ -44,8 +53,7 @@ set_property(TARGET QAndroidMediaPlugin APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_D
jar/Qt${QtMultimedia_VERSION_MAJOR}AndroidMultimedia.jar:org.qtproject.qt.android.multimedia.QtMultimediaUtils
)
set_property(TARGET QAndroidMediaPlugin APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES
- plugins/multimedia/libplugins_multimedia_androidmediaplugin.so
- lib/libQt6MultimediaQuick.so:Qt6Quick
+ ${INSTALL_PLUGINSDIR}/multimedia/libplugins_multimedia_androidmediaplugin.so
)
set_property(TARGET QAndroidMediaPlugin APPEND PROPERTY QT_ANDROID_PERMISSIONS
android.permission.CAMERA android.permission.RECORD_AUDIO
diff --git a/src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp b/src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp
index 7239d7292..d200a72b5 100644
--- a/src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp
+++ b/src/plugins/multimedia/android/audio/qandroidaudiodecoder.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidaudiodecoder_p.h"
#include <QtCore/qcoreapplication.h>
@@ -55,7 +19,7 @@ QT_BEGIN_NAMESPACE
static const char tempFile[] = "encoded.wav";
constexpr int dequeueTimeout = 5000;
-Q_LOGGING_CATEGORY(adLogger, "QAndroidAudioDecoder")
+static Q_LOGGING_CATEGORY(adLogger, "QAndroidAudioDecoder")
Decoder::Decoder()
: m_format(AMediaFormat_new())
@@ -95,37 +59,32 @@ void Decoder::setSource(const QUrl &source)
"org/qtproject/qt/android/multimedia/QtMultimediaUtils",
"getMimeType",
"(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;",
- QNativeInterface::QAndroidApplication::context(),
+ QNativeInterface::QAndroidApplication::context().object(),
QJniObject::fromString(source.path()).object());
const QString mime = path.isValid() ? path.toString() : "";
if (!mime.isEmpty() && !mime.contains("audio", Qt::CaseInsensitive)) {
- emit error(QAudioDecoder::FormatError,
- tr("Cannot set source, invalid mime type for the provided source."));
+ m_formatError = tr("Cannot set source, invalid mime type for the source provided.");
return;
}
if (!m_extractor)
m_extractor = AMediaExtractor_new();
- int fd = -1;
- if (source.path().contains(QLatin1String("content"))) {
- fd = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative",
- "openFdForContentUrl",
- "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I",
- QNativeInterface::QAndroidApplication::context(),
- QJniObject::fromString(source.path()).object(),
- QJniObject::fromString(QLatin1String("r")).object());
- } else {
- fd = open(source.path().toStdString().c_str(), O_RDONLY);
- }
+ QFile file(source.path());
+ if (!file.open(QFile::ReadOnly)) {
+ emit error(QAudioDecoder::ResourceError, tr("Cannot open the file"));
+ return;
+ }
+
+ const int fd = file.handle();
if (fd < 0) {
- emit error(QAudioDecoder::ResourceError, tr("Invalid fileDescriptor for source."));
- return;
- }
- const int size = QFile(source.toString()).size();
+ emit error(QAudioDecoder::ResourceError, tr("Invalid fileDescriptor for source."));
+ return;
+ }
+ const int size = file.size();
media_status_t status = AMediaExtractor_setDataSourceFd(m_extractor, fd, 0,
size > 0 ? size : LONG_MAX);
close(fd);
@@ -135,7 +94,7 @@ void Decoder::setSource(const QUrl &source)
AMediaExtractor_delete(m_extractor);
m_extractor = nullptr;
}
- emit error(QAudioDecoder::ResourceError, tr("Setting source for Audio Decoder failed."));
+ m_formatError = tr("Setting source for Audio Decoder failed.");
}
}
@@ -176,6 +135,11 @@ void Decoder::createDecoder()
void Decoder::doDecode()
{
+ if (!m_formatError.isEmpty()) {
+ emit error(QAudioDecoder::FormatError, m_formatError);
+ return;
+ }
+
if (!m_extractor) {
emit error(QAudioDecoder::ResourceError, tr("Cannot decode, source not set."));
return;
@@ -326,6 +290,12 @@ void QAndroidAudioDecoder::start()
m_position = -1;
+ if (m_device && (!m_device->isOpen() || !m_device->isReadable())) {
+ emit error(QAudioDecoder::ResourceError,
+ QString::fromUtf8("Unable to read from the specified device"));
+ return;
+ }
+
if (!m_threadDecoder) {
m_threadDecoder = new QThread(this);
m_decoder->moveToThread(m_threadDecoder);
@@ -435,11 +405,11 @@ bool QAndroidAudioDecoder::createTempFile()
bool success = file.open(QIODevice::QIODevice::ReadWrite);
if (!success)
- emit error(QAudioDecoder::ResourceError, tr("Error while opening tmp file"));
+ emit error(QAudioDecoder::ResourceError, tr("Error opening temporary file: %1").arg(file.errorString()));
success &= (file.write(m_deviceBuffer) == m_deviceBuffer.size());
if (!success)
- emit error(QAudioDecoder::ResourceError, tr("Error while writing data to tmp file"));
+ emit error(QAudioDecoder::ResourceError, tr("Error while writing data to temporary file"));
file.close();
m_deviceBuffer.clear();
@@ -463,3 +433,5 @@ void QAndroidAudioDecoder::readDevice() {
}
QT_END_NAMESPACE
+
+#include "moc_qandroidaudiodecoder_p.cpp"
diff --git a/src/plugins/multimedia/android/audio/qandroidaudiodecoder_p.h b/src/plugins/multimedia/android/audio/qandroidaudiodecoder_p.h
index a1795e39b..65a0f1855 100644
--- a/src/plugins/multimedia/android/audio/qandroidaudiodecoder_p.h
+++ b/src/plugins/multimedia/android/audio/qandroidaudiodecoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDAUDIODECODER_P_H
#define QANDROIDAUDIODECODER_P_H
@@ -90,6 +54,7 @@ private:
AMediaFormat *m_format = nullptr;
QAudioFormat m_outputFormat;
+ QString m_formatError;
bool m_inputEOS;
};
@@ -142,7 +107,6 @@ private:
qint64 m_position = -1;
qint64 m_duration = -1;
- long long m_presentationTimeUs = 0;
QByteArray m_deviceBuffer;
diff --git a/src/plugins/multimedia/android/common/qandroidaudioinput.cpp b/src/plugins/multimedia/android/common/qandroidaudioinput.cpp
index 71be7869d..a1eb9258b 100644
--- a/src/plugins/multimedia/android/common/qandroidaudioinput.cpp
+++ b/src/plugins/multimedia/android/common/qandroidaudioinput.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidaudioinput_p.h"
@@ -79,3 +43,5 @@ bool QAndroidAudioInput::isMuted() const
}
QT_END_NAMESPACE
+
+#include "moc_qandroidaudioinput_p.cpp"
diff --git a/src/plugins/multimedia/android/common/qandroidaudioinput_p.h b/src/plugins/multimedia/android/common/qandroidaudioinput_p.h
index 100b13aab..ef59da8ec 100644
--- a/src/plugins/multimedia/android/common/qandroidaudioinput_p.h
+++ b/src/plugins/multimedia/android/common/qandroidaudioinput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDAUDIOINPUT_H
#define QANDROIDAUDIOINPUT_H
diff --git a/src/plugins/multimedia/android/common/qandroidaudiooutput_p.h b/src/plugins/multimedia/android/common/qandroidaudiooutput_p.h
index e17f158fc..d5d25b458 100644
--- a/src/plugins/multimedia/android/common/qandroidaudiooutput_p.h
+++ b/src/plugins/multimedia/android/common/qandroidaudiooutput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDAUDIOOUTPUT_H
#define QANDROIDAUDIOOUTPUT_H
diff --git a/src/plugins/multimedia/android/common/qandroidglobal_p.h b/src/plugins/multimedia/android/common/qandroidglobal_p.h
index 45bd22ffb..1022fa061 100644
--- a/src/plugins/multimedia/android/common/qandroidglobal_p.h
+++ b/src/plugins/multimedia/android/common/qandroidglobal_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDGLOBAL_H
#define QANDROIDGLOBAL_H
diff --git a/src/plugins/multimedia/android/common/qandroidmultimediautils.cpp b/src/plugins/multimedia/android/common/qandroidmultimediautils.cpp
index e333f9520..6e4b95fe9 100644
--- a/src/plugins/multimedia/android/common/qandroidmultimediautils.cpp
+++ b/src/plugins/multimedia/android/common/qandroidmultimediautils.cpp
@@ -1,47 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidmultimediautils_p.h"
#include "qandroidglobal_p.h"
#include <qlist.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qpermissions.h>
#include <QtCore/private/qandroidextras_p.h>
QT_BEGIN_NAMESPACE
@@ -124,43 +89,27 @@ static bool androidRequestPermission(const QString &permission)
return true;
}
-static bool androidCheckPermission(const QString &permission)
+static bool androidCheckPermission(const QPermission &permission)
{
- if (QNativeInterface::QAndroidApplication::sdkVersion() < 23)
- return true;
-
- // Permission already granted?
- return (QtAndroidPrivate::checkPermission(permission).result() == QtAndroidPrivate::Authorized);
+ return qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
}
bool qt_androidCheckCameraPermission()
{
- return androidCheckPermission(QStringLiteral("android.permission.CAMERA"));
+ const QCameraPermission permission;
+ const auto granted = androidCheckPermission(permission);
+ if (!granted)
+ qCDebug(qtAndroidMediaPlugin, "Camera permission not granted!");
+ return granted;
}
bool qt_androidCheckMicrophonePermission()
{
- return androidCheckPermission(QStringLiteral("android.permission.RECORD_AUDIO"));
-}
-
-bool qt_androidRequestCameraPermission()
-{
- if (!androidRequestPermission(QStringLiteral("android.permission.CAMERA"))) {
- qCDebug(qtAndroidMediaPlugin, "Camera permission denied by user!");
- return false;
- }
-
- return true;
-}
-
-bool qt_androidRequestRecordingPermission()
-{
- if (!androidRequestPermission(QStringLiteral("android.permission.RECORD_AUDIO"))) {
- qCDebug(qtAndroidMediaPlugin, "Microphone permission denied by user!");
- return false;
- }
-
- return true;
+ const QMicrophonePermission permission;
+ const auto granted = androidCheckPermission(permission);
+ if (!granted)
+ qCDebug(qtAndroidMediaPlugin, "Microphone permission not granted!");
+ return granted;
}
bool qt_androidRequestWriteStoragePermission()
diff --git a/src/plugins/multimedia/android/common/qandroidmultimediautils_p.h b/src/plugins/multimedia/android/common/qandroidmultimediautils_p.h
index 5bc187da3..5fe841e8c 100644
--- a/src/plugins/multimedia/android/common/qandroidmultimediautils_p.h
+++ b/src/plugins/multimedia/android/common/qandroidmultimediautils_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDMULTIMEDIAUTILS_H
#define QANDROIDMULTIMEDIAUTILS_H
@@ -66,8 +30,6 @@ bool qt_sizeLessThan(const QSize &s1, const QSize &s2);
QVideoFrameFormat::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f);
AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrameFormat::PixelFormat f);
-bool qt_androidRequestCameraPermission();
-bool qt_androidRequestRecordingPermission();
bool qt_androidRequestWriteStoragePermission();
bool qt_androidCheckCameraPermission();
diff --git a/src/plugins/multimedia/android/common/qandroidvideooutput.cpp b/src/plugins/multimedia/android/common/qandroidvideooutput.cpp
index a3a4966cc..5a4eebf51 100644
--- a/src/plugins/multimedia/android/common/qandroidvideooutput.cpp
+++ b/src/plugins/multimedia/android/common/qandroidvideooutput.cpp
@@ -1,112 +1,258 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidvideooutput_p.h"
-
#include "androidsurfacetexture_p.h"
+
+#include <rhi/qrhi.h>
+#include <QtGui/private/qopenglextensions_p.h>
+#include <private/qabstractvideobuffer_p.h>
+#include <private/qvideoframeconverter_p.h>
+#include <private/qplatformvideosink_p.h>
#include <qvideosink.h>
-#include "private/qabstractvideobuffer_p.h"
-#include "private/qplatformvideosink_p.h"
-#include <QVideoFrameFormat>
-#include <QFile>
-#include <QtGui/private/qrhigles2_p.h>
-#include <QOpenGLContext>
-#include <QPainter>
-#include <QPainterPath>
-#include <QMutexLocker>
-#include <QTextLayout>
-#include <QTextFormat>
+#include <qopenglcontext.h>
+#include <qopenglfunctions.h>
+#include <qvideoframeformat.h>
+#include <qthread.h>
+#include <qfile.h>
QT_BEGIN_NAMESPACE
-void GraphicsResourceDeleter::deleteResourcesHelper(const QList<QRhiResource *> &res)
+class QAndroidVideoFrameTextures : public QVideoFrameTextures
{
- qDeleteAll(res);
-}
+public:
+ QAndroidVideoFrameTextures(QRhi *rhi, QSize size, quint64 handle)
+ {
+ m_tex.reset(rhi->newTexture(QRhiTexture::RGBA8, size, 1));
+ m_tex->createFrom({quint64(handle), 0});
+ }
+
+ QRhiTexture *texture(uint plane) const override
+ {
+ return plane == 0 ? m_tex.get() : nullptr;
+ }
+
+private:
+ std::unique_ptr<QRhiTexture> m_tex;
+};
+
+// QRhiWithThreadGuard keeps QRhi and QThread (that created it) alive to allow proper cleaning
+class QRhiWithThreadGuard : public QObject {
+ Q_OBJECT
+public:
+ QRhiWithThreadGuard(std::shared_ptr<QRhi> r, std::shared_ptr<AndroidTextureThread> t)
+ : m_guardRhi(std::move(r)), m_thread(std::move(t)) {}
+ ~QRhiWithThreadGuard();
+protected:
+ std::shared_ptr<QRhi> m_guardRhi;
+private:
+ std::shared_ptr<AndroidTextureThread> m_thread;
+};
-void GraphicsResourceDeleter::deleteRhiHelper(QRhi *rhi, QOffscreenSurface *surf)
-{
- delete rhi;
- delete surf;
-}
-void GraphicsResourceDeleter::deleteThisHelper()
+class AndroidTextureVideoBuffer : public QRhiWithThreadGuard, public QAbstractVideoBuffer
{
- delete this;
-}
+public:
+ AndroidTextureVideoBuffer(
+ std::shared_ptr<QRhi> rhi, std::shared_ptr<AndroidTextureThread> thread,
+ std::unique_ptr<QRhiTexture> tex, const QSize &size)
+ : QRhiWithThreadGuard(std::move(rhi), std::move(thread))
+ , QAbstractVideoBuffer(QVideoFrame::RhiTextureHandle, m_guardRhi.get())
+ , m_size(size)
+ , m_tex(std::move(tex))
+ {}
+
+ QVideoFrame::MapMode mapMode() const override { return m_mapMode; }
+
+ MapData map(QVideoFrame::MapMode mode) override;
+
+ void unmap() override
+ {
+ m_image = {};
+ m_mapMode = QVideoFrame::NotMapped;
+ }
+
+ std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *rhi) override
+ {
+ return std::make_unique<QAndroidVideoFrameTextures>(rhi, m_size, m_tex->nativeTexture().object);
+ }
+
+private:
+ QSize m_size;
+ std::unique_ptr<QRhiTexture> m_tex;
+ QImage m_image;
+ QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped;
+};
-bool AndroidTextureVideoBuffer::updateReadbackFrame()
+class ImageFromVideoFrameHelper : public QAbstractVideoBuffer
{
- // Even though the texture was updated in a previous call, we need to re-check
- // that this has not become a stale buffer, e.g., if the output size changed or
- // has since became invalid.
- if (!m_output->m_nativeSize.isValid())
- return false;
-
- // Size changed
- if (m_output->m_nativeSize != m_size)
- return false;
-
- // In the unlikely event that we don't have a valid fbo, but have a valid size,
- // force an update.
- const bool forceUpdate = !m_output->m_readbackTex;
- if (m_textureUpdated && !forceUpdate)
- return true;
-
- // update the video texture (called from the render thread)
- return (m_textureUpdated = m_output->renderAndReadbackFrame());
-}
+public:
+ ImageFromVideoFrameHelper(AndroidTextureVideoBuffer &atvb)
+ : QAbstractVideoBuffer(QVideoFrame::RhiTextureHandle, atvb.rhi())
+ , m_atvb(atvb)
+ {}
+ std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *rhi) override
+ {
+ return m_atvb.mapTextures(rhi);
+ }
+ QVideoFrame::MapMode mapMode() const override { return QVideoFrame::NotMapped; }
+ MapData map(QVideoFrame::MapMode) override { return {}; }
+ void unmap() override {}
+
+private:
+ AndroidTextureVideoBuffer &m_atvb;
+};
QAbstractVideoBuffer::MapData AndroidTextureVideoBuffer::map(QVideoFrame::MapMode mode)
{
- MapData mapData;
- if (m_mapMode == QVideoFrame::NotMapped && mode == QVideoFrame::ReadOnly && updateReadbackFrame()) {
- m_mapMode = mode;
- m_image = m_output->m_readbackImage;
+ QAbstractVideoBuffer::MapData mapData;
+
+ if (m_mapMode == QVideoFrame::NotMapped && mode == QVideoFrame::ReadOnly) {
+ m_mapMode = QVideoFrame::ReadOnly;
+ m_image = qImageFromVideoFrame(QVideoFrame(new ImageFromVideoFrameHelper(*this),
+ QVideoFrameFormat(m_size, QVideoFrameFormat::Format_RGBA8888)));
mapData.nPlanes = 1;
mapData.bytesPerLine[0] = m_image.bytesPerLine();
mapData.size[0] = static_cast<int>(m_image.sizeInBytes());
mapData.data[0] = m_image.bits();
}
+
return mapData;
}
+static const float g_quad[] = {
+ -1.f, -1.f, 0.f, 0.f,
+ -1.f, 1.f, 0.f, 1.f,
+ 1.f, 1.f, 1.f, 1.f,
+ 1.f, -1.f, 1.f, 0.f
+};
+
+class TextureCopy
+{
+ static QShader getShader(const QString &name)
+ {
+ QFile f(name);
+ if (f.open(QIODevice::ReadOnly))
+ return QShader::fromSerialized(f.readAll());
+ return {};
+ }
+
+public:
+ TextureCopy(QRhi *rhi, QRhiTexture *externalTex)
+ : m_rhi(rhi)
+ {
+ m_vertexBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(g_quad)));
+ m_vertexBuffer->create();
+
+ m_uniformBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 64 + 4 + 4));
+ m_uniformBuffer->create();
+
+ m_sampler.reset(m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
+ QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
+ m_sampler->create();
+
+ m_srb.reset(m_rhi->newShaderResourceBindings());
+ m_srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_uniformBuffer.get()),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, externalTex, m_sampler.get())
+ });
+ m_srb->create();
+
+ m_vertexShader = getShader(QStringLiteral(":/qt-project.org/multimedia/shaders/externalsampler.vert.qsb"));
+ Q_ASSERT(m_vertexShader.isValid());
+ m_fragmentShader = getShader(QStringLiteral(":/qt-project.org/multimedia/shaders/externalsampler.frag.qsb"));
+ Q_ASSERT(m_fragmentShader.isValid());
+ }
+
+ std::unique_ptr<QRhiTexture> copyExternalTexture(QSize size, const QMatrix4x4 &externalTexMatrix);
+
+private:
+ QRhi *m_rhi = nullptr;
+ std::unique_ptr<QRhiBuffer> m_vertexBuffer;
+ std::unique_ptr<QRhiBuffer> m_uniformBuffer;
+ std::unique_ptr<QRhiSampler> m_sampler;
+ std::unique_ptr<QRhiShaderResourceBindings> m_srb;
+ QShader m_vertexShader;
+ QShader m_fragmentShader;
+};
+
+static std::unique_ptr<QRhiGraphicsPipeline> newGraphicsPipeline(QRhi *rhi,
+ QRhiShaderResourceBindings *shaderResourceBindings,
+ QRhiRenderPassDescriptor *renderPassDescriptor,
+ QShader vertexShader,
+ QShader fragmentShader)
+{
+ std::unique_ptr<QRhiGraphicsPipeline> gp(rhi->newGraphicsPipeline());
+ gp->setTopology(QRhiGraphicsPipeline::TriangleFan);
+ gp->setShaderStages({
+ { QRhiShaderStage::Vertex, vertexShader },
+ { QRhiShaderStage::Fragment, fragmentShader }
+ });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 4 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
+ });
+ gp->setVertexInputLayout(inputLayout);
+ gp->setShaderResourceBindings(shaderResourceBindings);
+ gp->setRenderPassDescriptor(renderPassDescriptor);
+ gp->create();
+
+ return gp;
+}
+
+std::unique_ptr<QRhiTexture> TextureCopy::copyExternalTexture(QSize size, const QMatrix4x4 &externalTexMatrix)
+{
+ std::unique_ptr<QRhiTexture> tex(m_rhi->newTexture(QRhiTexture::RGBA8, size, 1, QRhiTexture::RenderTarget));
+ if (!tex->create()) {
+ qWarning("Failed to create frame texture");
+ return {};
+ }
+
+ std::unique_ptr<QRhiTextureRenderTarget> renderTarget(m_rhi->newTextureRenderTarget({ { tex.get() } }));
+ std::unique_ptr<QRhiRenderPassDescriptor> renderPassDescriptor(renderTarget->newCompatibleRenderPassDescriptor());
+ renderTarget->setRenderPassDescriptor(renderPassDescriptor.get());
+ renderTarget->create();
+
+ QRhiResourceUpdateBatch *rub = m_rhi->nextResourceUpdateBatch();
+ rub->uploadStaticBuffer(m_vertexBuffer.get(), g_quad);
+
+ QMatrix4x4 identity;
+ char *p = m_uniformBuffer->beginFullDynamicBufferUpdateForCurrentFrame();
+ memcpy(p, identity.constData(), 64);
+ memcpy(p + 64, externalTexMatrix.constData(), 64);
+ float opacity = 1.0f;
+ memcpy(p + 64 + 64, &opacity, 4);
+ m_uniformBuffer->endFullDynamicBufferUpdateForCurrentFrame();
+
+ auto graphicsPipeline = newGraphicsPipeline(m_rhi, m_srb.get(), renderPassDescriptor.get(),
+ m_vertexShader, m_fragmentShader);
+
+ const QRhiCommandBuffer::VertexInput vbufBinding(m_vertexBuffer.get(), 0);
+
+ QRhiCommandBuffer *cb = nullptr;
+ if (m_rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
+ return {};
+
+ cb->beginPass(renderTarget.get(), Qt::transparent, { 1.0f, 0 }, rub);
+ cb->setGraphicsPipeline(graphicsPipeline.get());
+ cb->setViewport({0, 0, float(size.width()), float(size.height())});
+ cb->setShaderResources(m_srb.get());
+ cb->setVertexInput(0, 1, &vbufBinding);
+ cb->draw(4);
+ cb->endPass();
+ m_rhi->endOffscreenFrame();
+
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QOpenGLFunctions *f = ctx->functions();
+ static_cast<QOpenGLExtensions *>(f)->flushShared();
+
+ return tex;
+}
+
static QMatrix4x4 extTransformMatrix(AndroidSurfaceTexture *surfaceTexture)
{
QMatrix4x4 m = surfaceTexture->getTransformMatrix();
@@ -121,419 +267,204 @@ static QMatrix4x4 extTransformMatrix(AndroidSurfaceTexture *surfaceTexture)
return m;
}
-quint64 AndroidTextureVideoBuffer::textureHandle(int plane) const
+class AndroidTextureThread : public QThread
{
- if (plane != 0 || !m_rhi || !m_output->m_nativeSize.isValid())
- return 0;
+ Q_OBJECT
+public:
+ AndroidTextureThread(QAndroidTextureVideoOutput * vo)
+ : QThread()
+ , m_videoOutput(vo)
+ {
+ }
- m_output->ensureExternalTexture(m_rhi);
- m_output->m_surfaceTexture->updateTexImage();
- m_externalMatrix = extTransformMatrix(m_output->m_surfaceTexture);
- return m_output->m_externalTex->nativeTexture().object;
-}
+ ~AndroidTextureThread() {
+ QMetaObject::invokeMethod(this,
+ &AndroidTextureThread::clearSurfaceTexture, Qt::BlockingQueuedConnection);
+ this->quit();
+ this->wait();
+ }
-QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent) : QAndroidVideoOutput(parent) { }
+ void start()
+ {
+ QThread::start();
+ moveToThread(this);
+ }
-QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput()
-{
- clearSurfaceTexture();
-
- if (m_graphicsDeleter) { // Make sure all of these are deleted on the render thread.
- m_graphicsDeleter->deleteResources({
- m_externalTex,
- m_readbackSrc,
- m_readbackTex,
- m_readbackVBuf,
- m_readbackUBuf,
- m_externalTexSampler,
- m_readbackSrb,
- m_readbackRenderTarget,
- m_readbackRpDesc,
- m_readbackPs
- });
+ void initRhi(QOpenGLContext *context)
+ {
+ QRhiGles2InitParams params;
+ params.shareContext = context;
+ params.fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+ m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params));
+ }
- m_graphicsDeleter->deleteRhi(m_readbackRhi, m_readbackRhiFallbackSurface);
- m_graphicsDeleter->deleteThis();
+public slots:
+ void onFrameAvailable(quint64 index)
+ {
+ // Check if 'm_surfaceTexture' is not reset and if the current index is the same that
+ // was used for creating connection because there can be pending frames in queue.
+ if (m_surfaceTexture && m_surfaceTexture->index() == index) {
+ m_surfaceTexture->updateTexImage();
+ auto matrix = extTransformMatrix(m_surfaceTexture.get());
+ auto tex = m_textureCopy->copyExternalTexture(m_size, matrix);
+ auto *buffer = new AndroidTextureVideoBuffer(m_rhi, m_videoOutput->getSurfaceThread(), std::move(tex), m_size);
+ QVideoFrame frame(buffer, QVideoFrameFormat(m_size, QVideoFrameFormat::Format_RGBA8888));
+ emit newFrame(frame);
+ }
}
-}
-void QAndroidTextureVideoOutput::setSubtitle(const QString &subtitle)
-{
- if (!m_sink)
- return;
- auto *sink = m_sink->platformVideoSink();
- sink->setSubtitleText(subtitle);
-}
+ void clearFrame() { emit newFrame({}); }
-QVideoSink *QAndroidTextureVideoOutput::surface() const
-{
- return m_sink;
-}
+ void setFrameSize(QSize size) { m_size = size; }
-void QAndroidTextureVideoOutput::setSurface(QVideoSink *surface)
-{
- if (surface == m_sink)
- return;
+ void clearSurfaceTexture()
+ {
+ m_surfaceTexture.reset();
+ m_texture.reset();
+ m_textureCopy.reset();
+ m_rhi.reset();
+ }
- m_sink = surface;
-}
+ AndroidSurfaceTexture *createSurfaceTexture(QRhi *rhi)
+ {
+ if (m_surfaceTexture)
+ return m_surfaceTexture.get();
-bool QAndroidTextureVideoOutput::isReady()
-{
- return true;
-}
+ QOpenGLContext *ctx = rhi
+ ? static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles())->context
+ : nullptr;
+ initRhi(ctx);
-void QAndroidTextureVideoOutput::initSurfaceTexture()
-{
- if (m_surfaceTexture)
- return;
+ m_texture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, m_size, 1, QRhiTexture::ExternalOES));
+ m_texture->create();
+ m_surfaceTexture = std::make_unique<AndroidSurfaceTexture>(m_texture->nativeTexture().object);
+ if (m_surfaceTexture->surfaceTexture()) {
+ const quint64 index = m_surfaceTexture->index();
+ connect(m_surfaceTexture.get(), &AndroidSurfaceTexture::frameAvailable, this,
+ [this, index] () { this->onFrameAvailable(index); });
- if (!m_sink)
- return;
-
- QMutexLocker locker(&m_mutex);
+ m_textureCopy = std::make_unique<TextureCopy>(m_rhi.get(), m_texture.get());
- m_surfaceTexture = new AndroidSurfaceTexture(m_externalTex ? m_externalTex->nativeTexture().object : 0);
+ } else {
+ m_texture.reset();
+ m_surfaceTexture.reset();
+ }
- if (m_surfaceTexture->surfaceTexture() != 0) {
- connect(m_surfaceTexture, &AndroidSurfaceTexture::frameAvailable,
- this, &QAndroidTextureVideoOutput::onFrameAvailable);
- } else {
- delete m_surfaceTexture;
- m_surfaceTexture = nullptr;
- if (m_graphicsDeleter)
- m_graphicsDeleter->deleteResources({ m_externalTex });
- m_externalTex = nullptr;
+ return m_surfaceTexture.get();
}
-}
-void QAndroidTextureVideoOutput::clearSurfaceTexture()
-{
- QMutexLocker locker(&m_mutex);
- if (m_surfaceTexture) {
- delete m_surfaceTexture;
- m_surfaceTexture = nullptr;
- }
+signals:
+ void newFrame(const QVideoFrame &);
- // Also reset the attached texture
- if (m_graphicsDeleter)
- m_graphicsDeleter->deleteResources({ m_externalTex });
- m_externalTex = nullptr;
-}
+private:
+ QAndroidTextureVideoOutput * m_videoOutput;
+ std::shared_ptr<QRhi> m_rhi;
+ std::unique_ptr<AndroidSurfaceTexture> m_surfaceTexture;
+ std::unique_ptr<QRhiTexture> m_texture;
+ std::unique_ptr<TextureCopy> m_textureCopy;
+ QSize m_size;
+};
-AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
-{
- initSurfaceTexture();
- return m_surfaceTexture;
+QRhiWithThreadGuard::~QRhiWithThreadGuard() {
+ // It may happen that reseting m_rhi shared_ptr will delete it (if it is the last reference)
+ // QRHI need to be deleted from the thread that created it.
+ QMetaObject::invokeMethod(m_thread.get(), [&]() {m_guardRhi.reset();}, Qt::BlockingQueuedConnection);
}
-void QAndroidTextureVideoOutput::setVideoSize(const QSize &size)
+QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QVideoSink *sink, QObject *parent)
+ : QAndroidVideoOutput(parent)
+ , m_sink(sink)
{
- QMutexLocker locker(&m_mutex);
- if (m_nativeSize == size)
+ if (!m_sink) {
+ qDebug() << "Cannot create QAndroidTextureVideoOutput without a sink.";
+ m_surfaceThread = nullptr;
return;
+ }
- stop();
-
- m_nativeSize = size;
+ startNewSurfaceThread();
}
-void QAndroidTextureVideoOutput::start()
+void QAndroidTextureVideoOutput::startNewSurfaceThread()
{
- m_started = true;
- renderAndReadbackFrame();
+ m_surfaceThread = std::make_shared<AndroidTextureThread>(this);
+ connect(m_surfaceThread.get(), &AndroidTextureThread::newFrame,
+ this, &QAndroidTextureVideoOutput::newFrame);
+ m_surfaceThread->start();
}
-void QAndroidTextureVideoOutput::stop()
+QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput()
{
- m_nativeSize = QSize();
- m_started = false;
+ // Make sure that no more VideFrames will be created by surfaceThread
+ QMetaObject::invokeMethod(m_surfaceThread.get(),
+ &AndroidTextureThread::clearSurfaceTexture, Qt::BlockingQueuedConnection);
}
-void QAndroidTextureVideoOutput::renderFrame()
+void QAndroidTextureVideoOutput::setSubtitle(const QString &subtitle)
{
- if (!m_started) {
- m_renderFrame = true;
- bool frameok = renderAndReadbackFrame();
- if (!frameok) {
- m_renderFrame = true;
- renderAndReadbackFrame();
- }
+ if (m_sink) {
+ auto *sink = m_sink->platformVideoSink();
+ if (sink)
+ sink->setSubtitleText(subtitle);
}
}
-void QAndroidTextureVideoOutput::reset()
+bool QAndroidTextureVideoOutput::shouldTextureBeUpdated() const
{
- // flush pending frame
- if (m_sink)
- m_sink->platformVideoSink()->setVideoFrame(QVideoFrame());
-
- clearSurfaceTexture();
+ return m_sink->rhi() && m_surfaceCreatedWithoutRhi;
}
-bool QAndroidTextureVideoOutput::moveToOpenGLContextThread()
+AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
{
if (!m_sink)
- return false;
-
- const auto rhi = m_sink->rhi();
- if (!rhi || rhi->backend() != QRhi::OpenGLES2)
- return false;
-
- const auto nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
- if (!nativeHandles)
- return false;
-
- const auto context = nativeHandles->context;
- if (!context)
- return false;
-
- // check if QAndroidTextureVideoOutput is already in the OpenGL context thread
- if (QThread::currentThread() == context->thread())
- return false;
-
- // move to the OpenGL context thread;
- parent()->moveToThread(context->thread());
- moveToThread(context->thread());
-
- return true;
+ return nullptr;
+
+ AndroidSurfaceTexture *surface = nullptr;
+ QMetaObject::invokeMethod(m_surfaceThread.get(), [&]() {
+ auto rhi = m_sink->rhi();
+ if (!rhi) {
+ m_surfaceCreatedWithoutRhi = true;
+ }
+ else if (m_surfaceCreatedWithoutRhi) {
+ m_surfaceThread->clearSurfaceTexture();
+ m_surfaceCreatedWithoutRhi = false;
+ }
+ surface = m_surfaceThread->createSurfaceTexture(rhi);
+ },
+ Qt::BlockingQueuedConnection);
+ return surface;
}
-void QAndroidTextureVideoOutput::onFrameAvailable()
+void QAndroidTextureVideoOutput::setVideoSize(const QSize &size)
{
- if (!(m_nativeSize.isValid() && m_sink) || !(m_started || m_renderFrame))
+ if (m_nativeSize == size)
return;
- m_renderFrame = false;
- QRhi *rhi = m_sink ? m_sink->rhi() : nullptr;
-
- auto *buffer = new AndroidTextureVideoBuffer(rhi, this, m_nativeSize);
- const QVideoFrameFormat::PixelFormat format = rhi ? QVideoFrameFormat::Format_SamplerExternalOES
- : QVideoFrameFormat::Format_RGBA8888;
- QVideoFrame frame(buffer, QVideoFrameFormat(m_nativeSize, format));
- m_sink->platformVideoSink()->setVideoFrame(frame);
-
- QMetaObject::invokeMethod(m_surfaceTexture, "frameAvailable", Qt::QueuedConnection);
+ m_nativeSize = size;
+ QMetaObject::invokeMethod(m_surfaceThread.get(),
+ [&](){ m_surfaceThread->setFrameSize(size); },
+ Qt::BlockingQueuedConnection);
}
-static const float g_quad[] = {
- -1.f, -1.f, 0.f, 0.f,
- -1.f, 1.f, 0.f, 1.f,
- 1.f, 1.f, 1.f, 1.f,
- 1.f, -1.f, 1.f, 0.f
-};
-
-static QShader getShader(const QString &name)
+void QAndroidTextureVideoOutput::stop()
{
- QFile f(name);
- if (f.open(QIODevice::ReadOnly))
- return QShader::fromSerialized(f.readAll());
-
- return QShader();
+ m_nativeSize = {};
+ QMetaObject::invokeMethod(m_surfaceThread.get(), [&](){ m_surfaceThread->clearFrame(); });
}
-bool QAndroidTextureVideoOutput::renderAndReadbackFrame()
+void QAndroidTextureVideoOutput::reset()
{
- QMutexLocker locker(&m_mutex);
-
- if (!m_nativeSize.isValid() || !m_surfaceTexture)
- return false;
-
- if (moveToOpenGLContextThread()) {
- // just moved to another thread, must close the execution of this method
- QMetaObject::invokeMethod(this, "onFrameAvailable", Qt::ConnectionType::DirectConnection);
- return false;
- }
-
- if (!m_readbackRhi) {
- QRhi *sinkRhi = m_sink ? m_sink->rhi() : nullptr;
- if (sinkRhi && sinkRhi->backend() == QRhi::OpenGLES2) {
- // There is an rhi from the sink, e.g. VideoOutput. We lack the necessary
- // insight to use that directly, so create our own a QRhi that just wraps the
- // same QOpenGLContext.
-
- const auto constHandles =
- static_cast<const QRhiGles2NativeHandles *>(sinkRhi->nativeHandles());
- if (!constHandles) {
- qWarning("Failed to get the QRhiGles2NativeHandles to create QRhi readback.");
- return false;
- }
-
- auto handles = const_cast<QRhiGles2NativeHandles *>(constHandles);
- const auto context = handles->context;
- if (!context) {
- qWarning("Failed to get the QOpenGLContext to create QRhi readback.");
- return false;
- }
-
- sinkRhi->finish();
- m_readbackRhiFallbackSurface =
- QRhiGles2InitParams::newFallbackSurface(context->format());
- QRhiGles2InitParams initParams;
- initParams.format = context->format();
- initParams.fallbackSurface = m_readbackRhiFallbackSurface;
- context->doneCurrent();
-
- m_readbackRhi = QRhi::create(QRhi::OpenGLES2, &initParams, {}, handles);
- } else {
- // No rhi from the sink, e.g. QVideoWidget.
- // We will fire up our own QRhi with its own QOpenGLContext.
- m_readbackRhiFallbackSurface = QRhiGles2InitParams::newFallbackSurface({});
- QRhiGles2InitParams initParams;
- initParams.fallbackSurface = m_readbackRhiFallbackSurface;
- m_readbackRhi = QRhi::create(QRhi::OpenGLES2, &initParams);
- }
- }
-
- if (!m_readbackRhi) {
- qWarning("Failed to create QRhi for video frame readback");
- return false;
- }
-
- QRhiCommandBuffer *cb = nullptr;
- if (m_readbackRhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
- return false;
-
- if (!m_readbackTex || m_readbackTex->pixelSize() != m_nativeSize) {
- delete m_readbackRenderTarget;
- delete m_readbackRpDesc;
- delete m_readbackTex;
- m_readbackTex = m_readbackRhi->newTexture(QRhiTexture::RGBA8, m_nativeSize, 1, QRhiTexture::RenderTarget);
- if (!m_readbackTex->create()) {
- qWarning("Failed to create readback texture");
- return false;
- }
- m_readbackRenderTarget = m_readbackRhi->newTextureRenderTarget({ { m_readbackTex } });
- m_readbackRpDesc = m_readbackRenderTarget->newCompatibleRenderPassDescriptor();
- m_readbackRenderTarget->setRenderPassDescriptor(m_readbackRpDesc);
- m_readbackRenderTarget->create();
- }
-
- m_readbackRhi->makeThreadLocalNativeContextCurrent();
- ensureExternalTexture(m_readbackRhi);
- m_surfaceTexture->updateTexImage();
-
- // The only purpose of m_readbackSrc is to be nice and have a QRhiTexture that belongs
- // to m_readbackRhi, not the sink's rhi if there is one. The underlying native object
- // (and the rhi's OpenGL context) are the same regardless.
- if (!m_readbackSrc)
- m_readbackSrc = m_readbackRhi->newTexture(QRhiTexture::RGBA8, m_nativeSize, 1, QRhiTexture::ExternalOES);
-
- // Keep the object the same (therefore all references to m_readbackSrc in
- // the srb or other objects stay valid all the time), just call createFrom
- // if the native external texture changes.
- const quint64 texId = m_externalTex->nativeTexture().object;
- if (m_readbackSrc->nativeTexture().object != texId)
- m_readbackSrc->createFrom({ texId, 0 });
-
- QRhiResourceUpdateBatch *rub = nullptr;
- if (!m_readbackVBuf) {
- m_readbackVBuf = m_readbackRhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(g_quad));
- m_readbackVBuf->create();
- if (!rub)
- rub = m_readbackRhi->nextResourceUpdateBatch();
- rub->uploadStaticBuffer(m_readbackVBuf, g_quad);
- }
-
- if (!m_readbackUBuf) {
- m_readbackUBuf = m_readbackRhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 64 + 4 + 4);
- m_readbackUBuf->create();
- }
-
- if (!m_externalTexSampler) {
- m_externalTexSampler = m_readbackRhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
- QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
- m_externalTexSampler->create();
- }
-
- if (!m_readbackSrb) {
- m_readbackSrb = m_readbackRhi->newShaderResourceBindings();
- m_readbackSrb->setBindings({
- QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_readbackUBuf),
- QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_readbackSrc, m_externalTexSampler)
- });
- m_readbackSrb->create();
- }
-
- if (!m_readbackPs) {
- m_readbackPs = m_readbackRhi->newGraphicsPipeline();
- m_readbackPs->setTopology(QRhiGraphicsPipeline::TriangleFan);
- QShader vs = getShader(QStringLiteral(":/qt-project.org/multimedia/shaders/externalsampler.vert.qsb"));
- Q_ASSERT(vs.isValid());
- QShader fs = getShader(QStringLiteral(":/qt-project.org/multimedia/shaders/externalsampler.frag.qsb"));
- Q_ASSERT(fs.isValid());
- m_readbackPs->setShaderStages({
- { QRhiShaderStage::Vertex, vs },
- { QRhiShaderStage::Fragment, fs }
- });
- QRhiVertexInputLayout inputLayout;
- inputLayout.setBindings({
- { 4 * sizeof(float) }
- });
- inputLayout.setAttributes({
- { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
- { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
- });
- m_readbackPs->setVertexInputLayout(inputLayout);
- m_readbackPs->setShaderResourceBindings(m_readbackSrb);
- m_readbackPs->setRenderPassDescriptor(m_readbackRpDesc);
- m_readbackPs->create();
- }
-
- QMatrix4x4 identity;
- char *p = m_readbackUBuf->beginFullDynamicBufferUpdateForCurrentFrame();
- memcpy(p, identity.constData(), 64);
- QMatrix4x4 extMatrix = extTransformMatrix(m_surfaceTexture);
- memcpy(p + 64, extMatrix.constData(), 64);
- float opacity = 1.0f;
- memcpy(p + 64 + 64, &opacity, 4);
- m_readbackUBuf->endFullDynamicBufferUpdateForCurrentFrame();
-
- cb->beginPass(m_readbackRenderTarget, Qt::transparent, { 1.0f, 0 }, rub);
- cb->setGraphicsPipeline(m_readbackPs);
- cb->setViewport(QRhiViewport(0, 0, m_nativeSize.width(), m_nativeSize.height()));
- cb->setShaderResources();
- const QRhiCommandBuffer::VertexInput vbufBinding(m_readbackVBuf, 0);
- cb->setVertexInput(0, 1, &vbufBinding);
- cb->draw(4);
-
- QRhiReadbackDescription readDesc(m_readbackTex);
- QRhiReadbackResult readResult;
- bool readCompleted = false;
- // invoked at latest in the endOffscreenFrame() below
- readResult.completed = [&readCompleted] { readCompleted = true; };
- rub = m_readbackRhi->nextResourceUpdateBatch();
- rub->readBackTexture(readDesc, &readResult);
-
- cb->endPass(rub);
-
- m_readbackRhi->endOffscreenFrame();
-
- if (!readCompleted)
- return false;
-
- // implicit sharing, keep the data alive
- m_readbackImageData = readResult.data;
- // the QImage does not own the data
- m_readbackImage = QImage(reinterpret_cast<const uchar *>(m_readbackImageData.constData()),
- readResult.pixelSize.width(), readResult.pixelSize.height(),
- QImage::Format_ARGB32_Premultiplied);
-
- return true;
+ if (m_sink)
+ m_sink->platformVideoSink()->setVideoFrame({});
+ QMetaObject::invokeMethod(m_surfaceThread.get(), &AndroidTextureThread::clearSurfaceTexture);
}
-void QAndroidTextureVideoOutput::ensureExternalTexture(QRhi *rhi)
+void QAndroidTextureVideoOutput::newFrame(const QVideoFrame &frame)
{
- if (!m_graphicsDeleter)
- m_graphicsDeleter = new GraphicsResourceDeleter;
-
- if (!m_externalTex) {
- m_surfaceTexture->detachFromGLContext();
- m_externalTex = rhi->newTexture(QRhiTexture::RGBA8, m_nativeSize, 1, QRhiTexture::ExternalOES);
- if (!m_externalTex->create())
- qWarning("Failed to create native texture object");
- m_surfaceTexture->attachToGLContext(m_externalTex->nativeTexture().object);
- }
+ if (m_sink)
+ m_sink->setVideoFrame(frame);
}
QT_END_NAMESPACE
+
+#include "qandroidvideooutput.moc"
+#include "moc_qandroidvideooutput_p.cpp"
diff --git a/src/plugins/multimedia/android/common/qandroidvideooutput_p.h b/src/plugins/multimedia/android/common/qandroidvideooutput_p.h
index 377f3a4eb..c59a1b76c 100644
--- a/src/plugins/multimedia/android/common/qandroidvideooutput_p.h
+++ b/src/plugins/multimedia/android/common/qandroidvideooutput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDVIDEOOUTPUT_H
#define QANDROIDVIDEOOUTPUT_H
@@ -51,15 +15,13 @@
// We mean it.
//
-#include <qobject.h>
#include <qsize.h>
#include <qmutex.h>
#include <qreadwritelock.h>
#include <private/qabstractvideobuffer_p.h>
#include <qmatrix4x4.h>
-#include <QtGui/private/qrhi_p.h>
-#include <QtGui/qoffscreensurface.h>
-#include <QPixmap>
+#include <qoffscreensurface.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -82,6 +44,7 @@ public:
virtual void start() { }
virtual void stop() { }
virtual void reset() { }
+ virtual QSize getVideoSize() const { return QSize(0, 0); }
Q_SIGNALS:
void readyChanged(bool);
@@ -90,126 +53,36 @@ protected:
QAndroidVideoOutput(QObject *parent) : QObject(parent) { }
};
-class GraphicsResourceDeleter : public QObject
-{
- Q_OBJECT
-public:
- void deleteResources(const QList<QRhiResource *> &res) { QMetaObject::invokeMethod(this, "deleteResourcesHelper", Qt::AutoConnection, Q_ARG(QList<QRhiResource*>, res)); }
- void deleteRhi(QRhi *rhi, QOffscreenSurface *surf) { QMetaObject::invokeMethod(this, "deleteRhiHelper", Qt::AutoConnection, Q_ARG(QRhi*, rhi), Q_ARG(QOffscreenSurface*, surf)); }
- void deleteThis() { QMetaObject::invokeMethod(this, "deleteThisHelper"); }
-
-private:
- Q_INVOKABLE void deleteResourcesHelper(const QList<QRhiResource *> &res);
- Q_INVOKABLE void deleteRhiHelper(QRhi *rhi, QOffscreenSurface *surf);
- Q_INVOKABLE void deleteThisHelper();
-};
-
+class AndroidTextureThread;
class QAndroidTextureVideoOutput : public QAndroidVideoOutput
{
Q_OBJECT
public:
- explicit QAndroidTextureVideoOutput(QObject *parent = 0);
+ explicit QAndroidTextureVideoOutput(QVideoSink *sink, QObject *parent = 0);
~QAndroidTextureVideoOutput() override;
- QVideoSink *surface() const;
- void setSurface(QVideoSink *surface);
+ QVideoSink *surface() const { return m_sink; }
+ bool shouldTextureBeUpdated() const;
AndroidSurfaceTexture *surfaceTexture() override;
- bool isReady() override;
void setVideoSize(const QSize &) override;
- void start() override;
void stop() override;
void reset() override;
- void renderFrame();
+ QSize getVideoSize() const override { return m_nativeSize; }
void setSubtitle(const QString &subtitle);
+ std::shared_ptr<AndroidTextureThread> getSurfaceThread() { return m_surfaceThread; }
private Q_SLOTS:
- void onFrameAvailable();
+ void newFrame(const QVideoFrame &);
private:
- void initSurfaceTexture();
- bool renderAndReadbackFrame();
- void ensureExternalTexture(QRhi *rhi);
-
- bool moveToOpenGLContextThread();
-
- QMutex m_mutex;
- QReadWriteLock m_subtitleLock;
-
- void clearSurfaceTexture();
-
+ void startNewSurfaceThread();
QVideoSink *m_sink = nullptr;
QSize m_nativeSize;
- bool m_started = false;
- bool m_renderFrame = false;
-
- AndroidSurfaceTexture *m_surfaceTexture = nullptr;
-
- QRhiTexture *m_externalTex = nullptr;
-
- QRhi *m_readbackRhi = nullptr;
- QOffscreenSurface *m_readbackRhiFallbackSurface = nullptr;
- QRhiTexture *m_readbackSrc = nullptr;
- QRhiTexture *m_readbackTex = nullptr;
- QRhiBuffer *m_readbackVBuf = nullptr;
- QRhiBuffer *m_readbackUBuf = nullptr;
- QRhiSampler *m_externalTexSampler = nullptr;
- QRhiShaderResourceBindings *m_readbackSrb = nullptr;
- QRhiTextureRenderTarget *m_readbackRenderTarget = nullptr;
- QRhiRenderPassDescriptor *m_readbackRpDesc = nullptr;
- QRhiGraphicsPipeline *m_readbackPs = nullptr;
-
- QImage m_readbackImage;
- QByteArray m_readbackImageData;
+ bool m_surfaceCreatedWithoutRhi = false;
- QString m_subtitleText;
- QPixmap m_subtitlePixmap;
-
- GraphicsResourceDeleter *m_graphicsDeleter = nullptr;
-
- friend class AndroidTextureVideoBuffer;
-};
-
-
-class AndroidTextureVideoBuffer : public QAbstractVideoBuffer
-{
-public:
- AndroidTextureVideoBuffer(QRhi *rhi, QAndroidTextureVideoOutput *output, const QSize &size)
- : QAbstractVideoBuffer(rhi ? QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle, rhi)
- , m_output(output)
- , m_size(size)
- {
- }
-
- virtual ~AndroidTextureVideoBuffer() {}
-
- QVideoFrame::MapMode mapMode() const override { return m_mapMode; }
-
- MapData map(QVideoFrame::MapMode mode) override;
-
- void unmap() override
- {
- m_image = QImage();
- m_mapMode = QVideoFrame::NotMapped;
- }
-
- quint64 textureHandle(int plane) const override;
-
- QMatrix4x4 externalTextureMatrix() const override
- {
- return m_externalMatrix;
- }
-
-private:
- bool updateReadbackFrame();
-
- QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped;
- QAndroidTextureVideoOutput *m_output = nullptr;
- QImage m_image;
- QSize m_size;
- mutable QMatrix4x4 m_externalMatrix;
- bool m_textureUpdated = false;
+ std::shared_ptr<AndroidTextureThread> m_surfaceThread;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/common/qandroidvideosink.cpp b/src/plugins/multimedia/android/common/qandroidvideosink.cpp
index 7cc0fefe4..3da5eab31 100644
--- a/src/plugins/multimedia/android/common/qandroidvideosink.cpp
+++ b/src/plugins/multimedia/android/common/qandroidvideosink.cpp
@@ -1,44 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidvideosink_p.h"
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtCore/qdebug.h>
@@ -46,8 +10,6 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcMediaVideoSink, "qt.multimedia.videosink")
-
QAndroidVideoSink::QAndroidVideoSink(QVideoSink *parent)
: QPlatformVideoSink(parent)
{
@@ -65,6 +27,9 @@ void QAndroidVideoSink::setRhi(QRhi *rhi)
return;
m_rhi = rhi;
+ emit rhiChanged(rhi);
}
QT_END_NAMESPACE
+
+#include "moc_qandroidvideosink_p.cpp"
diff --git a/src/plugins/multimedia/android/common/qandroidvideosink_p.h b/src/plugins/multimedia/android/common/qandroidvideosink_p.h
index d653234f1..9afc58f65 100644
--- a/src/plugins/multimedia/android/common/qandroidvideosink_p.h
+++ b/src/plugins/multimedia/android/common/qandroidvideosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDVIDEOSINK_P_H
#define QANDROIDVIDEOSINK_P_H
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp b/src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp
index 3bcc93564..52d2e00f6 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamera.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidcamera_p.h"
#include "qandroidcamerasession_p.h"
@@ -60,8 +24,11 @@ QAndroidCamera::~QAndroidCamera()
void QAndroidCamera::setActive(bool active)
{
- if (m_cameraSession)
+ if (m_cameraSession) {
m_cameraSession->setActive(active);
+ } else {
+ isPendingSetActive = active;
+ }
}
bool QAndroidCamera::isActive() const
@@ -135,6 +102,11 @@ void QAndroidCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
connect(m_cameraSession, &QAndroidCameraSession::activeChanged, this, &QAndroidCamera::activeChanged);
connect(m_cameraSession, &QAndroidCameraSession::error, this, &QAndroidCamera::error);
connect(m_cameraSession, &QAndroidCameraSession::opened, this, &QAndroidCamera::onCameraOpened);
+
+ if (isPendingSetActive) {
+ setActive(true);
+ isPendingSetActive = false;
+ }
}
void QAndroidCamera::setFocusMode(QCamera::FocusMode mode)
@@ -225,13 +197,11 @@ void QAndroidCamera::onCameraOpened()
if (m_cameraSession->camera()->isZoomSupported()) {
m_zoomRatios = m_cameraSession->camera()->getZoomRatios();
qreal maxZoom = m_zoomRatios.last() / qreal(100);
- if (m_maximumZoom != maxZoom) {
- m_maximumZoom = maxZoom;
- }
+ maximumZoomFactorChanged(maxZoom);
zoomTo(1, -1);
} else {
m_zoomRatios.clear();
- m_maximumZoom = 1.0;
+ maximumZoomFactorChanged(1.0);
}
m_minExposureCompensationIndex = m_cameraSession->camera()->getMinExposureCompensation();
@@ -588,3 +558,5 @@ void QAndroidCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
}
QT_END_NAMESPACE
+
+#include "moc_qandroidcamera_p.cpp"
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcamera_p.h b/src/plugins/multimedia/android/mediacapture/qandroidcamera_p.h
index 9ee775012..77bbc3133 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidcamera_p.h
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamera_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDCAMERACONTROL_H
@@ -112,7 +76,6 @@ private:
bool m_continuousVideoFocusSupported = false;
bool m_focusPointSupported = false;
- float m_maximumZoom;
QList<int> m_zoomRatios;
QList<QCamera::ExposureMode> m_supportedExposureModes;
@@ -123,6 +86,7 @@ private:
bool isFlashSupported = false;
bool isFlashAutoSupported = false;
bool isTorchSupported = false;
+ bool isPendingSetActive = false;
QCameraDevice m_cameraDev;
QMap<QCamera::WhiteBalanceMode, QString> m_supportedWhiteBalanceModes;
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp
index 83ac04545..f7dd1c653 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Ruslan Baratov
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Ruslan Baratov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidcamerasession_p.h"
@@ -53,6 +17,7 @@
#include <qdebug.h>
#include <qvideoframe.h>
#include <private/qplatformimagecapture_p.h>
+#include <private/qplatformvideosink_p.h>
#include <private/qmemoryvideobuffer_p.h>
#include <private/qcameradevice_p.h>
#include <private/qmediastoragelocation_p.h>
@@ -89,6 +54,8 @@ QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
QAndroidCameraSession::~QAndroidCameraSession()
{
+ if (m_sink)
+ disconnect(m_retryPreviewConnection);
close();
}
@@ -269,6 +236,8 @@ void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool resta
// -- adjust resolution
QSize adjustedViewfinderResolution;
+ const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
+
const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0;
if (validCaptureSize
&& m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
@@ -280,8 +249,6 @@ void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool resta
if (validCaptureSize)
captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
- const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
-
if (validCaptureSize) {
// search for viewfinder resolution with the same aspect ratio
qreal minAspectDiff = 1;
@@ -319,31 +286,37 @@ void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool resta
// -- adjust FPS
- AndroidCamera::FpsRange adjustedFps = m_requestedFpsRange;;
+ AndroidCamera::FpsRange adjustedFps = m_requestedFpsRange;
if (adjustedFps.min == 0 || adjustedFps.max == 0)
adjustedFps = currentFpsRange;
// -- Set values on camera
// fix the resolution of output based on the orientation
- QSize outputResolution = adjustedViewfinderResolution;
+ QSize cameraOutputResolution = adjustedViewfinderResolution;
+ QSize videoOutputResolution = adjustedViewfinderResolution;
+ QSize currentVideoOutputResolution = m_videoOutput ? m_videoOutput->getVideoSize() : QSize(0, 0);
const int rotation = currentCameraRotation();
- if (rotation == 90 || rotation == 270)
- outputResolution.transpose();
+ // only transpose if it's valid for the preview
+ if (rotation == 90 || rotation == 270) {
+ videoOutputResolution.transpose();
+ if (previewSizes.contains(cameraOutputResolution.transposed()))
+ cameraOutputResolution.transpose();
+ }
- if (currentViewfinderResolution != outputResolution
+ if (currentViewfinderResolution != cameraOutputResolution
+ || (m_videoOutput && currentVideoOutputResolution != videoOutputResolution)
|| currentPreviewFormat != adjustedPreviewFormat || currentFpsRange.min != adjustedFps.min
|| currentFpsRange.max != adjustedFps.max) {
-
if (m_videoOutput) {
- m_videoOutput->setVideoSize(outputResolution);
+ m_videoOutput->setVideoSize(videoOutputResolution);
}
// if preview is started, we have to stop it first before changing its size
if (m_previewStarted && restartPreview)
m_camera->stopPreview();
- m_camera->setPreviewSize(outputResolution);
+ m_camera->setPreviewSize(cameraOutputResolution);
m_camera->setPreviewFormat(adjustedPreviewFormat);
m_camera->setPreviewFpsRange(adjustedFps);
@@ -444,7 +417,6 @@ void QAndroidCameraSession::stopPreview()
if (m_videoOutput) {
m_videoOutput->stop();
- m_videoOutput->reset();
}
m_previewStarted = false;
}
@@ -612,7 +584,6 @@ int QAndroidCameraSession::captureImage()
m_currentImageCaptureId = newImageCaptureId;
- applyImageSettings();
applyResolution(m_actualImageSettings.resolution());
m_camera->takePicture();
@@ -689,12 +660,16 @@ void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame)
m_videoFrameCallbackMutex.unlock();
}
-void QAndroidCameraSession::onCameraPictureCaptured(const QVideoFrame &frame)
+void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &bytes,
+ QVideoFrameFormat::PixelFormat format, QSize size,int bytesPerLine)
{
- // Loading and saving the captured image can be slow, do it in a separate thread
- (void)QtConcurrent::run(&QAndroidCameraSession::processCapturedImage, this,
- m_currentImageCaptureId, frame, m_imageCaptureToBuffer,
- m_currentImageCaptureFileName);
+ if (m_imageCaptureToBuffer) {
+ processCapturedImageToBuffer(m_currentImageCaptureId, bytes, format, size, bytesPerLine);
+ } else {
+ // Loading and saving the captured image can be slow, do it in a separate thread
+ (void)QtConcurrent::run(&QAndroidCameraSession::processCapturedImage, this,
+ m_currentImageCaptureId, bytes, m_currentImageCaptureFileName);
+ }
// Preview needs to be restarted after taking a picture
if (m_camera)
@@ -732,37 +707,37 @@ void QAndroidCameraSession::onCameraPreviewStopped()
setReadyForCapture(false);
}
-void QAndroidCameraSession::processCapturedImage(int id, const QVideoFrame &frame,
- bool captureToBuffer, const QString &fileName)
+void QAndroidCameraSession::processCapturedImage(int id, const QByteArray &bytes, const QString &fileName)
{
- if (captureToBuffer) {
- emit imageAvailable(id, frame);
- return;
- }
-
const QString actualFileName = QMediaStorageLocation::generateFileName(
fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg"));
- QImageWriter writer(actualFileName);
-
- if (!writer.canWrite()) {
+ QFile writer(actualFileName);
+ if (!writer.open(QIODeviceBase::WriteOnly)) {
const QString errorMessage = tr("File is not available: %1").arg(writer.errorString());
emit imageCaptureError(id, QImageCapture::Error::ResourceError, errorMessage);
return;
}
- const bool written = writer.write(frame.toImage());
- if (!written) {
+ if (writer.write(bytes) < 0) {
const QString errorMessage = tr("Could not save to file: %1").arg(writer.errorString());
emit imageCaptureError(id, QImageCapture::Error::ResourceError, errorMessage);
return;
}
+ writer.close();
if (fileName.isEmpty() || QFileInfo(fileName).isRelative())
AndroidMultimediaUtils::registerMediaFile(actualFileName);
emit imageSaved(id, actualFileName);
}
+void QAndroidCameraSession::processCapturedImageToBuffer(int id, const QByteArray &bytes,
+ QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine)
+{
+ QVideoFrame frame(new QMemoryVideoBuffer(bytes, bytesPerLine), QVideoFrameFormat(size, format));
+ emit imageAvailable(id, frame);
+}
+
void QAndroidCameraSession::onVideoOutputReady(bool ready)
{
if (ready && m_active)
@@ -801,17 +776,30 @@ void QAndroidCameraSession::setVideoSink(QVideoSink *sink)
if (m_sink == sink)
return;
+ if (m_sink)
+ disconnect(m_retryPreviewConnection);
+
m_sink = sink;
+ if (m_sink)
+ m_retryPreviewConnection =
+ connect(m_sink->platformVideoSink(), &QPlatformVideoSink::rhiChanged, this, [&]()
+ {
+ if (m_active) {
+ setActive(false);
+ setActive(true);
+ }
+ }, Qt::DirectConnection);
if (m_sink) {
delete m_textureOutput;
m_textureOutput = nullptr;
- m_textureOutput = new QAndroidTextureVideoOutput(this);
- m_textureOutput->setSurface(m_sink);
+ m_textureOutput = new QAndroidTextureVideoOutput(m_sink, this);
}
setVideoOutput(m_textureOutput);
}
QT_END_NAMESPACE
+
+#include "moc_qandroidcamerasession_p.cpp"
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h
index 2fdd9d33b..3b56d9c3b 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcamerasession_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Ruslan Baratov
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Ruslan Baratov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDCAMERASESSION_H
#define QANDROIDCAMERASESSION_H
@@ -138,7 +102,7 @@ private Q_SLOTS:
void onCameraTakePictureFailed();
void onCameraPictureExposed();
- void onCameraPictureCaptured(const QVideoFrame &frame);
+ void onCameraPictureCaptured(const QByteArray &bytes, QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine);
void onLastPreviewFrameFetched(const QVideoFrame &frame);
void onNewPreviewFrame(const QVideoFrame &frame);
void onCameraPreviewStarted();
@@ -157,8 +121,9 @@ private:
void applyImageSettings();
void processPreviewImage(int id, const QVideoFrame &frame, int rotation);
- void processCapturedImage(int id, const QVideoFrame &frame, bool captureToBuffer,
- const QString &fileName);
+ void processCapturedImage(int id, const QByteArray &bytes, const QString &fileName);
+ void processCapturedImageToBuffer(int id, const QByteArray &bytes,
+ QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine);
void setActiveHelper(bool active);
@@ -193,6 +158,7 @@ private:
QMutex m_videoFrameCallbackMutex;
PreviewCallback *m_previewCallback;
bool m_keepActive;
+ QMetaObject::Connection m_retryPreviewConnection;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp
index d274fc9e9..3b005e4a5 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp
@@ -1,46 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidcapturesession_p.h"
#include "androidcamera_p.h"
#include "qandroidcamerasession_p.h"
+#include "qaudioinput.h"
+#include "qaudiooutput.h"
#include "androidmediaplayer_p.h"
#include "androidmultimediautils_p.h"
#include "qandroidmultimediautils_p.h"
@@ -74,6 +40,8 @@ QAndroidCaptureSession::~QAndroidCaptureSession()
{
stop();
m_mediaRecorder = nullptr;
+ if (m_audioInput && m_audioOutput)
+ AndroidMediaPlayer::stopSoundStreaming();
}
void QAndroidCaptureSession::setCameraSession(QAndroidCameraSession *cameraSession)
@@ -97,7 +65,23 @@ void QAndroidCaptureSession::setCameraSession(QAndroidCameraSession *cameraSessi
void QAndroidCaptureSession::setAudioInput(QPlatformAudioInput *input)
{
+ if (m_audioInput == input)
+ return;
+
+ if (m_audioInput) {
+ disconnect(m_audioInputChanged);
+ }
+
m_audioInput = input;
+
+ if (m_audioInput) {
+ m_audioInputChanged = connect(m_audioInput->q, &QAudioInput::deviceChanged, this, [this]() {
+ if (m_state == QMediaRecorder::RecordingState)
+ m_mediaRecorder->setAudioInput(m_audioInput->device.id());
+ updateStreamingState();
+ });
+ }
+ updateStreamingState();
}
void QAndroidCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
@@ -105,10 +89,30 @@ void QAndroidCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
if (m_audioOutput == output)
return;
+ if (m_audioOutput)
+ disconnect(m_audioOutputChanged);
+
m_audioOutput = output;
- if (m_audioOutput)
+ if (m_audioOutput) {
+ m_audioOutputChanged = connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this,
+ [this] () {
+ AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id());
+ updateStreamingState();
+ });
AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id());
+ }
+ updateStreamingState();
+}
+
+void QAndroidCaptureSession::updateStreamingState()
+{
+ if (m_audioInput && m_audioOutput) {
+ AndroidMediaPlayer::startSoundStreaming(m_audioInput->device.id().toInt(),
+ m_audioOutput->device.id().toInt());
+ } else {
+ AndroidMediaPlayer::stopSoundStreaming();
+ }
}
QMediaRecorder::RecorderState QAndroidCaptureSession::state() const
@@ -129,7 +133,7 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl &
return;
if (!m_cameraSession && !m_audioInput) {
- emit error(QMediaRecorder::ResourceError, QLatin1String("No devices are set"));
+ updateError(QMediaRecorder::ResourceError, QLatin1String("No devices are set"));
return;
}
@@ -137,14 +141,14 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl &
const bool validCameraSession = m_cameraSession && m_cameraSession->camera();
- if (validCameraSession && !qt_androidRequestCameraPermission()) {
- emit error(QMediaRecorder::ResourceError, QLatin1String("Camera permission denied."));
+ if (validCameraSession && !qt_androidCheckCameraPermission()) {
+ updateError(QMediaRecorder::ResourceError, QLatin1String("Camera permission denied."));
setKeepAlive(false);
return;
}
- if (m_audioInput && !qt_androidRequestRecordingPermission()) {
- emit error(QMediaRecorder::ResourceError, QLatin1String("Microphone permission denied."));
+ if (m_audioInput && !qt_androidCheckMicrophonePermission()) {
+ updateError(QMediaRecorder::ResourceError, QLatin1String("Microphone permission denied."));
setKeepAlive(false);
return;
}
@@ -160,7 +164,6 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl &
// Set audio/video sources
if (validCameraSession) {
m_cameraSession->camera()->stopPreviewSynchronous();
- m_cameraSession->applyResolution(settings.videoResolution(), false);
m_cameraSession->camera()->unlock();
m_mediaRecorder->setCamera(m_cameraSession->camera());
@@ -168,7 +171,6 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl &
}
if (m_audioInput) {
- m_mediaRecorder->setAudioSource(AndroidMediaRecorder::Camcorder);
m_mediaRecorder->setAudioInput(m_audioInput->device.id());
if (!m_mediaRecorder->isAudioSourceSet())
m_mediaRecorder->setAudioSource(AndroidMediaRecorder::DefaultAudioSource);
@@ -214,36 +216,20 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl &
m_outputLocationIsStandard = location.isEmpty() || QFileInfo(location).isRelative();
m_mediaRecorder->setOutputFile(filePath);
- // Even though the Android doc explicitly says that calling MediaRecorder.setPreviewDisplay()
- // is not necessary when the Camera already has a Surface, it doesn't actually work on some
- // devices. For example on the Samsung Galaxy Tab 2, the camera server dies after prepare()
- // and start() if MediaRecorder.setPreviewDisplay() is not called.
if (validCameraSession) {
-
- if (m_cameraSession->videoOutput()) {
- // When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the
- // same one that is set on the Camera or it will crash, hence the reset().
- m_cameraSession->videoOutput()->reset();
- if (m_cameraSession->videoOutput()->surfaceTexture())
- m_mediaRecorder->setSurfaceTexture(
- m_cameraSession->videoOutput()->surfaceTexture());
- else if (m_cameraSession->videoOutput()->surfaceHolder())
- m_mediaRecorder->setSurfaceHolder(m_cameraSession->videoOutput()->surfaceHolder());
- }
-
m_cameraSession->disableRotation();
}
if (!m_mediaRecorder->prepare()) {
- emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder."));
+ updateError(QMediaRecorder::FormatError,
+ QLatin1String("Unable to prepare the media recorder."));
restartViewfinder();
return;
}
if (!m_mediaRecorder->start()) {
- emit error(QMediaRecorder::FormatError,
- QMediaRecorderPrivate::msgFailedStartRecording());
+ updateError(QMediaRecorder::FormatError, QMediaRecorderPrivate::msgFailedStartRecording());
restartViewfinder();
return;
@@ -380,23 +366,7 @@ void QAndroidCaptureSession::restartViewfinder()
if (m_cameraSession && m_cameraSession->camera()) {
m_cameraSession->camera()->reconnect();
-
- // This is not necessary on most devices, but it crashes on some if we don't stop the
- // preview and reset the preview display on the camera when recording is over.
m_cameraSession->camera()->stopPreviewSynchronous();
-
- if (m_cameraSession->videoOutput()) {
- // When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the
- // same one that is set on the Camera or it will crash, hence the reset().
- m_cameraSession->videoOutput()->reset();
- if (m_cameraSession->videoOutput()->surfaceTexture())
- m_cameraSession->camera()->setPreviewTexture(
- m_cameraSession->videoOutput()->surfaceTexture());
- else if (m_cameraSession->videoOutput()->surfaceHolder())
- m_cameraSession->camera()->setPreviewDisplay(
- m_cameraSession->videoOutput()->surfaceHolder());
- }
-
m_cameraSession->camera()->startPreview();
m_cameraSession->setReadyForCapture(true);
m_cameraSession->enableRotation();
@@ -434,6 +404,10 @@ void QAndroidCaptureSession::onCameraOpened()
std::sort(m_supportedResolutions.begin(), m_supportedResolutions.end(), qt_sizeLessThan);
std::sort(m_supportedFramerates.begin(), m_supportedFramerates.end());
+
+ QMediaEncoderSettings defaultSettings;
+ applySettings(defaultSettings);
+ m_cameraSession->applyResolution(defaultSettings.videoResolution());
}
QAndroidCaptureSession::CaptureProfile QAndroidCaptureSession::getProfile(int id)
@@ -477,7 +451,7 @@ void QAndroidCaptureSession::onError(int what, int extra)
Q_UNUSED(what);
Q_UNUSED(extra);
stop(true);
- emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error."));
+ updateError(QMediaRecorder::ResourceError, QLatin1String("Unknown error."));
}
void QAndroidCaptureSession::onInfo(int what, int extra)
@@ -486,12 +460,14 @@ void QAndroidCaptureSession::onInfo(int what, int extra)
if (what == 800) {
// MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
stop();
- emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached."));
+ updateError(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached."));
} else if (what == 801) {
// MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
stop();
- emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum file size reached."));
+ updateError(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum file size reached."));
}
}
QT_END_NAMESPACE
+
+#include "moc_qandroidcapturesession_p.cpp"
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h
index e91e5b210..161d47994 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h
+++ b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDCAPTURESESSION_H
#define QANDROIDCAPTURESESSION_H
@@ -103,10 +67,10 @@ public:
if (m_mediaEncoder)
m_mediaEncoder->actualLocationChanged(location);
}
- void error(int error, const QString &errorString)
+ void updateError(int error, const QString &errorString)
{
if (m_mediaEncoder)
- m_mediaEncoder->error(QMediaRecorder::Error(error), errorString);
+ m_mediaEncoder->updateError(QMediaRecorder::Error(error), errorString);
}
private Q_SLOTS:
@@ -153,6 +117,7 @@ private:
CaptureProfile getProfile(int id);
void restartViewfinder();
+ void updateStreamingState();
QAndroidMediaEncoder *m_mediaEncoder = nullptr;
std::shared_ptr<AndroidMediaRecorder> m_mediaRecorder;
@@ -179,6 +144,8 @@ private:
QList<QSize> m_supportedResolutions;
QList<qreal> m_supportedFramerates;
+ QMetaObject::Connection m_audioInputChanged;
+ QMetaObject::Connection m_audioOutputChanged;
QMetaObject::Connection m_connOpenCamera;
QMetaObject::Connection m_connActiveChangedCamera;
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp b/src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp
index a3fd5cd16..4105851ed 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp
+++ b/src/plugins/multimedia/android/mediacapture/qandroidimagecapture.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidimagecapture_p.h"
@@ -105,3 +69,5 @@ void QAndroidImageCapture::setCaptureSession(QPlatformMediaCaptureSession *sessi
this, &QAndroidImageCapture::error);
}
QT_END_NAMESPACE
+
+#include "moc_qandroidimagecapture_p.cpp"
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidimagecapture_p.h b/src/plugins/multimedia/android/mediacapture/qandroidimagecapture_p.h
index 83d15445f..ac273c195 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidimagecapture_p.h
+++ b/src/plugins/multimedia/android/mediacapture/qandroidimagecapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDCAMERAIMAGECAPTURECONTROL_H
#define QANDROIDCAMERAIMAGECAPTURECONTROL_H
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp b/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp
index ddc690d77..e2b551d35 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp
+++ b/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Ruslan Baratov
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Ruslan Baratov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidmediacapturesession_p.h"
@@ -83,10 +47,8 @@ void QAndroidMediaCaptureSession::setCamera(QPlatformCamera *camera)
m_cameraControl->setCaptureSession(nullptr);
m_cameraControl = control;
- if (m_cameraControl) {
+ if (m_cameraControl)
m_cameraControl->setCaptureSession(this);
- m_cameraControl->setActive(true);
- }
emit cameraChanged();
}
@@ -149,3 +111,5 @@ void QAndroidMediaCaptureSession::setVideoPreview(QVideoSink *sink)
}
QT_END_NAMESPACE
+
+#include "moc_qandroidmediacapturesession_p.cpp"
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession_p.h b/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession_p.h
index bd32bb834..90c792c32 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession_p.h
+++ b/src/plugins/multimedia/android/mediacapture/qandroidmediacapturesession_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Ruslan Baratov
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Ruslan Baratov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDCAPTURESERVICE_H
#define QANDROIDCAPTURESERVICE_H
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp b/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp
index b3543bd91..d3449312d 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp
+++ b/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidmediaencoder_p.h"
#include "qandroidmultimediautils_p.h"
diff --git a/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h b/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h
index 281c10317..b46268449 100644
--- a/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h
+++ b/src/plugins/multimedia/android/mediacapture/qandroidmediaencoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDMEDIAENCODER_H
#define QANDROIDMEDIAENCODER_H
diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp
index 01a57c298..b257a8986 100644
--- a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp
+++ b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidmediaplayer_p.h"
#include "androidmediaplayer_p.h"
@@ -44,11 +8,12 @@
#include "qandroidaudiooutput_p.h"
#include "qaudiooutput.h"
+#include <private/qplatformvideosink_p.h>
#include <qloggingcategory.h>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcMediaPlayer, "qt.multimedia.mediaplayer.android")
+static Q_LOGGING_CATEGORY(lcMediaPlayer, "qt.multimedia.mediaplayer.android")
class StateChangeNotifier
{
@@ -84,6 +49,8 @@ QAndroidMediaPlayer::QAndroidMediaPlayer(QMediaPlayer *parent)
mMediaPlayer(new AndroidMediaPlayer),
mState(AndroidMediaPlayer::Uninitialized)
{
+ // Set seekable to True by default. It changes if MEDIA_INFO_NOT_SEEKABLE is received
+ seekableChanged(true);
connect(mMediaPlayer, &AndroidMediaPlayer::bufferingChanged, this,
&QAndroidMediaPlayer::onBufferingChanged);
connect(mMediaPlayer, &AndroidMediaPlayer::info, this, &QAndroidMediaPlayer::onInfo);
@@ -102,6 +69,9 @@ QAndroidMediaPlayer::QAndroidMediaPlayer(QMediaPlayer *parent)
QAndroidMediaPlayer::~QAndroidMediaPlayer()
{
+ if (m_videoSink)
+ disconnect(m_videoSink->platformVideoSink(), nullptr, this, nullptr);
+
mMediaPlayer->disconnect();
mMediaPlayer->release();
delete mMediaPlayer;
@@ -249,43 +219,24 @@ void QAndroidMediaPlayer::updateAvailablePlaybackRanges()
qreal QAndroidMediaPlayer::playbackRate() const
{
- if (mHasPendingPlaybackRate ||
- (mState & (AndroidMediaPlayer::Initialized
- | AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted
- | AndroidMediaPlayer::Error)) == 0) {
- return mPendingPlaybackRate;
- }
-
- return mMediaPlayer->playbackRate();
+ return mCurrentPlaybackRate;
}
void QAndroidMediaPlayer::setPlaybackRate(qreal rate)
{
- if ((mState & (AndroidMediaPlayer::Initialized
- | AndroidMediaPlayer::Prepared
- | AndroidMediaPlayer::Started
- | AndroidMediaPlayer::Paused
- | AndroidMediaPlayer::PlaybackCompleted
- | AndroidMediaPlayer::Error)) == 0) {
- if (mPendingPlaybackRate != rate) {
- mPendingPlaybackRate = rate;
+ if (mState != AndroidMediaPlayer::Started) {
+ // If video isn't playing, changing speed rate may start it automatically
+ // It need to be postponed
+ if (mCurrentPlaybackRate != rate) {
+ mCurrentPlaybackRate = rate;
mHasPendingPlaybackRate = true;
Q_EMIT playbackRateChanged(rate);
}
return;
}
- bool succeeded = mMediaPlayer->setPlaybackRate(rate);
-
- if (mHasPendingPlaybackRate) {
- mHasPendingPlaybackRate = false;
- mPendingPlaybackRate = qreal(1.0);
- if (!succeeded)
- Q_EMIT playbackRateChanged(playbackRate());
- } else if (succeeded) {
+ if (mMediaPlayer->setPlaybackRate(rate)) {
+ mCurrentPlaybackRate = rate;
Q_EMIT playbackRateChanged(rate);
}
}
@@ -325,7 +276,8 @@ void QAndroidMediaPlayer::setMedia(const QUrl &mediaContent,
if (mVideoSize.isValid() && mVideoOutput)
mVideoOutput->setVideoSize(mVideoSize);
- if ((mMediaPlayer->display() == 0) && mVideoOutput)
+ if (mVideoOutput &&
+ (mMediaPlayer->display() == 0 || mVideoOutput->shouldTextureBeUpdated()))
mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
mMediaPlayer->setDataSource(QNetworkRequest(mediaContent));
mMediaPlayer->prepareAsync();
@@ -344,6 +296,9 @@ void QAndroidMediaPlayer::setVideoSink(QVideoSink *sink)
if (m_videoSink == sink)
return;
+ if (m_videoSink)
+ disconnect(m_videoSink->platformVideoSink(), nullptr, this, nullptr);
+
m_videoSink = sink;
if (!m_videoSink) {
@@ -356,16 +311,17 @@ void QAndroidMediaPlayer::setVideoSink(QVideoSink *sink)
mMediaPlayer->setDisplay(nullptr);
}
- mVideoOutput = new QAndroidTextureVideoOutput(this);
+ mVideoOutput = new QAndroidTextureVideoOutput(sink, this);
connect(mVideoOutput, &QAndroidTextureVideoOutput::readyChanged, this,
&QAndroidMediaPlayer::onVideoOutputReady);
connect(mMediaPlayer, &AndroidMediaPlayer::timedTextChanged, mVideoOutput,
&QAndroidTextureVideoOutput::setSubtitle);
- mVideoOutput->setSurface(sink);
-
if (mVideoOutput->isReady())
mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+
+ connect(m_videoSink->platformVideoSink(), &QPlatformVideoSink::rhiChanged, this, [&]()
+ { mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); });
}
void QAndroidMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
@@ -416,6 +372,14 @@ void QAndroidMediaPlayer::play()
updateAudioDevice();
+ if (mHasPendingPlaybackRate) {
+ mHasPendingPlaybackRate = false;
+ if (mMediaPlayer->setPlaybackRate(mCurrentPlaybackRate))
+ return;
+ mCurrentPlaybackRate = mMediaPlayer->playbackRate();
+ Q_EMIT playbackRateChanged(mCurrentPlaybackRate);
+ }
+
mMediaPlayer->play();
}
@@ -437,8 +401,6 @@ void QAndroidMediaPlayer::pause()
mPendingState = QMediaPlayer::PausedState;
return;
}
- if (mVideoOutput)
- mVideoOutput->renderFrame();
const qint64 currentPosition = mMediaPlayer->getCurrentPosition();
setPosition(currentPosition);
@@ -462,17 +424,16 @@ void QAndroidMediaPlayer::stop()
return;
}
+ if (mCurrentPlaybackRate != 1.)
+ // Playback rate need to by reapplied
+ mHasPendingPlaybackRate = true;
+
if (mVideoOutput)
mVideoOutput->stop();
mMediaPlayer->stop();
}
-bool QAndroidMediaPlayer::isSeekable() const
-{
- return true;
-}
-
void QAndroidMediaPlayer::onInfo(qint32 what, qint32 extra)
{
StateChangeNotifier notifier(this);
@@ -552,7 +513,9 @@ void QAndroidMediaPlayer::onError(qint32 what, qint32 extra)
setMediaStatus(QMediaPlayer::InvalidMedia);
break;
case AndroidMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN:
- errorString += QLatin1String(" (Unknown error/Insufficient resources)");
+ errorString += mMediaContent.scheme() == QLatin1String("rtsp")
+ ? QLatin1String(" (Unknown error/Insufficient resources or RTSP may not be supported)")
+ : QLatin1String(" (Unknown error/Insufficient resources)");
error = QMediaPlayer::ResourceError;
break;
}
@@ -685,7 +648,6 @@ void QAndroidMediaPlayer::onStateChanged(qint32 state)
mMediaPlayer->setDisplay(0);
if (mVideoOutput) {
mVideoOutput->stop();
- mVideoOutput->reset();
}
}
}
@@ -981,8 +943,6 @@ void QAndroidMediaPlayer::flushPendingStates()
setVolume(mPendingVolume);
if (mPendingMute != -1)
setMuted((mPendingMute == 1));
- if (mHasPendingPlaybackRate)
- setPlaybackRate(mPendingPlaybackRate);
switch (newState) {
case QMediaPlayer::PlayingState:
@@ -1035,3 +995,5 @@ void QAndroidMediaPlayer::updateTrackInfo()
}
QT_END_NAMESPACE
+
+#include "moc_qandroidmediaplayer_p.cpp"
diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h
index 9eec31dc6..dd2a3469d 100644
--- a/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h
+++ b/src/plugins/multimedia/android/mediaplayer/qandroidmediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDMEDIAPLAYERCONTROL_H
#define QANDROIDMEDIAPLAYERCONTROL_H
@@ -97,8 +61,6 @@ public:
void pause() override;
void stop() override;
- bool isSeekable() const override;
-
int trackCount(TrackType trackType) override;
QMediaMetaData trackMetaData(TrackType trackType, int streamNumber) override;
int activeTrack(TrackType trackType) override;
@@ -138,7 +100,7 @@ private:
int mPendingMute = -1;
bool mReloadingMedia = false;
int mActiveStateChangeNotifiers = 0;
- qreal mPendingPlaybackRate = 1.;
+ qreal mCurrentPlaybackRate = 1.;
bool mHasPendingPlaybackRate = false; // we need this because the rate can theoretically be negative
QMap<TrackType, QList<QAndroidMetaData>> mTracksMetadata;
diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp b/src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp
index 4765fa0ad..b01845fa7 100644
--- a/src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp
+++ b/src/plugins/multimedia/android/mediaplayer/qandroidmetadata.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidmetadata_p.h"
@@ -49,8 +13,6 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcaMetadata, "qt.multimedia.android.metadata")
-
// Genre name ordered by ID
// see: http://id3.org/id3v2.3.0#Appendix_A_-_Genre_List_from_ID3v1
static const char* qt_ID3GenreNames[] =
@@ -170,15 +132,7 @@ QLocale::Language getLocaleLanguage(const QString &language)
if (language == QLatin1String("und") || language == QStringLiteral("mis"))
return QLocale::AnyLanguage;
- QLocale locale(language);
-
- if (locale == QLocale::c()) {
- qCWarning(lcaMetadata) << "Could not parse language:" << language
- << ". It is not a valid Unicode CLDR language code.";
- return QLocale::AnyLanguage;
- }
-
- return locale.language();
+ return QLocale::codeToLanguage(language, QLocale::ISO639Part2);
}
QAndroidMetaData::QAndroidMetaData(int trackType, int androidTrackType, int androidTrackNumber,
diff --git a/src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h b/src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h
index 812edb062..1bbad92dd 100644
--- a/src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h
+++ b/src/plugins/multimedia/android/mediaplayer/qandroidmetadata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDMETADATA_H
#define QANDROIDMETADATA_H
diff --git a/src/plugins/multimedia/android/qandroidformatsinfo.cpp b/src/plugins/multimedia/android/qandroidformatsinfo.cpp
index 584c7a122..3b23340ce 100644
--- a/src/plugins/multimedia/android/qandroidformatsinfo.cpp
+++ b/src/plugins/multimedia/android/qandroidformatsinfo.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidformatsinfo_p.h"
@@ -108,7 +72,6 @@ QAndroidFormatInfo::QAndroidFormatInfo()
{
const QMediaFormat::AudioCodec aac = hasEncoder(QMediaFormat::AudioCodec::AAC);
const QMediaFormat::AudioCodec mp3 = hasEncoder(QMediaFormat::AudioCodec::MP3);
- const QMediaFormat::AudioCodec flac = hasEncoder(QMediaFormat::AudioCodec::FLAC);
const QMediaFormat::AudioCodec opus = hasEncoder(QMediaFormat::AudioCodec::Opus);
const QMediaFormat::AudioCodec vorbis = hasEncoder(QMediaFormat::AudioCodec::Vorbis);
@@ -123,11 +86,12 @@ QAndroidFormatInfo::QAndroidFormatInfo()
encoders = {
{ QMediaFormat::AAC, {aac}, {} },
{ QMediaFormat::MP3, {mp3}, {} },
- { QMediaFormat::FLAC, {flac}, {} },
- { QMediaFormat::Mpeg4Audio, {mp3, aac, flac, vorbis}, {} },
- { QMediaFormat::MPEG4, {mp3, aac, flac, vorbis}, {h264, h265, av1} },
- { QMediaFormat::Ogg, {opus, vorbis, flac}, {} },
- { QMediaFormat::Matroska, {mp3, opus, flac}, {vp8, vp9, h264, h265, av1} },
+ // FLAC encoder is not supported by the MediaRecorder used for recording
+ // { QMediaFormat::FLAC, {flac}, {} },
+ { QMediaFormat::Mpeg4Audio, {mp3, aac, vorbis}, {} },
+ { QMediaFormat::MPEG4, {mp3, aac, vorbis}, {h264, h265, av1} },
+ { QMediaFormat::Ogg, {opus, vorbis}, {} },
+ { QMediaFormat::Matroska, {mp3, opus}, {vp8, vp9, h264, h265, av1} },
// NOTE: WebM seems to be documented to supported with VP8 encoder,
// but the Camera API doesn't work with it, keep it commented for now.
// { QMediaFormat::WebM, {vorbis, opus}, {vp8, vp9} }
diff --git a/src/plugins/multimedia/android/qandroidformatsinfo_p.h b/src/plugins/multimedia/android/qandroidformatsinfo_p.h
index fc349189a..2d14ad181 100644
--- a/src/plugins/multimedia/android/qandroidformatsinfo_p.h
+++ b/src/plugins/multimedia/android/qandroidformatsinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDFORMATINFO_H
#define QANDROIDFORMATINFO_H
diff --git a/src/plugins/multimedia/android/qandroidintegration.cpp b/src/plugins/multimedia/android/qandroidintegration.cpp
index 5289f7f04..c7077e49d 100644
--- a/src/plugins/multimedia/android/qandroidintegration.cpp
+++ b/src/plugins/multimedia/android/qandroidintegration.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidintegration_p.h"
#include "qandroidglobal_p.h"
@@ -73,72 +37,60 @@ public:
QPlatformMediaIntegration* create(const QString &name) override
{
- if (name == QLatin1String("android"))
+ if (name == u"android")
return new QAndroidIntegration;
return nullptr;
}
};
+QAndroidIntegration::QAndroidIntegration() : QPlatformMediaIntegration(QLatin1String("android")) { }
-QAndroidIntegration::QAndroidIntegration()
-{
-
-}
-
-QAndroidIntegration::~QAndroidIntegration()
-{
- delete m_formatInfo;
-}
-
-QPlatformAudioDecoder *QAndroidIntegration::createAudioDecoder(QAudioDecoder *decoder)
+QMaybe<QPlatformAudioDecoder *> QAndroidIntegration::createAudioDecoder(QAudioDecoder *decoder)
{
return new QAndroidAudioDecoder(decoder);
}
-QPlatformMediaFormatInfo *QAndroidIntegration::formatInfo()
+QPlatformMediaFormatInfo *QAndroidIntegration::createFormatInfo()
{
- if (!m_formatInfo)
- m_formatInfo = new QAndroidFormatInfo();
- return m_formatInfo;
-
+ return new QAndroidFormatInfo;
}
-QPlatformMediaCaptureSession *QAndroidIntegration::createCaptureSession()
+QMaybe<QPlatformMediaCaptureSession *> QAndroidIntegration::createCaptureSession()
{
return new QAndroidMediaCaptureSession();
}
-QPlatformMediaPlayer *QAndroidIntegration::createPlayer(QMediaPlayer *player)
+QMaybe<QPlatformMediaPlayer *> QAndroidIntegration::createPlayer(QMediaPlayer *player)
{
return new QAndroidMediaPlayer(player);
}
-QPlatformCamera *QAndroidIntegration::createCamera(QCamera *camera)
+QMaybe<QPlatformCamera *> QAndroidIntegration::createCamera(QCamera *camera)
{
return new QAndroidCamera(camera);
}
-QPlatformMediaRecorder *QAndroidIntegration::createRecorder(QMediaRecorder *recorder)
+QMaybe<QPlatformMediaRecorder *> QAndroidIntegration::createRecorder(QMediaRecorder *recorder)
{
return new QAndroidMediaEncoder(recorder);
}
-QPlatformImageCapture *QAndroidIntegration::createImageCapture(QImageCapture *imageCapture)
+QMaybe<QPlatformImageCapture *> QAndroidIntegration::createImageCapture(QImageCapture *imageCapture)
{
return new QAndroidImageCapture(imageCapture);
}
-QPlatformAudioOutput *QAndroidIntegration::createAudioOutput(QAudioOutput *q)
+QMaybe<QPlatformAudioOutput *> QAndroidIntegration::createAudioOutput(QAudioOutput *q)
{
return new QAndroidAudioOutput(q);
}
-QPlatformAudioInput *QAndroidIntegration::createAudioInput(QAudioInput *audioInput)
+QMaybe<QPlatformAudioInput *> QAndroidIntegration::createAudioInput(QAudioInput *audioInput)
{
return new QAndroidAudioInput(audioInput);
}
-QPlatformVideoSink *QAndroidIntegration::createVideoSink(QVideoSink *sink)
+QMaybe<QPlatformVideoSink *> QAndroidIntegration::createVideoSink(QVideoSink *sink)
{
return new QAndroidVideoSink(sink);
}
diff --git a/src/plugins/multimedia/android/qandroidintegration_p.h b/src/plugins/multimedia/android/qandroidintegration_p.h
index 3c6dde15f..9ef5a3267 100644
--- a/src/plugins/multimedia/android/qandroidintegration_p.h
+++ b/src/plugins/multimedia/android/qandroidintegration_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDINTEGRATION_H
#define QANDROIDINTEGRATION_H
@@ -61,24 +25,22 @@ class QAndroidIntegration : public QPlatformMediaIntegration
{
public:
QAndroidIntegration();
- ~QAndroidIntegration();
- QPlatformMediaFormatInfo *formatInfo() override;
+ QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *decoder) override;
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *player) override;
+ QMaybe<QPlatformCamera *> createCamera(QCamera *camera) override;
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *recorder) override;
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *imageCapture) override;
- QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *decoder) override;
- QPlatformMediaCaptureSession *createCaptureSession() override;
- QPlatformMediaPlayer *createPlayer(QMediaPlayer *player) override;
- QPlatformCamera *createCamera(QCamera *camera) override;
- QPlatformMediaRecorder *createRecorder(QMediaRecorder *recorder) override;
- QPlatformImageCapture *createImageCapture(QImageCapture *imageCapture) override;
+ QMaybe<QPlatformAudioOutput *> createAudioOutput(QAudioOutput *q) override;
+ QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *audioInput) override;
- QPlatformAudioOutput *createAudioOutput(QAudioOutput *q) override;
- QPlatformAudioInput *createAudioInput(QAudioInput *audioInput) override;
-
- QPlatformVideoSink *createVideoSink(QVideoSink *) override;
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *) override;
QList<QCameraDevice> videoInputs() override;
- QPlatformMediaFormatInfo *m_formatInfo = nullptr;
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp b/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp
index dbdffc8bb..268434217 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp
+++ b/src/plugins/multimedia/android/wrappers/jni/androidcamera.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Ruslan Baratov
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Ruslan Baratov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidcamera_p.h"
#include "androidsurfacetexture_p.h"
@@ -57,7 +21,7 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcAndroidCamera, "qt.multimedia.android.camera")
+static Q_LOGGING_CATEGORY(lcAndroidCamera, "qt.multimedia.android.camera")
static const char QtCameraListenerClassName[] = "org/qtproject/qt/android/multimedia/QtCameraListener";
@@ -161,10 +125,9 @@ static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data)
bytesPerLine = -1;
}
- QVideoFrame frame(new QMemoryVideoBuffer(bytes, bytesPerLine),
- QVideoFrameFormat(pictureSize, qt_pixelFormatFromAndroidImageFormat(format)));
+ auto pictureFormat = qt_pixelFormatFromAndroidImageFormat(format);
- emit camera->pictureCaptured(frame);
+ emit camera->pictureCaptured(bytes, pictureFormat, pictureSize, bytesPerLine);
}
static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data,
@@ -352,7 +315,7 @@ AndroidCamera::~AndroidCamera()
AndroidCamera *AndroidCamera::open(int cameraId)
{
- if (!qt_androidRequestCameraPermission())
+ if (!qt_androidCheckCameraPermission())
return nullptr;
AndroidCameraPrivate *d = new AndroidCameraPrivate();
@@ -801,7 +764,7 @@ QJniObject AndroidCamera::getCameraObject()
int AndroidCamera::getNumberOfCameras()
{
- if (!qt_androidRequestCameraPermission())
+ if (!qt_androidCheckCameraPermission())
return 0;
return QJniObject::callStaticMethod<jint>("android/hardware/Camera",
@@ -837,6 +800,12 @@ void AndroidCamera::getCameraInfo(int id, QCameraDevicePrivate *info)
default:
break;
}
+ // Add a number to allow correct access to cameras on systems with two
+ // (and more) front/back cameras
+ if (id > 1) {
+ info->id.append(QByteArray::number(id));
+ info->description.append(QString(" %1").arg(id));
+ }
}
QVideoFrameFormat::PixelFormat AndroidCamera::QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat format)
@@ -1235,6 +1204,7 @@ bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder)
void AndroidCameraPrivate::setDisplayOrientation(int degrees)
{
m_camera.callMethod<void>("setDisplayOrientation", "(I)V", degrees);
+ m_cameraListener.callMethod<void>("setPhotoRotation", "(I)V", degrees);
}
bool AndroidCameraPrivate::isZoomSupported()
@@ -1816,3 +1786,4 @@ bool AndroidCamera::registerNativeMethods()
QT_END_NAMESPACE
#include "androidcamera.moc"
+#include "moc_androidcamera_p.cpp"
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h b/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h
index ff018335b..8375cf3b1 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h
+++ b/src/plugins/multimedia/android/wrappers/jni/androidcamera_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Ruslan Baratov
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Ruslan Baratov
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDCAMERA_H
#define ANDROIDCAMERA_H
@@ -225,7 +189,7 @@ Q_SIGNALS:
void takePictureFailed();
void pictureExposed();
- void pictureCaptured(const QVideoFrame &frame);
+ void pictureCaptured(const QByteArray &frame, QVideoFrameFormat::PixelFormat format, QSize size, int bytesPerLine);
void lastPreviewFrameFetched(const QVideoFrame &frame);
void newPreviewFrame(const QVideoFrame &frame);
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp
index 733717ae7..25e1efdb0 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidmediametadataretriever_p.h"
@@ -160,7 +124,8 @@ bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url)
auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource",
"(Landroid/content/Context;Landroid/net/Uri;)V");
env->CallVoidMethod(m_metadataRetriever.object(), methodId,
- QNativeInterface::QAndroidApplication::context(), uri.object());
+ QNativeInterface::QAndroidApplication::context().object(),
+ uri.object());
if (env.checkAndClearExceptions())
return false;
}
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h b/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h
index 69727bbd3..68e346336 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediametadataretriever_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDMEDIAMETADATARETRIEVER_H
#define ANDROIDMEDIAMETADATARETRIEVER_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp
index 1378cfbeb..91f489f9e 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidmediaplayer_p.h"
#include "androidsurfacetexture_p.h"
@@ -53,7 +17,7 @@ Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcAudio, "qt.multimedia.audio")
+static Q_LOGGING_CATEGORY(lcAudio, "qt.multimedia.audio")
AndroidMediaPlayer::AndroidMediaPlayer()
: QObject()
@@ -63,7 +27,7 @@ AndroidMediaPlayer::AndroidMediaPlayer()
const jlong id = reinterpret_cast<jlong>(this);
mMediaPlayer = QJniObject(QtAndroidMediaPlayerClassName,
"(Landroid/content/Context;J)V",
- context,
+ context.object(),
id);
mediaPlayers->append(this);
}
@@ -288,6 +252,20 @@ void AndroidMediaPlayer::unblockAudio()
mAudioBlocked = false;
}
+void AndroidMediaPlayer::startSoundStreaming(const int inputId, const int outputId)
+{
+ QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
+ "startSoundStreaming",
+ inputId,
+ outputId);
+}
+
+void AndroidMediaPlayer::stopSoundStreaming()
+{
+ QJniObject::callStaticMethod<void>(
+ "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", "stopSoundStreaming");
+}
+
bool AndroidMediaPlayer::setPlaybackRate(qreal rate)
{
if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) {
@@ -296,34 +274,7 @@ bool AndroidMediaPlayer::setPlaybackRate(qreal rate)
return false;
}
- QJniObject player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle",
- "()Landroid/media/MediaPlayer;");
- if (player.isValid()) {
- QJniObject playbackParams = player.callObjectMethod("getPlaybackParams",
- "()Landroid/media/PlaybackParams;");
- if (playbackParams.isValid()) {
- playbackParams.callObjectMethod("setSpeed", "(F)Landroid/media/PlaybackParams;",
- jfloat(rate));
- // pitch can only be > 0
- if (!qFuzzyIsNull(rate))
- playbackParams.callObjectMethod("setPitch", "(F)Landroid/media/PlaybackParams;",
- jfloat(qAbs(rate)));
-
- QJniEnvironment env;
- auto methodId = env->GetMethodID(player.objectClass(), "setPlaybackParams",
- "(Landroid/media/PlaybackParams;)V");
- env->CallVoidMethod(player.object(), methodId, playbackParams.object());
-
- if (env.checkAndClearExceptions()) {
- qWarning() << "Invalid playback rate" << rate;
- return false;
- } else {
- return true;
- }
- }
- }
-
- return false;
+ return mMediaPlayer.callMethod<jboolean>("setPlaybackRate", jfloat(rate));
}
void AndroidMediaPlayer::setDisplay(AndroidSurfaceTexture *surfaceTexture)
@@ -580,3 +531,5 @@ bool AndroidMediaPlayer::registerNativeMethods()
}
QT_END_NAMESPACE
+
+#include "moc_androidmediaplayer_p.cpp"
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer_p.h b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer_p.h
index d5cf07f9c..66095b114 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer_p.h
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDMEDIAPLAYER_H
#define ANDROIDMEDIAPLAYER_H
@@ -136,6 +100,8 @@ public:
void setDataSource(const QNetworkRequest &request);
void prepareAsync();
void setVolume(int volume);
+ static void startSoundStreaming(const int inputId, const int outputId);
+ static void stopSoundStreaming();
bool setPlaybackRate(qreal rate);
void setDisplay(AndroidSurfaceTexture *surfaceTexture);
static bool setAudioOutput(const QByteArray &deviceId);
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp
index 61b0f6df8..a3c9f4556 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidmediarecorder_p.h"
#include "androidcamera_p.h"
@@ -51,7 +15,7 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(lcMediaRecorder, "qt.multimedia.mediarecorder.android")
+static Q_LOGGING_CATEGORY(lcMediaRecorder, "qt.multimedia.mediarecorder.android")
typedef QMap<QString, QJniObject> CamcorderProfiles;
Q_GLOBAL_STATIC(CamcorderProfiles, g_camcorderProfiles)
@@ -323,7 +287,7 @@ void AndroidMediaRecorder::setOutputFile(const QString &path)
"org/qtproject/qt/android/QtNative",
"openFdObjectForContentUrl",
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Ljava/io/FileDescriptor;",
- QNativeInterface::QAndroidApplication::context(),
+ QNativeInterface::QAndroidApplication::context().object(),
QJniObject::fromString(path).object(),
QJniObject::fromString(QLatin1String("rw")).object());
@@ -369,3 +333,5 @@ bool AndroidMediaRecorder::registerNativeMethods()
}
QT_END_NAMESPACE
+
+#include "moc_androidmediarecorder_p.cpp"
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder_p.h b/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder_p.h
index 77fd3ff90..ffdbcc149 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder_p.h
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmediarecorder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDMEDIARECORDER_H
#define ANDROIDMEDIARECORDER_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp b/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp
index 1fe482f30..9606bd6bb 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidmultimediautils_p.h"
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_p.h b/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_p.h
index 8a1fd7328..ee72c3c61 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_p.h
+++ b/src/plugins/multimedia/android/wrappers/jni/androidmultimediautils_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDMULTIMEDIAUTILS_H
#define ANDROIDMULTIMEDIAUTILS_H
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp b/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp
index d3a5b56ff..c5860b265 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp
+++ b/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidsurfacetexture_p.h"
#include <QtCore/qmutex.h>
@@ -48,6 +12,8 @@ typedef QList<jlong> SurfaceTextures;
Q_GLOBAL_STATIC(SurfaceTextures, g_surfaceTextures);
Q_GLOBAL_STATIC(QMutex, g_textureMutex);
+static QAtomicInteger<quint64> indexCounter = 0u;
+
// native method for QtSurfaceTexture.java
static void notifyFrameAvailable(JNIEnv* , jobject, jlong id)
{
@@ -63,6 +29,7 @@ static void notifyFrameAvailable(JNIEnv* , jobject, jlong id)
AndroidSurfaceTexture::AndroidSurfaceTexture(quint32 texName)
: QObject()
+ , m_index(indexCounter.fetchAndAddRelaxed(1))
{
Q_STATIC_ASSERT(sizeof (jlong) >= sizeof (void *));
m_surfaceTexture = QJniObject("android/graphics/SurfaceTexture", "(I)V", jint(texName));
@@ -181,3 +148,5 @@ void AndroidSurfaceTexture::setOnFrameAvailableListener(const QJniObject &listen
}
QT_END_NAMESPACE
+
+#include "moc_androidsurfacetexture_p.cpp"
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture_p.h b/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture_p.h
index d31df972b..24581ca8d 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture_p.h
+++ b/src/plugins/multimedia/android/wrappers/jni/androidsurfacetexture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDSURFACETEXTURE_H
#define ANDROIDSURFACETEXTURE_H
@@ -79,6 +43,7 @@ public:
static bool registerNativeMethods();
+ quint64 index() const { return m_index; }
Q_SIGNALS:
void frameAvailable();
@@ -88,6 +53,7 @@ private:
QJniObject m_surfaceTexture;
QJniObject m_surface;
QJniObject m_surfaceHolder;
+ const quint64 m_index = 0;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp b/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp
index 33d15a91b..dae9516c3 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp
+++ b/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidsurfaceview_p.h"
@@ -131,7 +95,7 @@ AndroidSurfaceView::AndroidSurfaceView()
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([this] {
m_surfaceView = QJniObject("android/view/SurfaceView",
"(Landroid/content/Context;)V",
- QNativeInterface::QAndroidApplication::context());
+ QNativeInterface::QAndroidApplication::context().object());
}).waitForFinished();
Q_ASSERT(m_surfaceView.isValid());
@@ -184,3 +148,5 @@ void AndroidSurfaceView::setGeometry(int x, int y, int width, int height)
}
QT_END_NAMESPACE
+
+#include "moc_androidsurfaceview_p.cpp"
diff --git a/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview_p.h b/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview_p.h
index db767069c..e6be60ef1 100644
--- a/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview_p.h
+++ b/src/plugins/multimedia/android/wrappers/jni/androidsurfaceview_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDSURFACEVIEW_H
#define ANDROIDSURFACEVIEW_H
diff --git a/src/plugins/multimedia/darwin/CMakeLists.txt b/src/plugins/multimedia/darwin/CMakeLists.txt
index a1a0cb13c..0bbc054eb 100644
--- a/src/plugins/multimedia/darwin/CMakeLists.txt
+++ b/src/plugins/multimedia/darwin/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_internal_find_apple_system_framework(FWCoreMedia CoreMedia) # special case
qt_internal_find_apple_system_framework(FWCoreAudio CoreAudio) # special case
diff --git a/src/plugins/multimedia/darwin/avfaudiodecoder.mm b/src/plugins/multimedia/darwin/avfaudiodecoder.mm
index 85ca7c273..3191b7db0 100644
--- a/src/plugins/multimedia/darwin/avfaudiodecoder.mm
+++ b/src/plugins/multimedia/darwin/avfaudiodecoder.mm
@@ -1,99 +1,37 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfaudiodecoder_p.h"
#include <QtCore/qmutex.h>
#include <QtCore/qiodevice.h>
#include <QMimeDatabase>
+#include <QThread>
#include "private/qcoreaudioutils_p.h"
+#include <QtCore/qloggingcategory.h>
#include <AVFoundation/AVFoundation.h>
-#define MAX_BUFFERS_IN_QUEUE 10
-
QT_USE_NAMESPACE
-@interface AVFResourceReaderDelegate : NSObject <AVAssetResourceLoaderDelegate>
-{
- AVFAudioDecoder *m_decoder;
- QMutex m_mutex;
-}
-
--(void)handleNextSampleBuffer:(CMSampleBufferRef)sampleBuffer;
-
--(BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader
- shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
-
-@end
+static Q_LOGGING_CATEGORY(qLcAVFAudioDecoder, "qt.multimedia.darwin.AVFAudioDecoder")
+constexpr static int MAX_BUFFERS_IN_QUEUE = 5;
-@implementation AVFResourceReaderDelegate
-
--(id)initWithDecoder: (AVFAudioDecoder *)decoder {
- if (!(self = [super init]))
- return nil;
-
- m_decoder = decoder;
-
- return self;
-}
-
--(void)dealloc {
- m_decoder = nil;
- [super dealloc];
-}
-
--(void)handleNextSampleBuffer:(CMSampleBufferRef)sampleBuffer
+QAudioBuffer handleNextSampleBuffer(CMSampleBufferRef sampleBuffer)
{
if (!sampleBuffer)
- return;
+ return {};
// Check format
CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
if (!formatDescription)
- return;
+ return {};
const AudioStreamBasicDescription* const asbd = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription);
QAudioFormat qtFormat = CoreAudioUtils::toQAudioFormat(*asbd);
if (qtFormat.sampleFormat() == QAudioFormat::Unknown && asbd->mBitsPerChannel == 8)
qtFormat.setSampleFormat(QAudioFormat::UInt8);
if (!qtFormat.isValid())
- return;
+ return {};
// Get the required size to allocate to audioBufferList
size_t audioBufferListSize = 0;
@@ -106,7 +44,7 @@ QT_USE_NAMESPACE
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
NULL);
if (err != noErr)
- return;
+ return {};
CMBlockBufferRef blockBuffer = NULL;
AudioBufferList* audioBufferList = (AudioBufferList*) malloc(audioBufferListSize);
@@ -121,7 +59,7 @@ QT_USE_NAMESPACE
&blockBuffer);
if (err != noErr) {
free(audioBufferList);
- return;
+ return {};
}
QByteArray abuf;
@@ -137,12 +75,29 @@ QT_USE_NAMESPACE
CMTime sampleStartTime = (CMSampleBufferGetPresentationTimeStamp(sampleBuffer));
float sampleStartTimeSecs = CMTimeGetSeconds(sampleStartTime);
- QAudioBuffer audioBuffer;
- audioBuffer = QAudioBuffer(abuf, qtFormat, qint64(sampleStartTimeSecs * 1000000));
- if (!audioBuffer.isValid())
- return;
+ return QAudioBuffer(abuf, qtFormat, qint64(sampleStartTimeSecs * 1000000));
+}
+
+@interface AVFResourceReaderDelegate : NSObject <AVAssetResourceLoaderDelegate> {
+ AVFAudioDecoder *m_decoder;
+ QMutex m_mutex;
+}
+
+- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader
+ shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
+
+@end
+
+@implementation AVFResourceReaderDelegate
+
+- (id)initWithDecoder:(AVFAudioDecoder *)decoder
+{
+ if (!(self = [super init]))
+ return nil;
+
+ m_decoder = decoder;
- emit m_decoder->newAudioBuffer(audioBuffer);
+ return self;
}
-(BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader
@@ -226,6 +181,22 @@ QAudioFormat qt_format_for_audio_track(AVAssetTrack *track)
}
+struct AVFAudioDecoder::DecodingContext
+{
+ AVAssetReader *m_reader = nullptr;
+ AVAssetReaderTrackOutput *m_readerOutput = nullptr;
+
+ ~DecodingContext()
+ {
+ if (m_reader) {
+ [m_reader cancelReading];
+ [m_reader release];
+ }
+
+ [m_readerOutput release];
+ }
+};
+
AVFAudioDecoder::AVFAudioDecoder(QAudioDecoder *parent)
: QPlatformAudioDecoder(parent)
{
@@ -233,31 +204,17 @@ AVFAudioDecoder::AVFAudioDecoder(QAudioDecoder *parent)
m_decodingQueue = dispatch_queue_create("decoder_queue", DISPATCH_QUEUE_SERIAL);
m_readerDelegate = [[AVFResourceReaderDelegate alloc] initWithDecoder:this];
-
- connect(this, &AVFAudioDecoder::readyToRead, this, &AVFAudioDecoder::startReading);
- connect(this, &AVFAudioDecoder::newAudioBuffer, this, &AVFAudioDecoder::handleNewAudioBuffer);
}
AVFAudioDecoder::~AVFAudioDecoder()
{
stop();
- [m_readerOutput release];
- m_readerOutput = nil;
-
- [m_reader release];
- m_reader = nil;
-
[m_readerDelegate release];
- m_readerDelegate = nil;
-
[m_asset release];
- m_asset = nil;
- if (m_readingQueue)
- dispatch_release(m_readingQueue);
- if (m_decodingQueue)
- dispatch_release(m_decodingQueue);
+ dispatch_release(m_readingQueue);
+ dispatch_release(m_decodingQueue);
}
QUrl AVFAudioDecoder::source() const
@@ -282,7 +239,7 @@ void AVFAudioDecoder::setSource(const QUrl &fileName)
m_asset = [[AVURLAsset alloc] initWithURL:nsURL options:nil];
}
- emit sourceChanged();
+ sourceChanged();
}
QIODevice *AVFAudioDecoder::sourceDevice() const
@@ -309,76 +266,83 @@ void AVFAudioDecoder::setSourceDevice(QIODevice *device)
NSURL *nsURL = [NSURL URLWithString:urlString];
m_asset = [[AVURLAsset alloc] initWithURL:nsURL options:nil];
- [m_asset.resourceLoader setDelegate:m_readerDelegate queue:m_readingQueue];
- m_loadingSource = true;
+ // use decoding queue instead of reading queue in order to fix random stucks.
+ // Anyway, decoding queue is empty in the moment.
+ [m_asset.resourceLoader setDelegate:m_readerDelegate queue:m_decodingQueue];
}
- emit sourceChanged();
+ sourceChanged();
}
void AVFAudioDecoder::start()
{
- Q_ASSERT(!m_buffersAvailable);
- if (isDecoding())
+ if (m_decodingContext) {
+ qCDebug(qLcAVFAudioDecoder()) << "AVFAudioDecoder has been already started";
return;
-
- if (m_position != -1) {
- m_position = -1;
- emit positionChanged(-1);
}
+ positionChanged(-1);
+
if (m_device && (!m_device->isOpen() || !m_device->isReadable())) {
processInvalidMedia(QAudioDecoder::ResourceError, tr("Unable to read from specified device"));
return;
}
- [m_asset loadValuesAsynchronouslyForKeys:@[@"tracks"] completionHandler:
- ^{
- dispatch_async(m_readingQueue,
- ^{
- NSError *error = nil;
- AVKeyValueStatus status = [m_asset statusOfValueForKey:@"tracks" error:&error];
- if (status != AVKeyValueStatusLoaded) {
- if (status == AVKeyValueStatusFailed) {
- if (error.domain == NSURLErrorDomain)
- processInvalidMedia(QAudioDecoder::ResourceError, QString::fromNSString(error.localizedDescription));
- else
- processInvalidMedia(QAudioDecoder::FormatError, tr("Could not load media source's tracks"));
- }
- return;
- }
- initAssetReader();
- });
+ m_decodingContext = std::make_shared<DecodingContext>();
+ std::weak_ptr<DecodingContext> weakContext(m_decodingContext);
+
+ auto handleLoadingResult = [=]() {
+ NSError *error = nil;
+ AVKeyValueStatus status = [m_asset statusOfValueForKey:@"tracks" error:&error];
+
+ if (status == AVKeyValueStatusFailed) {
+ if (error.domain == NSURLErrorDomain)
+ processInvalidMedia(QAudioDecoder::ResourceError,
+ QString::fromNSString(error.localizedDescription));
+ else
+ processInvalidMedia(QAudioDecoder::FormatError,
+ tr("Could not load media source's tracks"));
+ } else if (status != AVKeyValueStatusLoaded) {
+ qWarning() << "Unexpected AVKeyValueStatus:" << status;
+ stop();
}
- ];
+ else {
+ initAssetReader();
+ }
+ };
- if (m_device && m_loadingSource) {
- setIsDecoding(true);
- return;
+ [m_asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ]
+ completionHandler:[=]() {
+ invokeWithDecodingContext(weakContext, handleLoadingResult);
+ }];
+}
+
+void AVFAudioDecoder::decBuffersCounter(uint val)
+{
+ if (val) {
+ QMutexLocker locker(&m_buffersCounterMutex);
+ m_buffersCounter -= val;
}
+
+ Q_ASSERT(m_buffersCounter >= 0);
+
+ m_buffersCounterCondition.wakeAll();
}
void AVFAudioDecoder::stop()
{
+ qCDebug(qLcAVFAudioDecoder()) << "stop decoding";
+
+ m_decodingContext.reset();
+ decBuffersCounter(m_cachedBuffers.size());
m_cachedBuffers.clear();
- if (m_reader)
- [m_reader cancelReading];
+ bufferAvailableChanged(false);
+ positionChanged(-1);
+ durationChanged(-1);
- if (m_buffersAvailable != 0) {
- m_buffersAvailable = 0;
- emit bufferAvailableChanged(false);
- }
- if (m_position != -1) {
- m_position = -1;
- emit positionChanged(m_position);
- }
- if (m_duration != -1) {
- m_duration = -1;
- emit durationChanged(m_duration);
- }
- setIsDecoding(false);
+ onFinished();
}
QAudioFormat AVFAudioDecoder::audioFormat() const
@@ -390,140 +354,191 @@ void AVFAudioDecoder::setAudioFormat(const QAudioFormat &format)
{
if (m_format != format) {
m_format = format;
- emit formatChanged(m_format);
+ formatChanged(m_format);
}
}
QAudioBuffer AVFAudioDecoder::read()
{
- if (!m_buffersAvailable)
+ if (m_cachedBuffers.empty())
return QAudioBuffer();
Q_ASSERT(m_cachedBuffers.size() > 0);
- QAudioBuffer buffer = m_cachedBuffers.takeFirst();
+ QAudioBuffer buffer = m_cachedBuffers.dequeue();
+ decBuffersCounter(1);
- m_position = qint64(buffer.startTime() / 1000);
- emit positionChanged(m_position);
-
- m_buffersAvailable--;
- if (!m_buffersAvailable)
- emit bufferAvailableChanged(false);
+ positionChanged(buffer.startTime() / 1000);
+ bufferAvailableChanged(!m_cachedBuffers.empty());
return buffer;
}
-bool AVFAudioDecoder::bufferAvailable() const
+void AVFAudioDecoder::processInvalidMedia(QAudioDecoder::Error errorCode,
+ const QString &errorString)
{
- return m_buffersAvailable > 0;
-}
+ qCDebug(qLcAVFAudioDecoder()) << "Invalid media. Error code:" << errorCode
+ << "Description:" << errorString;
-qint64 AVFAudioDecoder::position() const
-{
- return m_position;
-}
+ Q_ASSERT(QThread::currentThread() == thread());
-qint64 AVFAudioDecoder::duration() const
-{
- return m_duration;
+ error(int(errorCode), errorString);
+
+ // TODO: may be check if decodingCondext was changed by
+ // user's action (restart) from the emitted error.
+ // We should handle it somehow (don't run stop, print warning or etc...)
+
+ stop();
}
-void AVFAudioDecoder::processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString)
+void AVFAudioDecoder::onFinished()
{
- stop();
- emit error(int(errorCode), errorString);
+ m_decodingContext.reset();
+
+ if (isDecoding())
+ finished();
}
void AVFAudioDecoder::initAssetReader()
{
- if (!m_asset)
- return;
+ qCDebug(qLcAVFAudioDecoder()) << "Init asset reader";
+
+ Q_ASSERT(m_asset);
+ Q_ASSERT(QThread::currentThread() == thread());
NSArray<AVAssetTrack *> *tracks = [m_asset tracksWithMediaType:AVMediaTypeAudio];
if (!tracks.count) {
processInvalidMedia(QAudioDecoder::FormatError, tr("No audio tracks found"));
return;
}
- AVAssetTrack *track = [tracks objectAtIndex:0];
- // Set format
- QAudioFormat format;
- if (m_format.isValid()) {
- format = m_format;
- } else {
- format = qt_format_for_audio_track(track);
- if (!format.isValid())
- {
- processInvalidMedia(QAudioDecoder::FormatError, tr("Unsupported source format"));
- return;
- }
- }
-
- // Set duration
- qint64 duration = CMTimeGetSeconds(track.timeRange.duration) * 1000;
- if (m_duration != duration) {
- m_duration = duration;
- emit durationChanged(m_duration);
+ AVAssetTrack *track = [tracks objectAtIndex:0];
+ QAudioFormat format = m_format.isValid() ? m_format : qt_format_for_audio_track(track);
+ if (!format.isValid()) {
+ processInvalidMedia(QAudioDecoder::FormatError, tr("Unsupported source format"));
+ return;
}
- // Initialize asset reader and output
- [m_reader release];
- m_reader = nil;
- [m_readerOutput release];
- m_readerOutput = nil;
+ durationChanged(CMTimeGetSeconds(track.timeRange.duration) * 1000);
NSError *error = nil;
NSDictionary *audioSettings = av_audio_settings_for_format(format);
- m_readerOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:track outputSettings:audioSettings];
- m_reader = [[AVAssetReader alloc] initWithAsset:m_asset error:&error];
+
+ AVAssetReaderTrackOutput *readerOutput =
+ [[AVAssetReaderTrackOutput alloc] initWithTrack:track outputSettings:audioSettings];
+ AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:m_asset error:&error];
if (error) {
processInvalidMedia(QAudioDecoder::ResourceError, QString::fromNSString(error.localizedDescription));
return;
}
- if (![m_reader canAddOutput:m_readerOutput]) {
+ if (![reader canAddOutput:readerOutput]) {
processInvalidMedia(QAudioDecoder::ResourceError, tr("Failed to add asset reader output"));
return;
}
- [m_reader addOutput:m_readerOutput];
- emit readyToRead();
+ [reader addOutput:readerOutput];
+
+ Q_ASSERT(m_decodingContext);
+ m_decodingContext->m_reader = reader;
+ m_decodingContext->m_readerOutput = readerOutput;
+
+ startReading();
}
void AVFAudioDecoder::startReading()
{
- m_loadingSource = false;
+ Q_ASSERT(m_decodingContext);
+ Q_ASSERT(m_decodingContext->m_reader);
+ Q_ASSERT(QThread::currentThread() == thread());
// Prepares the receiver for obtaining sample buffers from the asset.
- if (!m_reader || ![m_reader startReading]) {
+ if (![m_decodingContext->m_reader startReading]) {
processInvalidMedia(QAudioDecoder::ResourceError, tr("Could not start reading"));
return;
}
setIsDecoding(true);
+ std::weak_ptr<DecodingContext> weakContext = m_decodingContext;
+
// Since copyNextSampleBuffer is synchronous, submit it to an async dispatch queue
// to run in a separate thread. Call the handleNextSampleBuffer "callback" on another
// thread when new audio sample is read.
- dispatch_async(m_readingQueue, ^{
- CMSampleBufferRef sampleBuffer;
- while ((sampleBuffer = [m_readerOutput copyNextSampleBuffer])) {
- dispatch_async(m_decodingQueue, ^{
- if (CMSampleBufferDataIsReady(sampleBuffer))
- [m_readerDelegate handleNextSampleBuffer:sampleBuffer];
- CFRelease(sampleBuffer);
- });
- }
- if (m_reader.status == AVAssetReaderStatusCompleted)
- emit finished();
+ auto copyNextSampleBuffer = [=]() {
+ auto decodingContext = weakContext.lock();
+ if (!decodingContext)
+ return false;
+
+ CMSampleBufferRef sampleBuffer = [decodingContext->m_readerOutput copyNextSampleBuffer];
+ if (!sampleBuffer)
+ return false;
+
+ dispatch_async(m_decodingQueue, [=]() {
+ if (!weakContext.expired() && CMSampleBufferDataIsReady(sampleBuffer)) {
+ auto audioBuffer = handleNextSampleBuffer(sampleBuffer);
+
+ if (audioBuffer.isValid())
+ invokeWithDecodingContext(weakContext,
+ [=]() { handleNewAudioBuffer(audioBuffer); });
+ }
+
+ CFRelease(sampleBuffer);
+ });
+
+ return true;
+ };
+
+ dispatch_async(m_readingQueue, [=]() {
+ qCDebug(qLcAVFAudioDecoder()) << "start reading thread";
+
+ do {
+ // Note, waiting here doesn't ensure strong contol of the counter.
+ // However, it doesn't affect the logic: the reading flow works fine
+ // even if the counter is time-to-time more than max value
+ waitUntilBuffersCounterLessMax();
+ } while (copyNextSampleBuffer());
+
+ // TODO: check m_reader.status == AVAssetReaderStatusFailed
+ invokeWithDecodingContext(weakContext, [this]() { onFinished(); });
});
}
+void AVFAudioDecoder::waitUntilBuffersCounterLessMax()
+{
+ if (m_buffersCounter >= MAX_BUFFERS_IN_QUEUE) {
+ // the check avoids extra mutex lock.
+
+ QMutexLocker locker(&m_buffersCounterMutex);
+
+ while (m_buffersCounter >= MAX_BUFFERS_IN_QUEUE)
+ m_buffersCounterCondition.wait(&m_buffersCounterMutex);
+ }
+}
+
void AVFAudioDecoder::handleNewAudioBuffer(QAudioBuffer buffer)
{
- Q_ASSERT(m_cachedBuffers.size() <= MAX_BUFFERS_IN_QUEUE);
- m_cachedBuffers.push_back(buffer);
+ m_cachedBuffers.enqueue(buffer);
+ ++m_buffersCounter;
+
+ Q_ASSERT(m_cachedBuffers.size() == m_buffersCounter);
- m_buffersAvailable++;
- Q_ASSERT(m_buffersAvailable <= MAX_BUFFERS_IN_QUEUE);
+ bufferAvailableChanged(true);
+ bufferReady();
+}
- emit bufferAvailableChanged(true);
- emit bufferReady();
+/*
+ * The method calls the passed functor in the thread of AVFAudioDecoder and guarantees that
+ * the passed decoding context is not expired. In other words, it helps avoiding all callbacks
+ * after stopping of the decoder.
+ */
+template<typename F>
+void AVFAudioDecoder::invokeWithDecodingContext(std::weak_ptr<DecodingContext> weakContext, F &&f)
+{
+ if (!weakContext.expired())
+ QMetaObject::invokeMethod(this, [=]() {
+ // strong check: compare with actual decoding context.
+ // Otherwise, the context can be temporary locked by one of dispatch queues.
+ if (auto context = weakContext.lock(); context && context == m_decodingContext)
+ f();
+ });
}
+
+#include "moc_avfaudiodecoder_p.cpp"
diff --git a/src/plugins/multimedia/darwin/avfaudiodecoder_p.h b/src/plugins/multimedia/darwin/avfaudiodecoder_p.h
index 73a8cbd38..81ef3f49e 100644
--- a/src/plugins/multimedia/darwin/avfaudiodecoder_p.h
+++ b/src/plugins/multimedia/darwin/avfaudiodecoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFAUDIODECODER_H
#define AVFAUDIODECODER_H
@@ -54,6 +18,9 @@
#include <QtMultimedia/private/qtmultimediaglobal_p.h>
#include <QObject>
#include <QtCore/qurl.h>
+#include <QWaitCondition>
+#include <QMutex>
+#include <QQueue>
#include "private/qplatformaudiodecoder_p.h"
#include "qaudiodecoder.h"
@@ -71,6 +38,8 @@ class AVFAudioDecoder : public QPlatformAudioDecoder
{
Q_OBJECT
+ struct DecodingContext;
+
public:
AVFAudioDecoder(QAudioDecoder *parent);
virtual ~AVFAudioDecoder();
@@ -88,41 +57,41 @@ public:
void setAudioFormat(const QAudioFormat &format) override;
QAudioBuffer read() override;
- bool bufferAvailable() const override;
-
- qint64 position() const override;
- qint64 duration() const override;
-private slots:
+private:
void handleNewAudioBuffer(QAudioBuffer);
void startReading();
-signals:
- void newAudioBuffer(QAudioBuffer);
- void readyToRead();
-
-private:
void processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString);
void initAssetReader();
+ void onFinished();
+
+ void waitUntilBuffersCounterLessMax();
+
+ void decBuffersCounter(uint val);
+ template<typename F>
+ void invokeWithDecodingContext(std::weak_ptr<DecodingContext> weakContext, F &&f);
+
+private:
QUrl m_source;
QIODevice *m_device = nullptr;
QAudioFormat m_format;
- int m_buffersAvailable = 0;
- QList<QAudioBuffer> m_cachedBuffers;
-
- qint64 m_position = -1;
- qint64 m_duration = -1;
-
- bool m_loadingSource = false;
+ // Use a separate counter instead of buffers queue size in order to
+ // ensure atomic access and also make mutex locking shorter
+ std::atomic<int> m_buffersCounter = 0;
+ QQueue<QAudioBuffer> m_cachedBuffers;
AVURLAsset *m_asset = nullptr;
- AVAssetReader *m_reader = nullptr;
- AVAssetReaderTrackOutput *m_readerOutput = nullptr;
+
AVFResourceReaderDelegate *m_readerDelegate = nullptr;
dispatch_queue_t m_readingQueue;
dispatch_queue_t m_decodingQueue;
+
+ std::shared_ptr<DecodingContext> m_decodingContext;
+ QMutex m_buffersCounterMutex;
+ QWaitCondition m_buffersCounterCondition;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/darwin/avfvideobuffer.mm b/src/plugins/multimedia/darwin/avfvideobuffer.mm
index ea57051b0..434080fc1 100644
--- a/src/plugins/multimedia/darwin/avfvideobuffer.mm
+++ b/src/plugins/multimedia/darwin/avfvideobuffer.mm
@@ -1,46 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfvideobuffer_p.h"
-#include <private/qrhi_p.h>
-#include <private/qrhimetal_p.h>
-#include <private/qrhigles2_p.h>
+#include <rhi/qrhi.h>
#include <CoreVideo/CVMetalTexture.h>
#include <CoreVideo/CVMetalTextureCache.h>
#include <QtGui/qopenglcontext.h>
@@ -155,7 +117,7 @@ static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f)
}
-quint64 AVFVideoBuffer::textureHandle(int plane) const
+quint64 AVFVideoBuffer::textureHandle(QRhi *, int plane) const
{
auto *textureDescription = QVideoTextureHelper::textureDescription(m_format.pixelFormat());
int bufferPlanes = CVPixelBufferGetPlaneCount(m_buffer);
@@ -172,9 +134,16 @@ quint64 AVFVideoBuffer::textureHandle(int plane) const
height = textureDescription->heightForPlane(height, plane);
// Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
+ QMutexLocker locker(sink->textureCacheMutex());
+ if (!metalCache && sink->cvMetalTextureCache)
+ metalCache = CVMetalTextureCacheRef(CFRetain(sink->cvMetalTextureCache));
+ if (!metalCache) {
+ qWarning("cannot create texture, Metal texture cache was released?");
+ return {};
+ }
auto ret = CVMetalTextureCacheCreateTextureFromImage(
kCFAllocatorDefault,
- sink->cvMetalTextureCache,
+ metalCache,
m_buffer, nil,
rhiTextureFormatToMetalFormat(textureDescription->textureFormat[plane]),
width, height,
diff --git a/src/plugins/multimedia/darwin/avfvideobuffer_p.h b/src/plugins/multimedia/darwin/avfvideobuffer_p.h
index 1958b00e4..69d7b7f45 100644
--- a/src/plugins/multimedia/darwin/avfvideobuffer_p.h
+++ b/src/plugins/multimedia/darwin/avfvideobuffer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFVIDEOBUFFER_H
#define AVFVIDEOBUFFER_H
@@ -53,6 +17,7 @@
#include <QtMultimedia/qvideoframe.h>
#include <private/qabstractvideobuffer_p.h>
+#include <private/qcore_mac_p.h>
#include <QtCore/qobject.h>
#include <QtCore/qmutex.h>
@@ -76,7 +41,7 @@ public:
MapData map(QVideoFrame::MapMode mode);
void unmap();
- virtual quint64 textureHandle(int plane) const;
+ virtual quint64 textureHandle(QRhi *, int plane) const;
QVideoFrameFormat videoFormat() const { return m_format; }
@@ -84,7 +49,7 @@ private:
AVFVideoSinkInterface *sink = nullptr;
mutable CVMetalTextureRef cvMetalTexture[3] = {};
-
+ mutable QCFType<CVMetalTextureCacheRef> metalCache;
#if defined(Q_OS_MACOS)
mutable CVOpenGLTextureRef cvOpenGLTexture = nullptr;
#elif defined(Q_OS_IOS)
diff --git a/src/plugins/multimedia/darwin/avfvideosink.mm b/src/plugins/multimedia/darwin/avfvideosink.mm
index f44a65683..f4c8bdb2e 100644
--- a/src/plugins/multimedia/darwin/avfvideosink.mm
+++ b/src/plugins/multimedia/darwin/avfvideosink.mm
@@ -1,57 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfvideosink_p.h"
-#include <private/qrhi_p.h>
-#include <private/qrhimetal_p.h>
-#include <private/qrhigles2_p.h>
+#include <rhi/qrhi.h>
#include <QtGui/qopenglcontext.h>
#include <AVFoundation/AVFoundation.h>
#import <QuartzCore/CATransaction.h>
-#if QT_HAS_INCLUDE(<AppKit/AppKit.h>)
+#if __has_include(<AppKit/AppKit.h>)
#include <AppKit/AppKit.h>
#endif
-#if QT_HAS_INCLUDE(<UIKit/UIKit.h>)
+#if __has_include(<UIKit/UIKit.h>)
#include <UIKit/UIKit.h>
#endif
@@ -91,18 +53,12 @@ void AVFVideoSink::setVideoSinkInterface(AVFVideoSinkInterface *interface)
m_interface->setRhi(m_rhi);
}
-// The OpengGL texture cache can apparently only handle single plane formats, so lets simply restrict to BGRA
-static NSDictionary* const AVF_OUTPUT_SETTINGS_OPENGL = @{
- (NSString *)kCVPixelBufferPixelFormatTypeKey: @[
- @(kCVPixelFormatType_32BGRA),
- ],
- (NSString *)kCVPixelBufferOpenGLCompatibilityKey: @true
-};
-
AVFVideoSinkInterface::~AVFVideoSinkInterface()
{
if (m_layer)
[m_layer release];
+ if (m_outputSettings)
+ [m_outputSettings release];
freeTextureCaches();
}
@@ -127,7 +83,11 @@ void AVFVideoSinkInterface::setVideoSink(AVFVideoSink *sink)
if (sink == m_sink)
return;
+ if (m_sink)
+ m_sink->setVideoSinkInterface(nullptr);
+
m_sink = sink;
+
if (m_sink) {
m_sink->setVideoSinkInterface(this);
reconfigure();
@@ -136,6 +96,7 @@ void AVFVideoSinkInterface::setVideoSink(AVFVideoSink *sink)
void AVFVideoSinkInterface::setRhi(QRhi *rhi)
{
+ QMutexLocker locker(&m_textureCacheMutex);
if (m_rhi == rhi)
return;
freeTextureCaches();
@@ -159,8 +120,6 @@ void AVFVideoSinkInterface::setRhi(QRhi *rhi)
}
} else if (rhi->backend() == QRhi::OpenGLES2) {
#if QT_CONFIG(opengl)
- setOutputSettings(AVF_OUTPUT_SETTINGS_OPENGL);
-
#ifdef Q_OS_MACOS
const auto *gl = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
@@ -195,6 +154,7 @@ void AVFVideoSinkInterface::setRhi(QRhi *rhi)
m_rhi = nullptr;
#endif // QT_CONFIG(opengl)
}
+ setOutputSettings();
}
void AVFVideoSinkInterface::setLayer(CALayer *layer)
@@ -212,9 +172,46 @@ void AVFVideoSinkInterface::setLayer(CALayer *layer)
reconfigure();
}
-void AVFVideoSinkInterface::setOutputSettings(NSDictionary *settings)
+void AVFVideoSinkInterface::setOutputSettings()
{
- m_outputSettings = settings;
+ if (m_outputSettings)
+ [m_outputSettings release];
+ m_outputSettings = nil;
+
+ // Set pixel format
+ NSDictionary *dictionary = nil;
+ if (m_rhi && m_rhi->backend() == QRhi::OpenGLES2) {
+#if QT_CONFIG(opengl)
+ dictionary = @{(NSString *)kCVPixelBufferPixelFormatTypeKey:
+ @(kCVPixelFormatType_32BGRA)
+#ifndef Q_OS_IOS // On iOS this key generates a warning about unsupported key.
+ , (NSString *)kCVPixelBufferOpenGLCompatibilityKey: @true
+#endif // Q_OS_IOS
+ };
+#endif
+ } else {
+ dictionary = @{(NSString *)kCVPixelBufferPixelFormatTypeKey:
+ @[
+ @(kCVPixelFormatType_32BGRA),
+ @(kCVPixelFormatType_32RGBA),
+ @(kCVPixelFormatType_422YpCbCr8),
+ @(kCVPixelFormatType_422YpCbCr8_yuvs),
+ @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange),
+ @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange),
+ @(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange),
+ @(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange),
+ @(kCVPixelFormatType_OneComponent8),
+ @(kCVPixelFormatType_OneComponent16),
+ @(kCVPixelFormatType_420YpCbCr8Planar),
+ @(kCVPixelFormatType_420YpCbCr8PlanarFullRange)
+ ]
+#ifndef Q_OS_IOS // This key is not supported and generates a warning.
+ , (NSString *)kCVPixelBufferMetalCompatibilityKey: @true
+#endif // Q_OS_IOS
+ };
+ }
+
+ m_outputSettings = [[NSDictionary alloc] initWithDictionary:dictionary];
}
void AVFVideoSinkInterface::updateLayerBounds()
diff --git a/src/plugins/multimedia/darwin/avfvideosink_p.h b/src/plugins/multimedia/darwin/avfvideosink_p.h
index 9191d19f1..9b66e79f2 100644
--- a/src/plugins/multimedia/darwin/avfvideosink_p.h
+++ b/src/plugins/multimedia/darwin/avfvideosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFVIDEOWINDOWCONTROL_H
#define AVFVIDEOWINDOWCONTROL_H
@@ -51,6 +15,7 @@
// We mean it.
//
+#include <QtCore/QMutex>
#include "private/qplatformvideosink_p.h"
Q_FORWARD_DECLARE_OBJC_CLASS(CALayer);
@@ -100,7 +65,9 @@ public:
virtual void reconfigure() = 0;
virtual void setRhi(QRhi *);
virtual void setLayer(CALayer *layer);
- virtual void setOutputSettings(NSDictionary *settings);
+ virtual void setOutputSettings();
+
+ QMutex *textureCacheMutex() { return &m_textureCacheMutex; }
QRhi *rhi() const { return m_rhi; }
@@ -123,6 +90,7 @@ protected:
QRhi *m_rhi = nullptr;
CALayer *m_layer = nullptr;
NSDictionary *m_outputSettings = nullptr;
+ QMutex m_textureCacheMutex;
};
diff --git a/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm b/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm
index 27261b8a0..1b2d4b15d 100644
--- a/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm
+++ b/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfcamerasession_p.h"
#include "avfaudiopreviewdelegate_p.h"
diff --git a/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_p.h b/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_p.h
index e993637dd..8fa06ef39 100644
--- a/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfaudiopreviewdelegate_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFAUDIOPREVIEWDELEGATE_P_H
#define AVFAUDIOPREVIEWDELEGATE_P_H
diff --git a/src/plugins/multimedia/darwin/camera/avfcamera.mm b/src/plugins/multimedia/darwin/camera/avfcamera.mm
index 20f7eed65..05cdbae17 100644
--- a/src/plugins/multimedia/darwin/camera/avfcamera.mm
+++ b/src/plugins/multimedia/darwin/camera/avfcamera.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfcameradebug_p.h"
#include "avfcamera_p.h"
@@ -47,91 +11,6 @@
QT_USE_NAMESPACE
-
-namespace {
-
-// All these methods to work with exposure/ISO/SS in custom mode do not support macOS.
-
-#ifdef Q_OS_IOS
-
-// Misc. helpers to check values/ranges:
-
-bool qt_check_exposure_duration(AVCaptureDevice *captureDevice, CMTime duration)
-{
- Q_ASSERT(captureDevice);
-
- AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat;
- if (!activeFormat) {
- qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to obtain capture device format";
- return false;
- }
-
- return CMTimeCompare(duration, activeFormat.minExposureDuration) != -1
- && CMTimeCompare(activeFormat.maxExposureDuration, duration) != -1;
-}
-
-bool qt_check_ISO_value(AVCaptureDevice *captureDevice, int newISO)
-{
- Q_ASSERT(captureDevice);
-
- AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat;
- if (!activeFormat) {
- qCDebug(qLcCamera) << Q_FUNC_INFO << "failed to obtain capture device format";
- return false;
- }
-
- return !(newISO < activeFormat.minISO || newISO > activeFormat.maxISO);
-}
-
-bool qt_exposure_duration_equal(AVCaptureDevice *captureDevice, qreal qDuration)
-{
- Q_ASSERT(captureDevice);
- const CMTime avDuration = CMTimeMakeWithSeconds(qDuration, captureDevice.exposureDuration.timescale);
- return !CMTimeCompare(avDuration, captureDevice.exposureDuration);
-}
-
-bool qt_iso_equal(AVCaptureDevice *captureDevice, int iso)
-{
- Q_ASSERT(captureDevice);
- return qFuzzyCompare(float(iso), captureDevice.ISO);
-}
-
-bool qt_exposure_bias_equal(AVCaptureDevice *captureDevice, qreal bias)
-{
- Q_ASSERT(captureDevice);
- return qFuzzyCompare(bias, qreal(captureDevice.exposureTargetBias));
-}
-
-// Converters:
-
-bool qt_convert_exposure_mode(AVCaptureDevice *captureDevice, QCamera::ExposureMode mode,
- AVCaptureExposureMode &avMode)
-{
- // Test if mode supported and convert.
- Q_ASSERT(captureDevice);
-
- if (mode == QCamera::ExposureAuto) {
- if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
- avMode = AVCaptureExposureModeContinuousAutoExposure;
- return true;
- }
- }
-
- if (mode == QCamera::ExposureManual) {
- if ([captureDevice isExposureModeSupported:AVCaptureExposureModeCustom]) {
- avMode = AVCaptureExposureModeCustom;
- return true;
- }
- }
-
- return false;
-}
-
-#endif // defined(Q_OS_IOS)
-
-} // Unnamed namespace.
-
-
AVFCamera::AVFCamera(QCamera *camera)
: QAVFCameraBase(camera)
{
diff --git a/src/plugins/multimedia/darwin/camera/avfcamera_p.h b/src/plugins/multimedia/darwin/camera/avfcamera_p.h
index 05faaab55..3c3e6da09 100644
--- a/src/plugins/multimedia/darwin/camera/avfcamera_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfcamera_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFCAMERA_H
#define AVFCAMERA_H
diff --git a/src/plugins/multimedia/darwin/camera/avfcameradebug_p.h b/src/plugins/multimedia/darwin/camera/avfcameradebug_p.h
index f52257bde..f93c85142 100644
--- a/src/plugins/multimedia/darwin/camera/avfcameradebug_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfcameradebug_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFDEBUG_H
#define AVFDEBUG_H
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm b/src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm
index 5d388359f..f2da5c6ed 100644
--- a/src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm
+++ b/src/plugins/multimedia/darwin/camera/avfcamerarenderer.mm
@@ -1,43 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "private/qabstractvideobuffer_p.h"
+#include "private/qcameradevice_p.h"
#include "avfcamerarenderer_p.h"
#include "avfcamerasession_p.h"
#include "avfcameraservice_p.h"
@@ -48,7 +13,7 @@
#include "qvideosink.h"
#include "qavfhelpers_p.h"
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#import <AVFoundation/AVFoundation.h>
@@ -123,6 +88,8 @@ AVFCameraRenderer::~AVFCameraRenderer()
{
[m_cameraSession->captureSession() removeOutput:m_videoDataOutput];
[m_viewfinderFramesDelegate release];
+ [m_videoDataOutput release];
+
if (m_delegateQueue)
dispatch_release(m_delegateQueue);
#ifdef Q_OS_IOS
@@ -142,13 +109,26 @@ void AVFCameraRenderer::reconfigure()
deviceOrientationChanged();
}
-void AVFCameraRenderer::setOutputSettings(NSDictionary *settings)
+void AVFCameraRenderer::setOutputSettings()
{
if (!m_videoDataOutput)
return;
- m_videoDataOutput.videoSettings = settings;
- AVFVideoSinkInterface::setOutputSettings(settings);
+ if (m_cameraSession) {
+ const auto format = m_cameraSession->cameraFormat();
+ if (format.pixelFormat() != QVideoFrameFormat::Format_Invalid)
+ setPixelFormat(format.pixelFormat(), QCameraFormatPrivate::getColorRange(format));
+ }
+
+ // If no output settings set from above,
+ // it's most likely because the rhi is OpenGL
+ // and the pixel format is not BGRA.
+ // We force this in the base class implementation
+ if (!m_outputSettings)
+ AVFVideoSinkInterface::setOutputSettings();
+
+ if (m_outputSettings)
+ m_videoDataOutput.videoSettings = m_outputSettings;
}
void AVFCameraRenderer::configureAVCaptureSession(AVFCameraSession *cameraSession)
@@ -159,7 +139,7 @@ void AVFCameraRenderer::configureAVCaptureSession(AVFCameraSession *cameraSessio
m_needsHorizontalMirroring = false;
- m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
+ m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
// Configure video output
m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
@@ -269,7 +249,8 @@ void AVFCameraRenderer::handleViewfinderFrame()
}
}
-void AVFCameraRenderer::setPixelFormat(const QVideoFrameFormat::PixelFormat pixelFormat)
+void AVFCameraRenderer::setPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat,
+ QVideoFrameFormat::ColorRange colorRange)
{
if (rhi() && rhi()->backend() == QRhi::OpenGLES2) {
if (pixelFormat != QVideoFrameFormat::Format_BGRA8888)
@@ -280,26 +261,34 @@ void AVFCameraRenderer::setPixelFormat(const QVideoFrameFormat::PixelFormat pixe
// Default to 32BGRA pixel formats on the viewfinder, in case the requested
// format can't be used (shouldn't happen unless the developers sets a wrong camera
// format on the camera).
- unsigned avPixelFormat = kCVPixelFormatType_32BGRA;
- if (!QAVFHelpers::toCVPixelFormat(pixelFormat, avPixelFormat))
+ auto cvPixelFormat = QAVFHelpers::toCVPixelFormat(pixelFormat, colorRange);
+ if (cvPixelFormat == CvPixelFormatInvalid) {
+ cvPixelFormat = kCVPixelFormatType_32BGRA;
qWarning() << "QCamera::setCameraFormat: couldn't convert requested pixel format, using ARGB32";
+ }
bool isSupported = false;
NSArray *supportedPixelFormats = m_videoDataOutput.availableVideoCVPixelFormatTypes;
for (NSNumber *currentPixelFormat in supportedPixelFormats)
{
- if ([currentPixelFormat unsignedIntValue] == avPixelFormat) {
+ if ([currentPixelFormat unsignedIntValue] == cvPixelFormat) {
isSupported = true;
break;
}
}
if (isSupported) {
- NSDictionary* outputSettings = @{
- (NSString *)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:avPixelFormat],
- (NSString *)kCVPixelBufferMetalCompatibilityKey: @true
+ NSDictionary *outputSettings = @{
+ (NSString *)
+ kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithUnsignedInt:cvPixelFormat]
+#ifndef Q_OS_IOS // On iOS this key generates a warning about 'unsupported key'.
+ ,
+ (NSString *)kCVPixelBufferMetalCompatibilityKey : @true
+#endif // Q_OS_IOS
};
- setOutputSettings(outputSettings);
+ if (m_outputSettings)
+ [m_outputSettings release];
+ m_outputSettings = [[NSDictionary alloc] initWithDictionary:outputSettings];
} else {
qWarning() << "QCamera::setCameraFormat: requested pixel format not supported. Did you use a camera format from another camera?";
}
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h b/src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h
index 8e583a6f9..57f665cd6 100644
--- a/src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfcamerarenderer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFCAMERARENDERER_H
#define AVFCAMERARENDERER_H
@@ -85,7 +49,7 @@ public:
~AVFCameraRenderer();
void reconfigure() override;
- void setOutputSettings(NSDictionary *settings) override;
+ void setOutputSettings() override;
void configureAVCaptureSession(AVFCameraSession *cameraSession);
void syncHandleViewfinderFrame(const QVideoFrame &frame);
@@ -95,7 +59,8 @@ public:
AVFCaptureFramesDelegate *captureDelegate() const;
void resetCaptureDelegate() const;
- void setPixelFormat(const QVideoFrameFormat::PixelFormat format);
+ void setPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat,
+ QVideoFrameFormat::ColorRange colorRange);
Q_SIGNALS:
void newViewfinderFrame(const QVideoFrame &frame);
diff --git a/src/plugins/multimedia/darwin/camera/avfcameraservice.mm b/src/plugins/multimedia/darwin/camera/avfcameraservice.mm
index 5c7cc5848..b25fb50a9 100644
--- a/src/plugins/multimedia/darwin/camera/avfcameraservice.mm
+++ b/src/plugins/multimedia/darwin/camera/avfcameraservice.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qvariant.h>
#include <QtCore/qdebug.h>
@@ -77,8 +41,11 @@ void AVFCameraService::setCamera(QPlatformCamera *camera)
if (m_cameraControl == control)
return;
- if (m_cameraControl)
+ if (m_cameraControl) {
+ if (m_encoder)
+ m_cameraControl->disconnect(m_encoder);
m_cameraControl->setCaptureSession(nullptr);
+ }
m_cameraControl = control;
diff --git a/src/plugins/multimedia/darwin/camera/avfcameraservice_p.h b/src/plugins/multimedia/darwin/camera/avfcameraservice_p.h
index 6b617d32c..f3ef8d08e 100644
--- a/src/plugins/multimedia/darwin/camera/avfcameraservice_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfcameraservice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFCAMERASERVICE_H
#define AVFCAMERASERVICE_H
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerasession.mm b/src/plugins/multimedia/darwin/camera/avfcamerasession.mm
index bedd34f48..52e2eadfa 100644
--- a/src/plugins/multimedia/darwin/camera/avfcamerasession.mm
+++ b/src/plugins/multimedia/darwin/camera/avfcamerasession.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfcameradebug_p.h"
#include "avfcamerasession_p.h"
@@ -50,9 +14,11 @@
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qurl.h>
#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qpermissions.h>
#include <QtCore/qpointer.h>
#include <private/qplatformaudioinput_p.h>
@@ -180,8 +146,7 @@ void AVFCameraSession::setActiveCamera(const QCameraDevice &info)
if (m_activeCameraDevice != info) {
m_activeCameraDevice = info;
- requestCameraPermissionIfNeeded();
- if (m_cameraAuthorizationStatus == AVAuthorizationStatusAuthorized)
+ if (checkCameraPermission())
updateVideoInput();
}
}
@@ -194,6 +159,11 @@ void AVFCameraSession::setCameraFormat(const QCameraFormat &format)
updateCameraFormat(format);
}
+QCameraFormat AVFCameraSession::cameraFormat() const
+{
+ return m_cameraFormat;
+}
+
void AVFCameraSession::updateCameraFormat(const QCameraFormat &format)
{
m_cameraFormat = format;
@@ -203,11 +173,8 @@ void AVFCameraSession::updateCameraFormat(const QCameraFormat &format)
return;
AVCaptureDeviceFormat *newFormat = qt_convert_to_capture_device_format(captureDevice, format);
- if (newFormat) {
+ if (newFormat)
qt_set_active_format(captureDevice, newFormat, false);
- if (m_videoOutput)
- m_videoOutput->setPixelFormat(format.pixelFormat());
- }
}
void AVFCameraSession::setVideoOutput(AVFCameraRenderer *output)
@@ -372,8 +339,7 @@ AVCaptureDevice *AVFCameraSession::createAudioCaptureDevice()
void AVFCameraSession::attachVideoInputDevice()
{
- requestCameraPermissionIfNeeded();
- if (m_cameraAuthorizationStatus != AVAuthorizationStatusAuthorized)
+ if (!checkCameraPermission())
return;
if (m_videoInput) {
@@ -399,9 +365,6 @@ void AVFCameraSession::attachVideoInputDevice()
void AVFCameraSession::attachAudioInputDevice()
{
- if (m_microphoneAuthorizationStatus != AVAuthorizationStatusAuthorized)
- return;
-
if (m_audioInput) {
[m_captureSession removeInput:m_audioInput];
[m_audioInput release];
@@ -484,8 +447,7 @@ void AVFCameraSession::updateVideoInput()
void AVFCameraSession::updateAudioInput()
{
- requestMicrophonePermissionIfNeeded();
- if (m_microphoneAuthorizationStatus != AVAuthorizationStatusAuthorized)
+ if (!checkMicrophonePermission())
return;
auto recorder = m_service->recorderControl();
@@ -528,93 +490,24 @@ void AVFCameraSession::updateVideoOutput()
m_videoOutput->setVideoSink(m_videoSink);
}
-void AVFCameraSession::requestCameraPermissionIfNeeded()
-{
- if (m_cameraAuthorizationStatus == AVAuthorizationStatusAuthorized)
- return;
-
- switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
- {
- case AVAuthorizationStatusAuthorized:
- {
- m_cameraAuthorizationStatus = AVAuthorizationStatusAuthorized;
- break;
- }
- case AVAuthorizationStatusNotDetermined:
- {
- m_cameraAuthorizationStatus = AVAuthorizationStatusNotDetermined;
- QPointer<AVFCameraSession> guard(this);
- [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (guard)
- cameraAuthorizationChanged(granted);
- });
- }];
- break;
- }
- case AVAuthorizationStatusDenied:
- case AVAuthorizationStatusRestricted:
- {
- m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
- return;
- }
- }
-}
-
-void AVFCameraSession::requestMicrophonePermissionIfNeeded()
+bool AVFCameraSession::checkCameraPermission()
{
- if (m_microphoneAuthorizationStatus == AVAuthorizationStatusAuthorized)
- return;
+ const QCameraPermission permission;
+ const bool granted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+ if (!granted)
+ qWarning() << "Access to camera not granted";
- switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio])
- {
- case AVAuthorizationStatusAuthorized:
- {
- m_microphoneAuthorizationStatus = AVAuthorizationStatusAuthorized;
- break;
- }
- case AVAuthorizationStatusNotDetermined:
- {
- m_microphoneAuthorizationStatus = AVAuthorizationStatusNotDetermined;
- QPointer<AVFCameraSession> guard(this);
- [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (guard)
- microphoneAuthorizationChanged(granted);
- });
- }];
- break;
- }
- case AVAuthorizationStatusDenied:
- case AVAuthorizationStatusRestricted:
- {
- m_microphoneAuthorizationStatus = AVAuthorizationStatusDenied;
- return;
- }
- }
+ return granted;
}
-void AVFCameraSession::cameraAuthorizationChanged(bool authorized)
+bool AVFCameraSession::checkMicrophonePermission()
{
- if (authorized) {
- m_cameraAuthorizationStatus = AVAuthorizationStatusAuthorized;
- updateVideoInput();
- updateCameraFormat(m_cameraFormat);
- } else {
- m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
- qWarning() << "User has denied access to camera";
- }
-}
+ const QMicrophonePermission permission;
+ const bool granted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+ if (!granted)
+ qWarning() << "Access to microphone not granted";
-void AVFCameraSession::microphoneAuthorizationChanged(bool authorized)
-{
- if (authorized) {
- m_microphoneAuthorizationStatus = AVAuthorizationStatusAuthorized;
- updateAudioInput();
- } else {
- m_microphoneAuthorizationStatus = AVAuthorizationStatusDenied;
- qWarning() << "User has denied access to microphone";
- }
+ return granted;
}
#include "moc_avfcamerasession_p.cpp"
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerasession_p.h b/src/plugins/multimedia/darwin/camera/avfcamerasession_p.h
index c1a5e69a4..76e31ab48 100644
--- a/src/plugins/multimedia/darwin/camera/avfcamerasession_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfcamerasession_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFCAMERASESSION_H
#define AVFCAMERASESSION_H
@@ -80,6 +44,7 @@ public:
void setActiveCamera(const QCameraDevice &info);
void setCameraFormat(const QCameraFormat &format);
+ QCameraFormat cameraFormat() const;
AVFCameraRenderer *videoOutput() const { return m_videoOutput; }
AVCaptureAudioDataOutput *audioOutput() const { return m_audioOutput; }
@@ -115,9 +80,6 @@ public Q_SLOTS:
void processSessionStarted();
void processSessionStopped();
- void cameraAuthorizationChanged(bool authorized);
- void microphoneAuthorizationChanged(bool authorized);
-
Q_SIGNALS:
void readyToConfigureConnections();
void activeChanged(bool);
@@ -136,8 +98,8 @@ private:
AVCaptureDevice *createAudioCaptureDevice();
void attachVideoInputDevice();
void attachAudioInputDevice();
- void requestCameraPermissionIfNeeded();
- void requestMicrophonePermissionIfNeeded();
+ bool checkCameraPermission();
+ bool checkMicrophonePermission();
bool applyImageEncoderSettings();
@@ -163,9 +125,6 @@ private:
bool m_inputMuted = false;
FourCharCode m_defaultCodec;
-
- AVAuthorizationStatus m_cameraAuthorizationStatus = AVAuthorizationStatusNotDetermined;
- AVAuthorizationStatus m_microphoneAuthorizationStatus = AVAuthorizationStatusNotDetermined;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerautility.mm b/src/plugins/multimedia/darwin/camera/avfcamerautility.mm
index 614b99e06..0306a31ef 100644
--- a/src/plugins/multimedia/darwin/camera/avfcamerautility.mm
+++ b/src/plugins/multimedia/darwin/camera/avfcamerautility.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfcamerautility_p.h"
#include "avfcameradebug_p.h"
@@ -43,6 +7,7 @@
#include <QtCore/qvector.h>
#include <QtCore/qpair.h>
#include <private/qmultimediautils_p.h>
+#include <private/qcameradevice_p.h>
#include "avfvideobuffer_p.h"
#include "qavfhelpers_p.h"
@@ -105,9 +70,9 @@ struct ByResolution
}
};
-struct FormatHasNoFPSRange : std::unary_function<AVCaptureDeviceFormat *, bool>
+struct FormatHasNoFPSRange
{
- bool operator() (AVCaptureDeviceFormat *format)
+ bool operator() (AVCaptureDeviceFormat *format) const
{
Q_ASSERT(format);
return !format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count;
@@ -131,28 +96,46 @@ Float64 qt_find_min_framerate_distance(AVCaptureDeviceFormat *format, Float64 fp
} // Unnamed namespace.
-AVCaptureDeviceFormat *qt_convert_to_capture_device_format(AVCaptureDevice *captureDevice,
- const QCameraFormat &cameraFormat)
+AVCaptureDeviceFormat *
+qt_convert_to_capture_device_format(AVCaptureDevice *captureDevice,
+ const QCameraFormat &cameraFormat,
+ const std::function<bool(uint32_t)> &cvFormatValidator)
{
+ const auto cameraFormatPrivate = QCameraFormatPrivate::handle(cameraFormat);
+ if (!cameraFormatPrivate)
+ return nil;
+
+ const auto requiredCvPixFormat = QAVFHelpers::toCVPixelFormat(cameraFormatPrivate->pixelFormat,
+ cameraFormatPrivate->colorRange);
+
+ if (requiredCvPixFormat == CvPixelFormatInvalid)
+ return nil;
+
AVCaptureDeviceFormat *newFormat = nil;
+ Float64 newFormatMaxFrameRate = {};
NSArray<AVCaptureDeviceFormat *> *formats = captureDevice.formats;
for (AVCaptureDeviceFormat *format in formats) {
CMFormatDescriptionRef formatDesc = format.formatDescription;
CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDesc);
- FourCharCode formatCodec = CMVideoFormatDescriptionGetCodecType(formatDesc);
- if (QAVFHelpers::fromCVPixelFormat(formatCodec) == cameraFormat.pixelFormat()
- && cameraFormat.resolution().width() == dim.width
- && cameraFormat.resolution().height() == dim.height) {
- for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
- if (frameRateRange.minFrameRate >= cameraFormat.minFrameRate()
- && frameRateRange.maxFrameRate <= cameraFormat.maxFrameRate()) {
- newFormat = format;
- break;
- }
+ FourCharCode cvPixFormat = CMVideoFormatDescriptionGetCodecType(formatDesc);
+
+ if (cvPixFormat != requiredCvPixFormat)
+ continue;
+
+ if (cameraFormatPrivate->resolution != QSize(dim.width, dim.height))
+ continue;
+
+ if (cvFormatValidator && !cvFormatValidator(cvPixFormat))
+ continue;
+
+ for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
+ if (frameRateRange.minFrameRate >= cameraFormatPrivate->minFrameRate
+ && frameRateRange.maxFrameRate <= cameraFormatPrivate->maxFrameRate
+ && newFormatMaxFrameRate < frameRateRange.maxFrameRate) {
+ newFormat = format;
+ newFormatMaxFrameRate = frameRateRange.maxFrameRate;
}
}
- if (newFormat)
- break;
}
return newFormat;
}
@@ -258,13 +241,10 @@ QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format)
if (!res.width || !resPAR.width)
return QSize();
- int n, d;
- qt_real_to_fraction(resPAR.width > res.width
- ? res.width / qreal(resPAR.width)
- : resPAR.width / qreal(res.width),
- &n, &d);
+ auto frac = qRealToFraction(resPAR.width > res.width ? res.width / qreal(resPAR.width)
+ : resPAR.width / qreal(res.width));
- return QSize(n, d);
+ return QSize(frac.numerator, frac.denominator);
}
AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice,
@@ -520,9 +500,8 @@ CMTime qt_adjusted_frame_duration(AVFrameRateRange *range, qreal fps)
if (fps >= range.maxFrameRate)
return range.minFrameDuration;
- int n, d;
- qt_real_to_fraction(1. / fps, &n, &d);
- return CMTimeMake(n, d);
+ auto frac = qRealToFraction(1. / fps);
+ return CMTimeMake(frac.numerator, frac.denominator);
}
void qt_set_framerate_limits(AVCaptureDevice *captureDevice, qreal minFPS, qreal maxFPS)
diff --git a/src/plugins/multimedia/darwin/camera/avfcamerautility_p.h b/src/plugins/multimedia/darwin/camera/avfcamerautility_p.h
index cdfff4905..b5c9e9bda 100644
--- a/src/plugins/multimedia/darwin/camera/avfcamerautility_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfcamerautility_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFCAMERAUTILITY_H
#define AVFCAMERAUTILITY_H
@@ -167,8 +131,9 @@ private:
typedef QPair<qreal, qreal> AVFPSRange;
AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection);
-AVCaptureDeviceFormat *qt_convert_to_capture_device_format(AVCaptureDevice *captureDevice,
- const QCameraFormat &format);
+AVCaptureDeviceFormat *qt_convert_to_capture_device_format(
+ AVCaptureDevice *captureDevice, const QCameraFormat &format,
+ const std::function<bool(uint32_t)> &cvFormatValidator = nullptr);
QList<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captureDevice,
FourCharCode preferredFormat);
QSize qt_device_format_resolution(AVCaptureDeviceFormat *format);
diff --git a/src/plugins/multimedia/darwin/camera/avfimagecapture.mm b/src/plugins/multimedia/darwin/camera/avfimagecapture.mm
index 8aab1b605..07988f3e2 100644
--- a/src/plugins/multimedia/darwin/camera/avfimagecapture.mm
+++ b/src/plugins/multimedia/darwin/camera/avfimagecapture.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfcameradebug_p.h"
#include "avfimagecapture_p.h"
diff --git a/src/plugins/multimedia/darwin/camera/avfimagecapture_p.h b/src/plugins/multimedia/darwin/camera/avfimagecapture_p.h
index ef11bd70f..0714fa3cc 100644
--- a/src/plugins/multimedia/darwin/camera/avfimagecapture_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfimagecapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFCAMERAIMAGECAPTURE_H
#define AVFCAMERAIMAGECAPTURE_H
diff --git a/src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm b/src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm
index 1cd9d4cf0..37fc69926 100644
--- a/src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm
+++ b/src/plugins/multimedia/darwin/camera/avfmediaassetwriter.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfmediaencoder_p.h"
#include "avfcamerarenderer_p.h"
diff --git a/src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h b/src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h
index 6691b66a1..8fe3e8522 100644
--- a/src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfmediaassetwriter_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFMEDIAASSETWRITER_H
#define AVFMEDIAASSETWRITER_H
diff --git a/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm b/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm
index ba40393e8..3fbc57995 100644
--- a/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm
+++ b/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfmediaencoder_p.h"
@@ -52,11 +16,14 @@
#include "private/qmediarecorder_p.h"
#include "qdarwinformatsinfo_p.h"
#include "private/qplatformaudiooutput_p.h"
+#include <private/qplatformaudioinput_p.h>
#include <QtCore/qmath.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmimetype.h>
+#include <private/qcoreaudioutils_p.h>
+
QT_USE_NAMESPACE
namespace {
@@ -135,7 +102,7 @@ void AVFMediaEncoder::updateDuration(qint64 duration)
durationChanged(m_duration);
}
-static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettings)
+static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettings, const QAudioFormat &format)
{
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
@@ -219,22 +186,36 @@ static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettin
}
}
}
+
+ // if channel count is provided and it's bigger than 2
+ // provide a supported channel layout
+ if (isChannelCountSupported && channelCount > 2) {
+ AudioChannelLayout channelLayout;
+ memset(&channelLayout, 0, sizeof(AudioChannelLayout));
+ auto channelLayoutTags = qt_supported_channel_layout_tags_for_format(codecId, channelCount);
+ if (channelLayoutTags.size()) {
+ channelLayout.mChannelLayoutTag = channelLayoutTags.first();
+ [settings setObject:[NSData dataWithBytes: &channelLayout length: sizeof(channelLayout)] forKey:AVChannelLayoutKey];
+ } else {
+ isChannelCountSupported = false;
+ }
+ }
+
+ if (isChannelCountSupported)
+ [settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey];
}
- if (isChannelCountSupported && channelCount > 2) {
- AudioChannelLayout channelLayout;
- memset(&channelLayout, 0, sizeof(AudioChannelLayout));
- auto channelLayoutTags = qt_supported_channel_layout_tags_for_format(codecId, channelCount);
- if (channelLayoutTags.size()) {
- channelLayout.mChannelLayoutTag = channelLayoutTags.first();
- [settings setObject:[NSData dataWithBytes: &channelLayout length: sizeof(channelLayout)] forKey:AVChannelLayoutKey];
+ if (!isChannelCountSupported) {
+ // fallback to providing channel layout if channel count is not specified or supported
+ UInt32 size = 0;
+ if (format.isValid()) {
+ auto layout = CoreAudioUtils::toAudioChannelLayout(format, &size);
+ [settings setObject:[NSData dataWithBytes:layout.get() length:sizeof(AudioChannelLayout)] forKey:AVChannelLayoutKey];
} else {
- isChannelCountSupported = false;
+ // finally default to setting channel count to 1
+ [settings setObject:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
}
}
- if (!isChannelCountSupported)
- channelCount = 2;
- [settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey];
if (codecId == kAudioFormatAppleLossless)
[settings setObject:[NSNumber numberWithInt:24] forKey:AVEncoderBitDepthHintKey];
@@ -421,7 +402,9 @@ void AVFMediaEncoder::applySettings(QMediaEncoderSettings &settings)
AVFCameraSession *session = m_service->session();
// audio settings
- m_audioSettings = avfAudioSettings(settings);
+ const auto audioInput = m_service->audioInput();
+ const QAudioFormat audioFormat = audioInput ? audioInput->device.preferredFormat() : QAudioFormat();
+ m_audioSettings = avfAudioSettings(settings, audioFormat);
if (m_audioSettings)
[m_audioSettings retain];
@@ -496,7 +479,7 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings)
if (!cameraControl && !audioInput) {
qWarning() << Q_FUNC_INFO << "Cannot record without any inputs";
- Q_EMIT error(QMediaRecorder::ResourceError, tr("No inputs specified"));
+ updateError(QMediaRecorder::ResourceError, tr("No inputs specified"));
return;
}
@@ -508,8 +491,8 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings)
if (!audioOnly) {
if (!cameraControl || !cameraControl->isActive()) {
qCDebug(qLcCamera) << Q_FUNC_INFO << "can not start record while camera is not active";
- Q_EMIT error(QMediaRecorder::ResourceError,
- QMediaRecorderPrivate::msgFailedStartRecording());
+ updateError(QMediaRecorder::ResourceError,
+ QMediaRecorderPrivate::msgFailedStartRecording());
return;
}
}
@@ -523,13 +506,13 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings)
NSURL *nsFileURL = fileURL.toNSURL();
if (!nsFileURL) {
qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL;
- Q_EMIT error(QMediaRecorder::ResourceError, tr("Invalid output file URL"));
+ updateError(QMediaRecorder::ResourceError, tr("Invalid output file URL"));
return;
}
if (!qt_is_writable_file_URL(nsFileURL)) {
qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL
<< "(the location is not writable)";
- Q_EMIT error(QMediaRecorder::ResourceError, tr("Non-writeable file location"));
+ updateError(QMediaRecorder::ResourceError, tr("Non-writeable file location"));
return;
}
if (qt_file_exists(nsFileURL)) {
@@ -537,7 +520,7 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings)
// Objective-C exception, which is not good at all.
qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL
<< "(file already exists)";
- Q_EMIT error(QMediaRecorder::ResourceError, tr("File already exists"));
+ updateError(QMediaRecorder::ResourceError, tr("File already exists"));
return;
}
@@ -572,8 +555,7 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings)
[m_writer start];
} else {
[session startRunning];
- Q_EMIT error(QMediaRecorder::FormatError,
- QMediaRecorderPrivate::msgFailedStartRecording());
+ updateError(QMediaRecorder::FormatError, QMediaRecorderPrivate::msgFailedStartRecording());
}
}
@@ -638,7 +620,8 @@ void AVFMediaEncoder::assetWriterFinished()
if (session->audioPreviewDelegate()) {
[session->audioPreviewDelegate() resetAudioPreviewDelegate];
}
- [session->captureSession() startRunning];
+ if (session->videoOutput() || session->audioPreviewDelegate())
+ [session->captureSession() startRunning];
}
m_state = QMediaRecorder::StoppedState;
@@ -648,7 +631,7 @@ void AVFMediaEncoder::assetWriterFinished()
void AVFMediaEncoder::assetWriterError(QString err)
{
- Q_EMIT error(QMediaRecorder::FormatError, err);
+ updateError(QMediaRecorder::FormatError, err);
if (m_state != QMediaRecorder::StoppedState)
stopWriter();
}
diff --git a/src/plugins/multimedia/darwin/camera/avfmediaencoder_p.h b/src/plugins/multimedia/darwin/camera/avfmediaencoder_p.h
index eac5a0282..23aced325 100644
--- a/src/plugins/multimedia/darwin/camera/avfmediaencoder_p.h
+++ b/src/plugins/multimedia/darwin/camera/avfmediaencoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFMEDIAENCODER_H
#define AVFMEDIAENCODER_H
diff --git a/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm b/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
index db7f4f8d3..9d99de0b9 100644
--- a/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
+++ b/src/plugins/multimedia/darwin/camera/qavfcamerabase.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfcameradebug_p.h"
#include "qavfcamerabase_p.h"
@@ -43,6 +7,8 @@
#include <private/qcameradevice_p.h>
#include "qavfhelpers_p.h"
#include <private/qplatformmediaintegration_p.h>
+#include <QtCore/qset.h>
+#include <QtCore/qsystemdetection.h>
QT_USE_NAMESPACE
@@ -54,29 +20,6 @@ namespace {
// Misc. helpers to check values/ranges:
-bool qt_check_ISO_conversion(float isoValue)
-{
- if (isoValue >= std::numeric_limits<int>::max())
- return false;
- if (isoValue <= std::numeric_limits<int>::min())
- return false;
- return true;
-}
-
-bool qt_check_ISO_range(AVCaptureDeviceFormat *format)
-{
- // Qt is using int for ISO, AVFoundation - float. It looks like the ISO range
- // at the moment can be represented by int (it's max - min > 100, etc.).
- Q_ASSERT(format);
- if (format.maxISO - format.minISO < 1.) {
- // ISO is in some strange units?
- return false;
- }
-
- return qt_check_ISO_conversion(format.minISO)
- && qt_check_ISO_conversion(format.maxISO);
-}
-
bool qt_check_exposure_duration(AVCaptureDevice *captureDevice, CMTime duration)
{
Q_ASSERT(captureDevice);
@@ -195,17 +138,64 @@ void QAVFVideoDevices::updateCameraDevices()
QList<QCameraDevice> cameras;
- AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
+ // List of all capture device types that we want to discover. Seems that this is the
+ // only way to discover all types. This filter is mandatory and has no "unspecified"
+ // option like AVCaptureDevicePosition(Unspecified) has. Order of the list is important
+ // because discovered devices will be in the same order and we want the first one found
+ // to be our default device.
+ NSArray *discoveryDevices = @[
+#ifdef Q_OS_IOS
+ AVCaptureDeviceTypeBuiltInTripleCamera, // We always prefer triple camera.
+ AVCaptureDeviceTypeBuiltInDualCamera, // If triple is not available, we prefer
+ // dual with wide + tele lens.
+ AVCaptureDeviceTypeBuiltInDualWideCamera, // Dual with wide and ultrawide is still
+ // better than single.
+#endif
+ AVCaptureDeviceTypeBuiltInWideAngleCamera, // This is the most common single camera type.
+ // We prefer that over tele and ultra-wide.
+#ifdef Q_OS_IOS
+ AVCaptureDeviceTypeBuiltInTelephotoCamera, // Cannot imagine how, but if only tele and
+ // ultrawide are available, we prefer tele.
+ AVCaptureDeviceTypeBuiltInUltraWideCamera,
+#endif
+ ];
+
+#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_14_0, __IPHONE_17_0, __TVOS_NA, __WATCHOS_NA)
+ if (@available(macOS 14, iOS 17, *)) {
+ discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
+ AVCaptureDeviceTypeExternal,
+ AVCaptureDeviceTypeContinuityCamera
+ ]];
+ } else
+#endif
+ {
+#ifdef Q_OS_MACOS
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_DEPRECATED
+ discoveryDevices = [discoveryDevices arrayByAddingObjectsFromArray: @[
+ AVCaptureDeviceTypeExternalUnknown
+ ]];
+ QT_WARNING_POP
+#endif
+ }
+ // Create discovery session to discover all possible camera types of the system.
+ // Both "hard" and "soft" types.
+ AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
+ discoverySessionWithDeviceTypes:discoveryDevices
+ mediaType:AVMediaTypeVideo
+ position:AVCaptureDevicePositionUnspecified];
+ NSArray<AVCaptureDevice *> *videoDevices = discoverySession.devices;
for (AVCaptureDevice *device in videoDevices) {
-
- QCameraDevicePrivate *info = new QCameraDevicePrivate;
- if (defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID])
+ auto info = std::make_unique<QCameraDevicePrivate>();
+ if ([videoDevices[0].uniqueID isEqualToString:device.uniqueID])
info->isDefault = true;
info->id = QByteArray([[device uniqueID] UTF8String]);
info->description = QString::fromNSString([device localizedName]);
+ qCDebug(qLcCamera) << "Handling camera info" << info->description
+ << (info->isDefault ? "(default)" : "");
+
QSet<QSize> photoResolutions;
QList<QCameraFormat> videoFormats;
@@ -222,9 +212,13 @@ void QAVFVideoDevices::updateCameraDevices()
auto encoding = CMVideoFormatDescriptionGetCodecType(format.formatDescription);
auto pixelFormat = QAVFHelpers::fromCVPixelFormat(encoding);
+ auto colorRange = QAVFHelpers::colorRangeForCVPixelFormat(encoding);
// Ignore pixel formats we can't handle
- if (pixelFormat == QVideoFrameFormat::Format_Invalid)
+ if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
+ qCDebug(qLcCamera) << "ignore camera CV format" << encoding
+ << "as no matching video format found";
continue;
+ }
for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) {
if (frameRateRange.minFrameRate < minFrameRate)
@@ -244,29 +238,30 @@ void QAVFVideoDevices::updateCameraDevices()
photoResolutions.insert(hrRes);
#endif
- auto *f = new QCameraFormatPrivate{
- QSharedData(),
- pixelFormat,
- resolution,
- minFrameRate,
- maxFrameRate
- };
+ qCDebug(qLcCamera) << "Add camera format. pixelFormat:" << pixelFormat
+ << "colorRange:" << colorRange << "cvPixelFormat" << encoding
+ << "resolution:" << resolution << "frameRate: [" << minFrameRate
+ << maxFrameRate << "]";
+
+ auto *f = new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution,
+ minFrameRate, maxFrameRate, colorRange };
videoFormats << f->create();
}
if (videoFormats.isEmpty()) {
// skip broken cameras without valid formats
- delete info;
+ qCWarning(qLcCamera())
+ << "Skip camera" << info->description << "without supported formats";
continue;
}
info->videoFormats = videoFormats;
info->photoResolutions = photoResolutions.values();
- cameras.append(info->create());
+ cameras.append(info.release()->create());
}
if (cameras != m_cameraDevices) {
m_cameraDevices = cameras;
- videoInputsChanged();
+ emit videoInputsChanged();
}
}
@@ -515,7 +510,7 @@ void QAVFCameraBase::updateCameraConfiguration()
}
minimumZoomFactorChanged(captureDevice.minAvailableVideoZoomFactor);
- maximumZoomFactorChanged(captureDevice.maxAvailableVideoZoomFactor);
+ maximumZoomFactorChanged(captureDevice.activeFormat.videoMaxZoomFactor);
captureDevice.videoZoomFactor = zoomFactor();
@@ -629,7 +624,8 @@ void QAVFCameraBase::zoomTo(float factor, float rate)
if (!captureDevice || !captureDevice.activeFormat)
return;
- factor = qBound(captureDevice.minAvailableVideoZoomFactor, factor, captureDevice.maxAvailableVideoZoomFactor);
+ factor = qBound(captureDevice.minAvailableVideoZoomFactor, factor,
+ captureDevice.activeFormat.videoMaxZoomFactor);
const AVFConfigurationLock lock(captureDevice);
if (!lock) {
@@ -637,10 +633,10 @@ void QAVFCameraBase::zoomTo(float factor, float rate)
return;
}
- if (rate < 0)
+ if (rate <= 0)
captureDevice.videoZoomFactor = factor;
- else
- [AVCaptureDevice rampToVideoZoomFactor:factor withRate:rate];
+ else
+ [captureDevice rampToVideoZoomFactor:factor withRate:rate];
#endif
}
@@ -687,14 +683,10 @@ bool QAVFCameraBase::isFlashReady() const
if (!isFlashModeSupported(flashMode()))
return false;
-#ifdef Q_OS_IOS
// AVCaptureDevice's docs:
// "The flash may become unavailable if, for example,
// the device overheats and needs to cool off."
return [captureDevice isFlashAvailable];
-#endif
-
- return true;
}
void QAVFCameraBase::setTorchMode(QCamera::TorchMode mode)
@@ -784,42 +776,55 @@ void QAVFCameraBase::applyFlashSettings()
return;
}
-
const AVFConfigurationLock lock(captureDevice);
if (captureDevice.hasFlash) {
- auto mode = flashMode();
+ const auto mode = flashMode();
+
+ auto setAvFlashModeSafe = [&captureDevice](AVCaptureFlashMode avFlashMode) {
+ // Note, in some cases captureDevice.hasFlash == false even though
+ // no there're no supported flash modes.
+ if ([captureDevice isFlashModeSupported:avFlashMode])
+ captureDevice.flashMode = avFlashMode;
+ else
+ qCDebug(qLcCamera) << "Attempt to setup unsupported flash mode " << avFlashMode;
+ };
+
if (mode == QCamera::FlashOff) {
- captureDevice.flashMode = AVCaptureFlashModeOff;
+ setAvFlashModeSafe(AVCaptureFlashModeOff);
} else {
-#ifdef Q_OS_IOS
- if (![captureDevice isFlashAvailable]) {
+ if ([captureDevice isFlashAvailable]) {
+ if (mode == QCamera::FlashOn)
+ setAvFlashModeSafe(AVCaptureFlashModeOn);
+ else if (mode == QCamera::FlashAuto)
+ setAvFlashModeSafe(AVCaptureFlashModeAuto);
+ } else {
qCDebug(qLcCamera) << Q_FUNC_INFO << "flash is not available at the moment";
- return;
}
-#endif
- if (mode == QCamera::FlashOn)
- captureDevice.flashMode = AVCaptureFlashModeOn;
- else if (mode == QCamera::FlashAuto)
- captureDevice.flashMode = AVCaptureFlashModeAuto;
}
}
if (captureDevice.hasTorch) {
- auto mode = torchMode();
+ const auto mode = torchMode();
+
+ auto setAvTorchModeSafe = [&captureDevice](AVCaptureTorchMode avTorchMode) {
+ if ([captureDevice isTorchModeSupported:avTorchMode])
+ captureDevice.torchMode = avTorchMode;
+ else
+ qCDebug(qLcCamera) << "Attempt to setup unsupported torch mode " << avTorchMode;
+ };
+
if (mode == QCamera::TorchOff) {
- captureDevice.torchMode = AVCaptureTorchModeOff;
+ setAvTorchModeSafe(AVCaptureTorchModeOff);
} else {
-#ifdef Q_OS_IOS
- if (![captureDevice isTorchAvailable]) {
+ if ([captureDevice isTorchAvailable]) {
+ if (mode == QCamera::TorchOn)
+ setAvTorchModeSafe(AVCaptureTorchModeOn);
+ else if (mode == QCamera::TorchAuto)
+ setAvTorchModeSafe(AVCaptureTorchModeAuto);
+ } else {
qCDebug(qLcCamera) << Q_FUNC_INFO << "torch is not available at the moment";
- return;
}
-#endif
- if (mode == QCamera::TorchOn)
- captureDevice.torchMode = AVCaptureTorchModeOn;
- else if (mode == QCamera::TorchAuto)
- captureDevice.torchMode = AVCaptureTorchModeAuto;
}
}
}
diff --git a/src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h b/src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h
index 3b68cd778..1ad3ba250 100644
--- a/src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h
+++ b/src/plugins/multimedia/darwin/camera/qavfcamerabase_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAVFCAMERABASE_H
#define QAVFCAMERABASE_H
diff --git a/src/plugins/multimedia/darwin/common/avfmetadata.mm b/src/plugins/multimedia/darwin/common/avfmetadata.mm
index 070756527..da07f69c6 100644
--- a/src/plugins/multimedia/darwin/common/avfmetadata.mm
+++ b/src/plugins/multimedia/darwin/common/avfmetadata.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfmetadata_p.h"
#include <qdarwinformatsinfo_p.h>
@@ -49,7 +13,7 @@
#include <QImage>
#include <QtMultimedia/qvideoframe.h>
-#if QT_HAS_INCLUDE(<AppKit/AppKit.h>)
+#if __has_include(<AppKit/AppKit.h>)
#include <AppKit/AppKit.h>
#endif
@@ -297,7 +261,7 @@ QMediaMetaData AVFMetaData::fromAssetTrack(AVAssetTrack *asset)
if ([asset.mediaType isEqualToString:AVMediaTypeVideo]) {
// add orientation
if (metadata.value(QMediaMetaData::Orientation).isNull()) {
- QVideoFrame::RotationAngle angle = QVideoFrame::Rotation0;
+ QtVideo::Rotation angle = QtVideo::Rotation::None;
bool mirrored;
AVFMediaPlayer::videoOrientationForAssetTrack(asset, angle, mirrored);
Q_UNUSED(mirrored);
diff --git a/src/plugins/multimedia/darwin/common/avfmetadata_p.h b/src/plugins/multimedia/darwin/common/avfmetadata_p.h
index e983be531..d1cb2e7e8 100644
--- a/src/plugins/multimedia/darwin/common/avfmetadata_p.h
+++ b/src/plugins/multimedia/darwin/common/avfmetadata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFMEDIAPLAYERMETADATACONTROL_H
#define AVFMEDIAPLAYERMETADATACONTROL_H
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm b/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm
index 64b625f0e..8c6561f37 100644
--- a/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfdisplaylink_p.h"
#include <QtCore/qcoreapplication.h>
@@ -239,3 +203,5 @@ bool AVFDisplayLink::event(QEvent *event)
}
return QObject::event(event);
}
+
+#include "moc_avfdisplaylink_p.cpp"
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink_p.h b/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink_p.h
index 6b95e1e07..c4eb504a5 100644
--- a/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink_p.h
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfdisplaylink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFDISPLAYLINK_H
#define AVFDISPLAYLINK_H
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm b/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm
index 86b7ce0a2..b6d9622ac 100644
--- a/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfmediaplayer_p.h"
#include "avfvideorenderercontrol_p.h"
@@ -127,6 +91,9 @@ static void *AVFMediaPlayerObserverCurrentItemDurationObservationContext = &AVFM
- (void) setURL:(NSURL *)url mimeType:(NSString *)mimeType
{
+ if (!m_session)
+ return;
+
[m_mimeType release];
m_mimeType = [mimeType retain];
@@ -140,23 +107,29 @@ static void *AVFMediaPlayerObserverCurrentItemDurationObservationContext = &AVFM
// use __block to avoid maintaining strong references on variables captured by the
// following block callback
+#if defined(Q_OS_IOS)
+ BOOL isAccessing = [m_URL startAccessingSecurityScopedResource];
+#endif
__block AVURLAsset *asset = [[AVURLAsset URLAssetWithURL:m_URL options:nil] retain];
[asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
__block NSArray *requestedKeys = [[NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil] retain];
- __block AVFMediaPlayerObserver *blockSelf = self;
- QPointer<AVFMediaPlayer> session(m_session);
+ __block AVFMediaPlayerObserver *blockSelf = [self retain];
// Tells the asset to load the values of any of the specified keys that are not already loaded.
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
- if (session)
- [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
+#if defined(Q_OS_IOS)
+ if (isAccessing)
+ [m_URL stopAccessingSecurityScopedResource];
+#endif
+ [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
[asset release];
[requestedKeys release];
+ [blockSelf release];
});
}];
}
@@ -193,6 +166,9 @@ static void *AVFMediaPlayerObserverCurrentItemDurationObservationContext = &AVFM
- (void) prepareToPlayAsset:(AVURLAsset *)asset
withKeys:(NSArray *)requestedKeys
{
+ if (!m_session)
+ return;
+
//Make sure that the value of each key has loaded successfully.
for (NSString *thisKey in requestedKeys)
{
@@ -213,10 +189,23 @@ static void *AVFMediaPlayerObserverCurrentItemDurationObservationContext = &AVFM
qDebug() << Q_FUNC_INFO << "isPlayable: " << [asset isPlayable];
#endif
if (!asset.playable)
+ qWarning() << "Asset reported to be not playable. Playback of this asset may not be possible.";
+
+ //At this point we're ready to set up for playback of the asset.
+ //Stop observing our prior AVPlayerItem, if we have one.
+ if (m_playerItem)
{
+ //Remove existing player item key value observers and notifications.
+ [self unloadMedia];
+ }
+
+ //Create a new instance of AVPlayerItem from the now successfully loaded AVAsset.
+ m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
+ if (!m_playerItem) {
+ qWarning() << "Failed to create player item";
//Generate an error describing the failure.
NSString *localizedDescription = NSLocalizedString(@"Item cannot be played", @"Item cannot be played description");
- NSString *localizedFailureReason = NSLocalizedString(@"The assets tracks were loaded, but could not be made playable.", @"Item cannot be played failure reason");
+ NSString *localizedFailureReason = NSLocalizedString(@"The assets tracks were loaded, but couldn't create player item.", @"Item cannot be played failure reason");
NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
localizedDescription, NSLocalizedDescriptionKey,
localizedFailureReason, NSLocalizedFailureReasonErrorKey,
@@ -224,21 +213,9 @@ static void *AVFMediaPlayerObserverCurrentItemDurationObservationContext = &AVFM
NSError *assetCannotBePlayedError = [NSError errorWithDomain:@"StitchedStreamPlayer" code:0 userInfo:errorDict];
[self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
-
return;
}
- //At this point we're ready to set up for playback of the asset.
- //Stop observing our prior AVPlayerItem, if we have one.
- if (m_playerItem)
- {
- //Remove existing player item key value observers and notifications.
- [self unloadMedia];
- }
-
- //Create a new instance of AVPlayerItem from the now successfully loaded AVAsset.
- m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
-
//Observe the player item "status" key to determine when it is ready to play.
[m_playerItem addObserver:self
forKeyPath:AVF_STATUS_KEY
@@ -472,19 +449,18 @@ static void *AVFMediaPlayerObserverCurrentItemDurationObservationContext = &AVFM
@end
AVFMediaPlayer::AVFMediaPlayer(QMediaPlayer *player)
- : QObject(player)
- , QPlatformMediaPlayer(player)
- , m_state(QMediaPlayer::StoppedState)
- , m_mediaStatus(QMediaPlayer::NoMedia)
- , m_mediaStream(nullptr)
- , m_tryingAsync(false)
- , m_rate(1.0)
- , m_requestedPosition(-1)
- , m_duration(0)
- , m_bufferProgress(0)
- , m_videoAvailable(false)
- , m_audioAvailable(false)
- , m_seekable(false)
+ : QObject(player),
+ QPlatformMediaPlayer(player),
+ m_state(QMediaPlayer::StoppedState),
+ m_mediaStatus(QMediaPlayer::NoMedia),
+ m_mediaStream(nullptr),
+ m_rate(1.0),
+ m_requestedPosition(-1),
+ m_duration(0),
+ m_bufferProgress(0),
+ m_videoAvailable(false),
+ m_audioAvailable(false),
+ m_seekable(false)
{
m_observer = [[AVFMediaPlayerObserver alloc] initWithMediaPlayerSession:this];
connect(&m_playbackTimer, &QTimer::timeout, this, &AVFMediaPlayer::processPositionChange);
@@ -583,7 +559,7 @@ void AVFMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
setVideoAvailable(false);
setSeekable(false);
m_requestedPosition = -1;
- orientationChanged(QVideoFrame::Rotation0, false);
+ orientationChanged(QtVideo::Rotation::None, false);
Q_EMIT positionChanged(position());
if (m_duration != 0) {
m_duration = 0;
@@ -1120,7 +1096,7 @@ void AVFMediaPlayer::updateTracks()
if (m_observer.videoTrack != track) {
m_observer.videoTrack = track;
bool isMirrored = false;
- QVideoFrame::RotationAngle orientation = QVideoFrame::Rotation0;
+ QtVideo::Rotation orientation = QtVideo::Rotation::None;
videoOrientationForAssetTrack(assetTrack, orientation, isMirrored);
orientationChanged(orientation, isMirrored);
}
@@ -1210,7 +1186,7 @@ void AVFMediaPlayer::nativeSizeChanged(QSize size)
m_videoSink->setNativeSize(size);
}
-void AVFMediaPlayer::orientationChanged(QVideoFrame::RotationAngle rotation, bool mirrored)
+void AVFMediaPlayer::orientationChanged(QtVideo::Rotation rotation, bool mirrored)
{
if (!m_videoOutput)
return;
@@ -1220,10 +1196,10 @@ void AVFMediaPlayer::orientationChanged(QVideoFrame::RotationAngle rotation, boo
}
void AVFMediaPlayer::videoOrientationForAssetTrack(AVAssetTrack *videoTrack,
- QVideoFrame::RotationAngle &angle,
+ QtVideo::Rotation &angle,
bool &mirrored)
{
- angle = QVideoFrame::Rotation0;
+ angle = QtVideo::Rotation::None;
if (videoTrack) {
CGAffineTransform transform = videoTrack.preferredTransform;
if (CGAffineTransformIsIdentity(transform))
@@ -1246,11 +1222,13 @@ void AVFMediaPlayer::videoOrientationForAssetTrack(AVAssetTrack *videoTrack,
}
if (qFuzzyCompare(degrees, qreal(90)) || qFuzzyCompare(degrees, qreal(-270))) {
- angle = QVideoFrame::Rotation90;
+ angle = QtVideo::Rotation::Clockwise90;
} else if (qFuzzyCompare(degrees, qreal(-90)) || qFuzzyCompare(degrees, qreal(270))) {
- angle = QVideoFrame::Rotation270;
+ angle = QtVideo::Rotation::Clockwise270;
} else if (qFuzzyCompare(degrees, qreal(180)) || qFuzzyCompare(degrees, qreal(-180))) {
- angle = QVideoFrame::Rotation180;
+ angle = QtVideo::Rotation::Clockwise180;
}
}
}
+
+#include "moc_avfmediaplayer_p.cpp"
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer_p.h b/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer_p.h
index 6993a28d7..d04ab0818 100644
--- a/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer_p.h
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfmediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFMEDIAPLAYER_H
#define AVFMEDIAPLAYER_H
@@ -110,7 +74,7 @@ public:
QMediaMetaData metaData() const override;
static void videoOrientationForAssetTrack(AVAssetTrack *track,
- QVideoFrame::RotationAngle &angle,
+ QtVideo::Rotation &angle,
bool &mirrored);
public Q_SLOTS:
@@ -157,7 +121,7 @@ private:
void setSeekable(bool seekable);
void resetStream(QIODevice *stream = nullptr);
- void orientationChanged(QVideoFrame::RotationAngle rotation, bool mirrored);
+ void orientationChanged(QtVideo::Rotation rotation, bool mirrored);
AVFVideoRendererControl *m_videoOutput = nullptr;
AVFVideoSink *m_videoSink = nullptr;
@@ -168,7 +132,6 @@ private:
QUrl m_resources;
QMediaMetaData m_metaData;
- bool m_tryingAsync;
qreal m_rate;
qint64 m_requestedPosition;
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm
index 0a59708f2..002d688eb 100644
--- a/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "avfvideorenderercontrol_p.h"
#include "avfdisplaylink_p.h"
@@ -45,7 +9,7 @@
#include <QtMultimedia/qvideoframeformat.h>
#include <avfvideosink_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtCore/qdebug.h>
@@ -154,7 +118,7 @@ void AVFVideoRendererControl::setLayer(CALayer *layer)
AVFVideoSinkInterface::setLayer(layer);
}
-void AVFVideoRendererControl::setVideoRotation(QVideoFrame::RotationAngle rotation)
+void AVFVideoRendererControl::setVideoRotation(QtVideo::Rotation rotation)
{
m_rotation = rotation;
}
@@ -189,29 +153,11 @@ void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts)
CVPixelBufferRelease(pixelBuffer);
frame = QVideoFrame(buffer, buffer->videoFormat());
- frame.setRotationAngle(m_rotation);
+ frame.setRotation(m_rotation);
frame.setMirrored(m_mirrored);
m_sink->setVideoFrame(frame);
}
-static NSDictionary* const AVF_OUTPUT_SETTINGS = @{
- (NSString *)kCVPixelBufferPixelFormatTypeKey: @[
- @(kCVPixelFormatType_32BGRA),
- @(kCVPixelFormatType_32RGBA),
- @(kCVPixelFormatType_422YpCbCr8),
- @(kCVPixelFormatType_422YpCbCr8_yuvs),
- @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange),
- @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange),
- @(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange),
- @(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange),
- @(kCVPixelFormatType_OneComponent8),
- @(q_kCVPixelFormatType_OneComponent16),
- @(kCVPixelFormatType_420YpCbCr8Planar),
- @(kCVPixelFormatType_420YpCbCr8PlanarFullRange)
- ],
- (NSString *)kCVPixelBufferMetalCompatibilityKey: @true
-};
-
CVPixelBufferRef AVFVideoRendererControl::copyPixelBufferFromLayer(size_t& width, size_t& height)
{
AVPlayerLayer *layer = playerLayer();
@@ -227,7 +173,7 @@ CVPixelBufferRef AVFVideoRendererControl::copyPixelBufferFromLayer(size_t& width
if (!m_videoOutput) {
if (!m_outputSettings)
- m_outputSettings = AVF_OUTPUT_SETTINGS;
+ setOutputSettings();
m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:m_outputSettings];
[m_videoOutput setDelegate:nil queue:nil];
}
diff --git a/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h
index ea04bbde7..177114127 100644
--- a/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h
+++ b/src/plugins/multimedia/darwin/mediaplayer/avfvideorenderercontrol_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef AVFVIDEORENDERERCONTROL_H
#define AVFVIDEORENDERERCONTROL_H
@@ -80,7 +44,7 @@ public:
void reconfigure() override;
void setLayer(CALayer *layer) override;
- void setVideoRotation(QVideoFrame::RotationAngle);
+ void setVideoRotation(QtVideo::Rotation);
void setVideoMirrored(bool mirrored);
void setSubtitleText(const QString &subtitle)
@@ -99,7 +63,7 @@ private:
AVPlayerItemVideoOutput *m_videoOutput = nullptr;
AVPlayerItemLegibleOutput *m_subtitleOutput = nullptr;
SubtitleDelegate *m_subtitleDelegate = nullptr;
- QVideoFrame::RotationAngle m_rotation = QVideoFrame::Rotation0;
+ QtVideo::Rotation m_rotation = QtVideo::Rotation::None;
bool m_mirrored = false;
};
diff --git a/src/plugins/multimedia/darwin/qavfhelpers.mm b/src/plugins/multimedia/darwin/qavfhelpers.mm
index 52215e883..51ae9eedc 100644
--- a/src/plugins/multimedia/darwin/qavfhelpers.mm
+++ b/src/plugins/multimedia/darwin/qavfhelpers.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qavfhelpers_p.h>
#include <CoreMedia/CMFormatDescription.h>
#include <CoreVideo/CoreVideo.h>
@@ -43,86 +7,73 @@
#import <CoreVideo/CoreVideo.h>
-QVideoFrameFormat::PixelFormat QAVFHelpers::fromCVPixelFormat(unsigned avPixelFormat)
+namespace {
+
+using PixelFormat = QVideoFrameFormat::PixelFormat;
+using ColorRange = QVideoFrameFormat::ColorRange;
+
+// clang-format off
+constexpr std::tuple<CvPixelFormat, PixelFormat, ColorRange> PixelFormatMap[] = {
+ { kCVPixelFormatType_32ARGB, PixelFormat::Format_ARGB8888, ColorRange::ColorRange_Unknown },
+ { kCVPixelFormatType_32BGRA, PixelFormat::Format_BGRA8888, ColorRange::ColorRange_Unknown },
+ { kCVPixelFormatType_420YpCbCr8Planar, PixelFormat::Format_YUV420P, ColorRange::ColorRange_Unknown },
+ { kCVPixelFormatType_420YpCbCr8PlanarFullRange, PixelFormat::Format_YUV420P, ColorRange::ColorRange_Full },
+ { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, PixelFormat::Format_NV12, ColorRange::ColorRange_Video },
+ { kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, PixelFormat::Format_NV12, ColorRange::ColorRange_Full },
+ { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, PixelFormat::Format_P010, ColorRange::ColorRange_Video },
+ { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange, PixelFormat::Format_P010, ColorRange::ColorRange_Full },
+ { kCVPixelFormatType_422YpCbCr8, PixelFormat::Format_UYVY, ColorRange::ColorRange_Video },
+ { kCVPixelFormatType_422YpCbCr8_yuvs, PixelFormat::Format_YUYV, ColorRange::ColorRange_Video },
+ { kCVPixelFormatType_OneComponent8, PixelFormat::Format_Y8, ColorRange::ColorRange_Unknown },
+ { kCVPixelFormatType_OneComponent16, PixelFormat::Format_Y16, ColorRange::ColorRange_Unknown },
+
+ // The cases with kCMVideoCodecType_JPEG/kCMVideoCodecType_JPEG_OpenDML as cv pixel format should be investigated.
+ // Matching kCMVideoCodecType_JPEG_OpenDML to ColorRange_Full is a little hack to distinguish between
+ // kCMVideoCodecType_JPEG and kCMVideoCodecType_JPEG_OpenDML.
+ { kCMVideoCodecType_JPEG, PixelFormat::Format_Jpeg, ColorRange::ColorRange_Unknown },
+ { kCMVideoCodecType_JPEG_OpenDML, PixelFormat::Format_Jpeg, ColorRange::ColorRange_Full }
+};
+// clang-format on
+
+template<typename Type, typename... Args>
+Type findInPixelFormatMap(Type defaultValue, Args... args)
{
- switch (avPixelFormat) {
- case kCVPixelFormatType_32ARGB:
- return QVideoFrameFormat::Format_ARGB8888;
- case kCVPixelFormatType_32BGRA:
- return QVideoFrameFormat::Format_BGRA8888;
- case kCVPixelFormatType_420YpCbCr8Planar:
- case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
- return QVideoFrameFormat::Format_YUV420P;
- case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
- case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
- return QVideoFrameFormat::Format_NV12;
- case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
- case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
- return QVideoFrameFormat::Format_P010;
- case kCVPixelFormatType_422YpCbCr8:
- return QVideoFrameFormat::Format_UYVY;
- case kCVPixelFormatType_422YpCbCr8_yuvs:
- return QVideoFrameFormat::Format_YUYV;
- case kCVPixelFormatType_OneComponent8:
- return QVideoFrameFormat::Format_Y8;
- case q_kCVPixelFormatType_OneComponent16:
- return QVideoFrameFormat::Format_Y16;
-
- case kCMVideoCodecType_JPEG:
- case kCMVideoCodecType_JPEG_OpenDML:
- return QVideoFrameFormat::Format_Jpeg;
- default:
- return QVideoFrameFormat::Format_Invalid;
- }
+ auto checkElement = [&](const auto &element) {
+ return ((args == std::get<Args>(element)) && ...);
+ };
+
+ auto found = std::find_if(std::begin(PixelFormatMap), std::end(PixelFormatMap), checkElement);
+ return found == std::end(PixelFormatMap) ? defaultValue : std::get<Type>(*found);
+}
+
}
-bool QAVFHelpers::toCVPixelFormat(QVideoFrameFormat::PixelFormat qtFormat, unsigned &conv)
+ColorRange QAVFHelpers::colorRangeForCVPixelFormat(CvPixelFormat cvPixelFormat)
{
- switch (qtFormat) {
- case QVideoFrameFormat::Format_ARGB8888:
- conv = kCVPixelFormatType_32ARGB;
- break;
- case QVideoFrameFormat::Format_BGRA8888:
- conv = kCVPixelFormatType_32BGRA;
- break;
- case QVideoFrameFormat::Format_YUV420P:
- conv = kCVPixelFormatType_420YpCbCr8PlanarFullRange;
- break;
- case QVideoFrameFormat::Format_NV12:
- conv = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
- break;
- case QVideoFrameFormat::Format_P010:
- conv = kCVPixelFormatType_420YpCbCr10BiPlanarFullRange;
- break;
- case QVideoFrameFormat::Format_UYVY:
- conv = kCVPixelFormatType_422YpCbCr8;
- break;
- case QVideoFrameFormat::Format_YUYV:
- conv = kCVPixelFormatType_422YpCbCr8_yuvs;
- break;
- case QVideoFrameFormat::Format_Y8:
- conv = kCVPixelFormatType_OneComponent8;
- break;
- case QVideoFrameFormat::Format_Y16:
- conv = q_kCVPixelFormatType_OneComponent16;
- break;
- default:
- return false;
- }
+ return findInPixelFormatMap(ColorRange::ColorRange_Unknown, cvPixelFormat);
+}
- return true;
+PixelFormat QAVFHelpers::fromCVPixelFormat(CvPixelFormat cvPixelFormat)
+{
+ return findInPixelFormatMap(PixelFormat::Format_Invalid, cvPixelFormat);
+}
+
+CvPixelFormat QAVFHelpers::toCVPixelFormat(PixelFormat pixelFmt, ColorRange colorRange)
+{
+ return findInPixelFormatMap(CvPixelFormatInvalid, pixelFmt, colorRange);
}
QVideoFrameFormat QAVFHelpers::videoFormatForImageBuffer(CVImageBufferRef buffer, bool openGL)
{
- auto avPixelFormat = CVPixelBufferGetPixelFormatType(buffer);
+ auto cvPixelFormat = CVPixelBufferGetPixelFormatType(buffer);
+ auto pixelFormat = fromCVPixelFormat(cvPixelFormat);
if (openGL) {
- if (avPixelFormat == kCVPixelFormatType_32BGRA)
- avPixelFormat = QVideoFrameFormat::Format_SamplerRect;
+ if (cvPixelFormat == kCVPixelFormatType_32BGRA)
+ pixelFormat = QVideoFrameFormat::Format_SamplerRect;
else
- qWarning() << "Accelerated macOS OpenGL video supports BGRA only, got CV pixel format" << avPixelFormat;
+ qWarning() << "Accelerated macOS OpenGL video supports BGRA only, got CV pixel format"
+ << cvPixelFormat;
}
- auto pixelFormat = fromCVPixelFormat(avPixelFormat);
size_t width = CVPixelBufferGetWidth(buffer);
size_t height = CVPixelBufferGetHeight(buffer);
@@ -132,57 +83,60 @@ QVideoFrameFormat QAVFHelpers::videoFormatForImageBuffer(CVImageBufferRef buffer
auto colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
auto colorTransfer = QVideoFrameFormat::ColorTransfer_Unknown;
- CFStringRef cSpace = reinterpret_cast<CFStringRef>(
- CVBufferGetAttachment(buffer, kCVImageBufferYCbCrMatrixKey, nullptr));
- if (CFEqual(cSpace, kCVImageBufferYCbCrMatrix_ITU_R_709_2)) {
- colorSpace = QVideoFrameFormat::ColorSpace_BT709;
- } else if (CFEqual(cSpace, kCVImageBufferYCbCrMatrix_ITU_R_601_4) ||
- CFEqual(cSpace, kCVImageBufferYCbCrMatrix_SMPTE_240M_1995)) {
- colorSpace = QVideoFrameFormat::ColorSpace_BT601;
- } else if (@available(macOS 10.11, iOS 9.0, *)) {
- if (CFEqual(cSpace, kCVImageBufferYCbCrMatrix_ITU_R_2020)) {
- colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
+ if (CFStringRef cSpace = reinterpret_cast<CFStringRef>(
+ CVBufferGetAttachment(buffer, kCVImageBufferYCbCrMatrixKey, nullptr))) {
+ if (CFEqual(cSpace, kCVImageBufferYCbCrMatrix_ITU_R_709_2)) {
+ colorSpace = QVideoFrameFormat::ColorSpace_BT709;
+ } else if (CFEqual(cSpace, kCVImageBufferYCbCrMatrix_ITU_R_601_4)
+ || CFEqual(cSpace, kCVImageBufferYCbCrMatrix_SMPTE_240M_1995)) {
+ colorSpace = QVideoFrameFormat::ColorSpace_BT601;
+ } else if (@available(macOS 10.11, iOS 9.0, *)) {
+ if (CFEqual(cSpace, kCVImageBufferYCbCrMatrix_ITU_R_2020)) {
+ colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
+ }
}
}
- CFStringRef cTransfer = reinterpret_cast<CFStringRef>(
- CVBufferGetAttachment(buffer, kCVImageBufferTransferFunctionKey, nullptr));
-
- if (CFEqual(cTransfer, kCVImageBufferTransferFunction_ITU_R_709_2)) {
- colorTransfer = QVideoFrameFormat::ColorTransfer_BT709;
- } else if (CFEqual(cTransfer, kCVImageBufferTransferFunction_SMPTE_240M_1995)) {
- colorTransfer = QVideoFrameFormat::ColorTransfer_BT601;
- } else if (CFEqual(cTransfer, kCVImageBufferTransferFunction_sRGB)) {
- colorTransfer = QVideoFrameFormat::ColorTransfer_Gamma22;
- } else if (CFEqual(cTransfer, kCVImageBufferTransferFunction_UseGamma)) {
- auto gamma = reinterpret_cast<CFNumberRef>(
- CVBufferGetAttachment(buffer, kCVImageBufferGammaLevelKey, nullptr));
- double g;
- CFNumberGetValue(gamma, kCFNumberFloat32Type, &g);
- // These are best fit values given what we have in our enum
- if (g < 0.8)
- ; // unknown
- else if (g < 1.5)
- colorTransfer = QVideoFrameFormat::ColorTransfer_Linear;
- else if (g < 2.1)
+ if (CFStringRef cTransfer = reinterpret_cast<CFStringRef>(
+ CVBufferGetAttachment(buffer, kCVImageBufferTransferFunctionKey, nullptr))) {
+
+ if (CFEqual(cTransfer, kCVImageBufferTransferFunction_ITU_R_709_2)) {
colorTransfer = QVideoFrameFormat::ColorTransfer_BT709;
- else if (g < 2.5)
+ } else if (CFEqual(cTransfer, kCVImageBufferTransferFunction_SMPTE_240M_1995)) {
+ colorTransfer = QVideoFrameFormat::ColorTransfer_BT601;
+ } else if (CFEqual(cTransfer, kCVImageBufferTransferFunction_sRGB)) {
colorTransfer = QVideoFrameFormat::ColorTransfer_Gamma22;
- else if (g < 3.2)
- colorTransfer = QVideoFrameFormat::ColorTransfer_Gamma28;
- }
- if (@available(macOS 10.12, iOS 11.0, *)) {
- if (CFEqual(cTransfer, kCVImageBufferTransferFunction_ITU_R_2020))
- colorTransfer = QVideoFrameFormat::ColorTransfer_BT709;
- }
- if (@available(macOS 10.12, iOS 11.0, *)) {
- if (CFEqual(cTransfer, kCVImageBufferTransferFunction_ITU_R_2100_HLG)) {
- colorTransfer = QVideoFrameFormat::ColorTransfer_STD_B67;
- } else if (CFEqual(cTransfer, kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ)) {
- colorTransfer = QVideoFrameFormat::ColorTransfer_ST2084;
+ } else if (CFEqual(cTransfer, kCVImageBufferTransferFunction_UseGamma)) {
+ auto gamma = reinterpret_cast<CFNumberRef>(
+ CVBufferGetAttachment(buffer, kCVImageBufferGammaLevelKey, nullptr));
+ double g;
+ CFNumberGetValue(gamma, kCFNumberFloat32Type, &g);
+ // These are best fit values given what we have in our enum
+ if (g < 0.8)
+ ; // unknown
+ else if (g < 1.5)
+ colorTransfer = QVideoFrameFormat::ColorTransfer_Linear;
+ else if (g < 2.1)
+ colorTransfer = QVideoFrameFormat::ColorTransfer_BT709;
+ else if (g < 2.5)
+ colorTransfer = QVideoFrameFormat::ColorTransfer_Gamma22;
+ else if (g < 3.2)
+ colorTransfer = QVideoFrameFormat::ColorTransfer_Gamma28;
+ }
+ if (@available(macOS 10.12, iOS 11.0, *)) {
+ if (CFEqual(cTransfer, kCVImageBufferTransferFunction_ITU_R_2020))
+ colorTransfer = QVideoFrameFormat::ColorTransfer_BT709;
+ }
+ if (@available(macOS 10.12, iOS 11.0, *)) {
+ if (CFEqual(cTransfer, kCVImageBufferTransferFunction_ITU_R_2100_HLG)) {
+ colorTransfer = QVideoFrameFormat::ColorTransfer_STD_B67;
+ } else if (CFEqual(cTransfer, kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ)) {
+ colorTransfer = QVideoFrameFormat::ColorTransfer_ST2084;
+ }
}
}
+ format.setColorRange(colorRangeForCVPixelFormat(cvPixelFormat));
format.setColorSpace(colorSpace);
format.setColorTransfer(colorTransfer);
return format;
diff --git a/src/plugins/multimedia/darwin/qavfhelpers_p.h b/src/plugins/multimedia/darwin/qavfhelpers_p.h
index abe9b613e..8133d5500 100644
--- a/src/plugins/multimedia/darwin/qavfhelpers_p.h
+++ b/src/plugins/multimedia/darwin/qavfhelpers_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAVFHELPERS_H
#define QAVFHELPERS_H
@@ -57,19 +21,19 @@
#include <CoreVideo/CVPixelBuffer.h>
#include <CoreVideo/CVImageBuffer.h>
-enum {
- // macOS 10.14 doesn't define this pixel format yet
- q_kCVPixelFormatType_OneComponent16 = 'L016'
-};
-
QT_BEGIN_NAMESPACE
+using CvPixelFormat = unsigned;
+constexpr CvPixelFormat CvPixelFormatInvalid = 0;
+
namespace QAVFHelpers
{
- QVideoFrameFormat::PixelFormat fromCVPixelFormat(unsigned avPixelFormat);
- bool toCVPixelFormat(QVideoFrameFormat::PixelFormat qtFormat, unsigned &conv);
+QVideoFrameFormat::ColorRange colorRangeForCVPixelFormat(CvPixelFormat cvPixelFormat);
+QVideoFrameFormat::PixelFormat fromCVPixelFormat(CvPixelFormat cvPixelFormat);
+CvPixelFormat toCVPixelFormat(QVideoFrameFormat::PixelFormat pixFmt,
+ QVideoFrameFormat::ColorRange colorRange);
- QVideoFrameFormat videoFormatForImageBuffer(CVImageBufferRef buffer, bool openGL = false);
+QVideoFrameFormat videoFormatForImageBuffer(CVImageBufferRef buffer, bool openGL = false);
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/darwin/qdarwinformatsinfo.mm b/src/plugins/multimedia/darwin/qdarwinformatsinfo.mm
index 4a8364bbb..582060a6c 100644
--- a/src/plugins/multimedia/darwin/qdarwinformatsinfo.mm
+++ b/src/plugins/multimedia/darwin/qdarwinformatsinfo.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdarwinformatsinfo_p.h"
#include <AVFoundation/AVFoundation.h>
diff --git a/src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h b/src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h
index 79e33b6f4..e01486286 100644
--- a/src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h
+++ b/src/plugins/multimedia/darwin/qdarwinformatsinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDARWINFORMATINFO_H
#define QDARWINFORMATINFO_H
diff --git a/src/plugins/multimedia/darwin/qdarwinintegration.mm b/src/plugins/multimedia/darwin/qdarwinintegration.mm
index 0f2ff14e7..0e880447e 100644
--- a/src/plugins/multimedia/darwin/qdarwinintegration.mm
+++ b/src/plugins/multimedia/darwin/qdarwinintegration.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdarwinintegration_p.h"
#include <avfmediaplayer_p.h>
@@ -65,65 +29,61 @@ public:
QPlatformMediaIntegration* create(const QString &name) override
{
- if (name == QLatin1String("darwin"))
+ if (name == u"darwin")
return new QDarwinIntegration;
return nullptr;
}
};
-
-QDarwinIntegration::QDarwinIntegration()
+QDarwinIntegration::QDarwinIntegration() : QPlatformMediaIntegration(QLatin1String("darwin"))
{
#if defined(Q_OS_MACOS) && QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_11_0)
if (__builtin_available(macOS 11.0, *))
VTRegisterSupplementalVideoDecoderIfAvailable(kCMVideoCodecType_VP9);
#endif
- m_videoDevices = new QAVFVideoDevices(this);
}
-QDarwinIntegration::~QDarwinIntegration()
+QPlatformMediaFormatInfo *QDarwinIntegration::createFormatInfo()
{
- delete m_formatInfo;
+ return new QDarwinFormatInfo();
}
-QPlatformMediaFormatInfo *QDarwinIntegration::formatInfo()
+QPlatformVideoDevices *QDarwinIntegration::createVideoDevices()
{
- if (!m_formatInfo)
- m_formatInfo = new QDarwinFormatInfo();
- return m_formatInfo;
+ return new QAVFVideoDevices(this);
}
-QPlatformAudioDecoder *QDarwinIntegration::createAudioDecoder(QAudioDecoder *decoder)
+QMaybe<QPlatformAudioDecoder *> QDarwinIntegration::createAudioDecoder(QAudioDecoder *decoder)
{
return new AVFAudioDecoder(decoder);
}
-QPlatformMediaCaptureSession *QDarwinIntegration::createCaptureSession()
+QMaybe<QPlatformMediaCaptureSession *> QDarwinIntegration::createCaptureSession()
{
return new AVFCameraService;
}
-QPlatformMediaPlayer *QDarwinIntegration::createPlayer(QMediaPlayer *player)
+QMaybe<QPlatformMediaPlayer *> QDarwinIntegration::createPlayer(QMediaPlayer *player)
{
return new AVFMediaPlayer(player);
}
-QPlatformCamera *QDarwinIntegration::createCamera(QCamera *camera)
+QMaybe<QPlatformCamera *> QDarwinIntegration::createCamera(QCamera *camera)
{
return new AVFCamera(camera);
}
-QPlatformMediaRecorder *QDarwinIntegration::createRecorder(QMediaRecorder *recorder)
+QMaybe<QPlatformMediaRecorder *> QDarwinIntegration::createRecorder(QMediaRecorder *recorder)
{
return new AVFMediaEncoder(recorder);
}
-QPlatformImageCapture *QDarwinIntegration::createImageCapture(QImageCapture *imageCapture)
+QMaybe<QPlatformImageCapture *> QDarwinIntegration::createImageCapture(QImageCapture *imageCapture)
{
return new AVFImageCapture(imageCapture);
}
-QPlatformVideoSink *QDarwinIntegration::createVideoSink(QVideoSink *sink)
+QMaybe<QPlatformVideoSink *> QDarwinIntegration::createVideoSink(QVideoSink *sink)
{
return new AVFVideoSink(sink);
}
diff --git a/src/plugins/multimedia/darwin/qdarwinintegration_p.h b/src/plugins/multimedia/darwin/qdarwinintegration_p.h
index 39e3434be..8333de4ec 100644
--- a/src/plugins/multimedia/darwin/qdarwinintegration_p.h
+++ b/src/plugins/multimedia/darwin/qdarwinintegration_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDARWININTEGRATION_H
#define QDARWININTEGRATION_H
@@ -61,20 +25,19 @@ class QDarwinIntegration : public QPlatformMediaIntegration
{
public:
QDarwinIntegration();
- ~QDarwinIntegration();
- QPlatformMediaFormatInfo *formatInfo() override;
+ QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *) override;
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *player) override;
+ QMaybe<QPlatformCamera *> createCamera(QCamera *camera) override;
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *) override;
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *) override;
- QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *) override;
- QPlatformMediaCaptureSession *createCaptureSession() override;
- QPlatformMediaPlayer *createPlayer(QMediaPlayer *player) override;
- QPlatformCamera *createCamera(QCamera *camera) override;
- QPlatformMediaRecorder *createRecorder(QMediaRecorder *) override;
- QPlatformImageCapture *createImageCapture(QImageCapture *) override;
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *) override;
- QPlatformVideoSink *createVideoSink(QVideoSink *) override;
-
- QPlatformMediaFormatInfo *m_formatInfo = nullptr;
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+ QPlatformVideoDevices *createVideoDevices() override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/CMakeLists.txt b/src/plugins/multimedia/ffmpeg/CMakeLists.txt
index 402a3b0aa..b18cea002 100644
--- a/src/plugins/multimedia/ffmpeg/CMakeLists.txt
+++ b/src/plugins/multimedia/ffmpeg/CMakeLists.txt
@@ -1,23 +1,28 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_find_package(EGL)
-qt_find_package(VAAPI COMPONENTS VA DRM PROVIDED_TARGETS VAAPI::VA VAAPI::DRM MODULE_NAME multimedia QMAKE_LIB vaapi)
+qt_find_package(VAAPI COMPONENTS VA DRM PROVIDED_TARGETS VAAPI::VAAPI MODULE_NAME multimedia QMAKE_LIB vaapi)
-qt_internal_find_apple_system_framework(FWCoreMedia CoreMedia) # special case
-qt_internal_find_apple_system_framework(FWCoreAudio CoreAudio) # special case
-qt_internal_find_apple_system_framework(FWAudioUnit AudioUnit) # special case
-qt_internal_find_apple_system_framework(FWVideoToolbox VideoToolbox) # special case
-qt_internal_find_apple_system_framework(FWAVFoundation AVFoundation) # special case
+qt_internal_find_apple_system_framework(FWCoreMedia CoreMedia)
+qt_internal_find_apple_system_framework(FWCoreAudio CoreAudio)
+qt_internal_find_apple_system_framework(FWAudioUnit AudioUnit)
+qt_internal_find_apple_system_framework(FWVideoToolbox VideoToolbox)
+qt_internal_find_apple_system_framework(FWAVFoundation AVFoundation)
+qt_internal_find_apple_system_framework(FWSecurity Security)
qt_internal_add_plugin(QFFmpegMediaPlugin
- OUTPUT_NAME ffmegmediaplugin
+ OUTPUT_NAME ffmpegmediaplugin
PLUGIN_TYPE multimedia
SOURCES
- qffmpeg_p.h
+ qffmpeg.cpp qffmpeg_p.h
+ qffmpegdefs_p.h
+ qffmpegioutils.cpp qffmpegioutils_p.h
+ qffmpegavaudioformat.cpp qffmpegavaudioformat_p.h
qffmpegaudiodecoder.cpp qffmpegaudiodecoder_p.h
qffmpegaudioinput.cpp qffmpegaudioinput_p.h
- qffmpegclock.cpp qffmpegclock_p.h
- qffmpegdecoder.cpp qffmpegdecoder_p.h
+ qffmpegconverter.cpp qffmpegconverter_p.h
qffmpeghwaccel.cpp qffmpeghwaccel_p.h
- qffmpegencoderoptions.cpp qffmpegencoderoptions_p.h
qffmpegmediametadata.cpp qffmpegmediametadata_p.h
qffmpegmediaplayer.cpp qffmpegmediaplayer_p.h
qffmpegvideosink.cpp qffmpegvideosink_p.h
@@ -27,23 +32,122 @@ qt_internal_add_plugin(QFFmpegMediaPlugin
qffmpegimagecapture.cpp qffmpegimagecapture_p.h
qffmpegmediacapturesession.cpp qffmpegmediacapturesession_p.h
qffmpegmediarecorder.cpp qffmpegmediarecorder_p.h
- qffmpegencoder.cpp qffmpegencoder_p.h
qffmpegthread.cpp qffmpegthread_p.h
qffmpegresampler.cpp qffmpegresampler_p.h
- qffmpegvideoframeencoder.cpp qffmpegvideoframeencoder_p.h
+ qffmpegencodingformatcontext.cpp qffmpegencodingformatcontext_p.h
+ qgrabwindowsurfacecapture.cpp qgrabwindowsurfacecapture_p.h
+ qffmpegsurfacecapturegrabber.cpp qffmpegsurfacecapturegrabber_p.h
+ qffmpegsymbolsresolve_p.h
+
+ qffmpegplaybackengine.cpp qffmpegplaybackengine_p.h
+ playbackengine/qffmpegplaybackenginedefs_p.h
+ playbackengine/qffmpegplaybackengineobject.cpp playbackengine/qffmpegplaybackengineobject_p.h
+ playbackengine/qffmpegdemuxer.cpp playbackengine/qffmpegdemuxer_p.h
+ playbackengine/qffmpegstreamdecoder.cpp playbackengine/qffmpegstreamdecoder_p.h
+ playbackengine/qffmpegrenderer.cpp playbackengine/qffmpegrenderer_p.h
+ playbackengine/qffmpegaudiorenderer.cpp playbackengine/qffmpegaudiorenderer_p.h
+ playbackengine/qffmpegvideorenderer.cpp playbackengine/qffmpegvideorenderer_p.h
+ playbackengine/qffmpegsubtitlerenderer.cpp playbackengine/qffmpegsubtitlerenderer_p.h
+ playbackengine/qffmpegtimecontroller.cpp playbackengine/qffmpegtimecontroller_p.h
+ playbackengine/qffmpegmediadataholder.cpp playbackengine/qffmpegmediadataholder_p.h
+ playbackengine/qffmpegcodec.cpp playbackengine/qffmpegcodec_p.h
+ playbackengine/qffmpegpacket_p.h
+ playbackengine/qffmpegframe_p.h
+ playbackengine/qffmpegpositionwithoffset_p.h
+
+ recordingengine/qffmpegaudioencoder_p.h
+ recordingengine/qffmpegaudioencoder.cpp
+ recordingengine/qffmpegaudioencoderutils_p.h
+ recordingengine/qffmpegaudioencoderutils.cpp
+ recordingengine/qffmpegencoderthread_p.h
+ recordingengine/qffmpegencoderthread.cpp
+ recordingengine/qffmpegencoderoptions_p.h
+ recordingengine/qffmpegencoderoptions.cpp
+ recordingengine/qffmpegmuxer_p.h
+ recordingengine/qffmpegmuxer.cpp
+ recordingengine/qffmpegrecordingengine_p.h
+ recordingengine/qffmpegrecordingengine.cpp
+ recordingengine/qffmpegvideoencoder_p.h
+ recordingengine/qffmpegvideoencoder.cpp
+ recordingengine/qffmpegvideoencoderutils_p.h
+ recordingengine/qffmpegvideoencoderutils.cpp
+ recordingengine/qffmpegvideoframeencoder_p.h
+ recordingengine/qffmpegvideoframeencoder.cpp
+
DEFINES
QT_COMPILING_FFMPEG
LIBRARIES
Qt::MultimediaPrivate
Qt::CorePrivate
- FFmpeg::avformat FFmpeg::avcodec FFmpeg::swresample FFmpeg::swscale FFmpeg::avutil
)
-qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_ffmpeg AND QT_FEATURE_vaapi
+if(DYNAMIC_RESOLVE_OPENSSL_SYMBOLS)
+ if(NOT OPENSSL_INCLUDE_DIR AND OPENSSL_ROOT_DIR)
+ set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/include")
+ endif()
+endif()
+
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION DYNAMIC_RESOLVE_OPENSSL_SYMBOLS
+ SOURCES
+ qffmpegopensslsymbols.cpp
+ INCLUDE_DIRECTORIES
+ ${OPENSSL_INCLUDE_DIR}
+)
+
+if (ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS)
+ if (QT_FEATURE_vaapi AND NOT DYNAMIC_RESOLVE_VAAPI_SYMBOLS)
+ if (NOT FFMPEG_SHARED_LIBRARIES)
+ message(WARNING
+ "QT_FEATURE_vaapi is found but statically built FFmpeg doesn't include vaapi,"
+ "however dynamic symbols resolve is possible.")
+ endif()
+
+ set(DYNAMIC_RESOLVE_VAAPI_SYMBOLS TRUE CACHE INTERNAL "")
+ elseif (NOT QT_FEATURE_vaapi AND DYNAMIC_RESOLVE_VAAPI_SYMBOLS)
+
+ message(FATAL_ERROR
+ "QT_FEATURE_vaapi is not found "
+ "but FFmpeg includes VAAPI and dynamic symbols resolve is enabled.")
+ endif()
+endif()
+
+qt_internal_extend_target(QFFmpegMediaPlugin
+ CONDITION
+ DYNAMIC_RESOLVE_OPENSSL_SYMBOLS OR DYNAMIC_RESOLVE_VAAPI_SYMBOLS
+ SOURCES
+ qffmpegsymbolsresolveutils.cpp qffmpegsymbolsresolveutils_p.h
+)
+
+function (__propagate_to_compile_definitions VAR)
+ if (${VAR})
+ target_compile_definitions(QFFmpegMediaPlugin PRIVATE ${VAR})
+ endif()
+endfunction()
+
+__propagate_to_compile_definitions(DYNAMIC_RESOLVE_OPENSSL_SYMBOLS)
+__propagate_to_compile_definitions(DYNAMIC_RESOLVE_VAAPI_SYMBOLS)
+__propagate_to_compile_definitions(DYNAMIC_RESOLVE_VA_DRM_SYMBOLS)
+__propagate_to_compile_definitions(DYNAMIC_RESOLVE_VA_X11_SYMBOLS)
+
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION DYNAMIC_RESOLVE_VAAPI_SYMBOLS
+ SOURCES
+ qffmpegvaapisymbols.cpp
+ INCLUDE_DIRECTORIES
+ "$<TARGET_PROPERTY:VAAPI::VAAPI,INTERFACE_INCLUDE_DIRECTORIES>"
+)
+
+qt_internal_extend_target(QFFmpegMediaPlugin
+ CONDITION NOT DYNAMIC_RESOLVE_VAAPI_SYMBOLS AND QT_FEATURE_vaapi
+ LIBRARIES VAAPI::VAAPI
+)
+
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_vaapi
SOURCES
qffmpeghwaccel_vaapi.cpp qffmpeghwaccel_vaapi_p.h
+ NO_UNITY_BUILD_SOURCES
+ # Conflicts with macros defined in X11.h, and Xlib.h
+ qffmpeghwaccel_vaapi.cpp
LIBRARIES
- VAAPI::VAAPI
EGL::EGL
)
@@ -54,6 +158,11 @@ qt_internal_extend_target(QFFmpegMediaPlugin CONDITION APPLE
../darwin/camera/avfcamerautility_p.h ../darwin/camera/avfcamerautility.mm
qffmpeghwaccel_videotoolbox.mm qffmpeghwaccel_videotoolbox_p.h
qavfcamera.mm qavfcamera_p.h
+ qavfsamplebufferdelegate.mm qavfsamplebufferdelegate_p.h
+
+ NO_UNITY_BUILD_SOURCES
+ qffmpeghwaccel_videotoolbox.mm # AVMediaType clash between libavformat and AVFoundation
+
INCLUDE_DIRECTORIES
../darwin
../darwin/camera
@@ -64,39 +173,120 @@ qt_internal_extend_target(QFFmpegMediaPlugin CONDITION APPLE
${FWCoreMedia}
${FWCoreVideo}
${FWVideoToolbox}
+ ${FWSecurity}
AVFoundation::AVFoundation
)
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION MACOS
+ SOURCES
+ qavfscreencapture.mm qavfscreencapture_p.h
+ qcgwindowcapture.mm qcgwindowcapture_p.h
+ qcgcapturablewindows.mm qcgcapturablewindows_p.h
+)
+
qt_internal_extend_target(QFFmpegMediaPlugin CONDITION WIN32
SOURCES
../windows/qwindowsvideodevices.cpp ../windows/qwindowsvideodevices_p.h
qwindowscamera.cpp qwindowscamera_p.h
qffmpeghwaccel_d3d11.cpp qffmpeghwaccel_d3d11_p.h
+ qgdiwindowcapture.cpp qgdiwindowcapture_p.h
+ qffmpegscreencapture_dxgi.cpp qffmpegscreencapture_dxgi_p.h
+ qwincapturablewindows.cpp qwincapturablewindows_p.h
INCLUDE_DIRECTORIES
../windows
LIBRARIES
+ Qt::MultimediaPrivate
WMF::WMF
mfreadwrite
)
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_cpp_winrt
+ SOURCES
+ qffmpegwindowcapture_uwp.cpp qffmpegwindowcapture_uwp_p.h
+ INCLUDE_DIRECTORIES
+ ../windows
+ LIBRARIES
+ Dwmapi
+ Dxva2
+ windowsapp
+)
+
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_xlib
+ SOURCES
+ qx11surfacecapture.cpp qx11surfacecapture_p.h
+ qx11capturablewindows.cpp qx11capturablewindows_p.h
+ LIBRARIES
+ X11
+ Xrandr
+ Xext
+)
+
+qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_eglfs
+ SOURCES
+ qeglfsscreencapture.cpp qeglfsscreencapture_p.h
+ qopenglvideobuffer.cpp qopenglvideobuffer_p.h
+ LIBRARIES
+ Qt::OpenGLPrivate
+ Qt::Quick
+)
+
+set_source_files_properties(qx11surfacecapture.cpp qx11capturablewindows.cpp # X headers
+ PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
+
qt_internal_extend_target(QFFmpegMediaPlugin CONDITION QT_FEATURE_linux_v4l
SOURCES
qv4l2camera.cpp qv4l2camera_p.h
+ qv4l2filedescriptor.cpp qv4l2filedescriptor_p.h
+ qv4l2memorytransfer.cpp qv4l2memorytransfer_p.h
+ qv4l2cameradevices.cpp qv4l2cameradevices_p.h
)
if (ANDROID)
qt_internal_extend_target(QFFmpegMediaPlugin
+ SOURCES
+ qffmpeghwaccel_mediacodec.cpp qffmpeghwaccel_mediacodec_p.h
+ qandroidcamera_p.h qandroidcamera.cpp
+ qandroidvideodevices.cpp qandroidvideodevices_p.h
+ qandroidcameraframe_p.h qandroidcameraframe.cpp
+ qandroidimagecapture_p.h qandroidimagecapture.cpp
+ ../android/wrappers/jni/androidsurfacetexture_p.h
+ ../android/wrappers/jni/androidsurfacetexture.cpp
+ NO_UNITY_BUILD_SOURCES
+ # Duplicate declration of JNI Classes using `Q_DECLARE_JNI_CLASS`
+ qandroidcamera.cpp
+ qandroidvideodevices.cpp
+ qandroidcameraframe.cpp
INCLUDE_DIRECTORIES
${FFMPEG_DIR}/include
+ ../android/wrappers/jni/
+ LIBRARIES
+ OpenSLES
+ mediandk
+ android
)
set_property(TARGET QFFmpegMediaPlugin APPEND PROPERTY QT_ANDROID_LIB_DEPENDENCIES
- plugins/multimedia/libplugins_multimedia_ffmegmediaplugin.so
+ ${INSTALL_PLUGINSDIR}/multimedia/libplugins_multimedia_ffmpegmediaplugin.so
)
set_property(TARGET QFFmpegMediaPlugin APPEND PROPERTY QT_ANDROID_PERMISSIONS
- android.permission.CAMERA android.permission.RECORD_AUDIO
+ android.permission.CAMERA
+ android.permission.RECORD_AUDIO
android.permission.BLUETOOTH
android.permission.MODIFY_AUDIO_SETTINGS
)
endif()
+
+# TODO: get libs from FindFFmpeg.cmake
+set(ffmpeg_libs FFmpeg::avformat FFmpeg::avcodec FFmpeg::swresample FFmpeg::swscale FFmpeg::avutil)
+
+if (QT_DEPLOY_FFMPEG AND NOT BUILD_SHARED_LIBS)
+ message(FATAL_ERROR "QT_DEPLOY_FFMPEG is not implemented yet for static builds")
+endif()
+
+if (QT_DEPLOY_FFMPEG AND FFMPEG_SHARED_LIBRARIES AND BUILD_SHARED_LIBS)
+ include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/QtDeployFFmpeg.cmake")
+ qt_internal_multimedia_copy_or_install_ffmpeg()
+endif()
+
+qt_internal_extend_target(QFFmpegMediaPlugin LIBRARIES ${ffmpeg_libs})
diff --git a/src/plugins/multimedia/ffmpeg/cmake/QtDeployFFmpeg.cmake b/src/plugins/multimedia/ffmpeg/cmake/QtDeployFFmpeg.cmake
new file mode 100644
index 000000000..5e7d4b552
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/cmake/QtDeployFFmpeg.cmake
@@ -0,0 +1,43 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+function(qt_internal_multimedia_set_ffmpeg_link_directory directory)
+ foreach (lib ${ffmpeg_libs} FFmpeg)
+ set_target_properties(${lib} PROPERTIES INTERFACE_LINK_DIRECTORIES ${directory})
+ endforeach()
+endfunction()
+
+function(qt_internal_multimedia_copy_or_install_ffmpeg)
+ if (WIN32)
+ set(install_dir ${INSTALL_BINDIR})
+ else()
+ set(install_dir ${INSTALL_LIBDIR})
+ endif()
+
+ if (QT_WILL_INSTALL)
+ qt_install(FILES "${FFMPEG_SHARED_LIBRARIES}" DESTINATION ${install_dir})
+ else()
+ # elseif(NOT WIN32) actually we can just drop the coping for unix platforms
+ # However, it makes sense to copy anyway for consistency:
+ # in order to have the same configuration for developer builds.
+
+ set(ffmpeg_output_dir "${QT_BUILD_DIR}/${install_dir}")
+ file(MAKE_DIRECTORY ${ffmpeg_output_dir})
+
+ foreach(lib_path ${FFMPEG_SHARED_LIBRARIES})
+ get_filename_component(lib_name ${lib_path} NAME)
+ if(NOT EXISTS "${ffmpeg_output_dir}/${lib_name}")
+ file(COPY ${lib_path} DESTINATION ${ffmpeg_output_dir})
+ endif()
+ endforeach()
+
+ # On Windows, shared linking goes through 'integration' static libs,
+ # otherwise we should link the directory with copied libs
+ if (NOT WIN32)
+ qt_internal_multimedia_set_ffmpeg_link_directory(${ffmpeg_output_dir})
+ endif()
+ endif()
+
+ # Should we set the compile definition for the plugin or for the QtMM module?
+ # target_compile_definitions(QFFmpegMediaPlugin PRIVATE FFMPEG_DEPLOY_FOLDER="${FFMPEG_DEPLOY_FOLDER}")
+endfunction()
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer.cpp
new file mode 100644
index 000000000..88caac941
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer.cpp
@@ -0,0 +1,355 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegaudiorenderer_p.h"
+#include "qaudiosink.h"
+#include "qaudiooutput.h"
+#include "private/qplatformaudiooutput_p.h"
+#include <QtCore/qloggingcategory.h>
+
+#include "qffmpegresampler_p.h"
+#include "qffmpegmediaformatinfo_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcAudioRenderer, "qt.multimedia.ffmpeg.audiorenderer");
+
+namespace QFFmpeg {
+
+using namespace std::chrono_literals;
+using namespace std::chrono;
+
+namespace {
+constexpr auto DesiredBufferTime = 110000us;
+constexpr auto MinDesiredBufferTime = 22000us;
+constexpr auto MaxDesiredBufferTime = 64000us;
+constexpr auto MinDesiredFreeBufferTime = 10000us;
+
+// It might be changed with #ifdef, as on Linux, QPulseAudioSink has quite unstable timings,
+// and it needs much more time to make sure that the buffer is overloaded.
+constexpr auto BufferLoadingMeasureTime = 400ms;
+
+constexpr auto DurationBias = 2ms; // avoids extra timer events
+
+qreal sampleRateFactor() {
+ // Test purposes:
+ //
+ // The env var describes a factor for the sample rate of
+ // audio data that we feed to the audio sink.
+ //
+ // In some cases audio sink might consume data slightly slower or faster than expected;
+ // even though the synchronization in the audio renderer is supposed to handle it,
+ // it makes sense to experiment with QT_MEDIA_PLAYER_AUDIO_SAMPLE_RATE_FACTOR != 1.
+ //
+ // Set QT_MEDIA_PLAYER_AUDIO_SAMPLE_RATE_FACTOR > 1 (e.g. 1.01 - 1.1) to test high buffer loading
+ // or try compensating too fast data consumption by the audio sink.
+ // Set QT_MEDIA_PLAYER_AUDIO_SAMPLE_RATE_FACTOR < 1 to test low buffer loading
+ // or try compensating too slow data consumption by the audio sink.
+
+
+ static const qreal result = []() {
+ const auto sampleRateFactorStr = qEnvironmentVariable("QT_MEDIA_PLAYER_AUDIO_SAMPLE_RATE_FACTOR");
+ bool ok = false;
+ const auto result = sampleRateFactorStr.toDouble(&ok);
+ return ok ? result : 1.;
+ }();
+
+ return result;
+}
+} // namespace
+
+AudioRenderer::AudioRenderer(const TimeController &tc, QAudioOutput *output)
+ : Renderer(tc), m_output(output)
+{
+ if (output) {
+ // TODO: implement the signals in QPlatformAudioOutput and connect to them, QTBUG-112294
+ connect(output, &QAudioOutput::deviceChanged, this, &AudioRenderer::onDeviceChanged);
+ connect(output, &QAudioOutput::volumeChanged, this, &AudioRenderer::updateVolume);
+ connect(output, &QAudioOutput::mutedChanged, this, &AudioRenderer::updateVolume);
+ }
+}
+
+void AudioRenderer::setOutput(QAudioOutput *output)
+{
+ setOutputInternal(m_output, output, [this](QAudioOutput *) { onDeviceChanged(); });
+}
+
+AudioRenderer::~AudioRenderer()
+{
+ freeOutput();
+}
+
+void AudioRenderer::updateVolume()
+{
+ if (m_sink)
+ m_sink->setVolume(m_output->isMuted() ? 0.f : m_output->volume());
+}
+
+void AudioRenderer::onDeviceChanged()
+{
+ m_deviceChanged = true;
+}
+
+Renderer::RenderingResult AudioRenderer::renderInternal(Frame frame)
+{
+ if (frame.isValid())
+ updateOutput(frame.codec());
+
+ if (!m_ioDevice || !m_resampler)
+ return {};
+
+ Q_ASSERT(m_sink);
+
+ auto firstFrameFlagGuard = qScopeGuard([&]() { m_firstFrame = false; });
+
+ const SynchronizationStamp syncStamp{ m_sink->state(), m_sink->bytesFree(),
+ m_bufferedData.offset, Clock::now() };
+
+ if (!m_bufferedData.isValid()) {
+ if (!frame.isValid()) {
+ if (std::exchange(m_drained, true))
+ return {};
+
+ const auto time = bufferLoadingTime(syncStamp);
+
+ qCDebug(qLcAudioRenderer) << "Draining AudioRenderer, time:" << time;
+
+ return { time.count() == 0, time };
+ }
+
+ m_bufferedData = { m_resampler->resample(frame.avFrame()) };
+ }
+
+ if (m_bufferedData.isValid()) {
+ // synchronize after "QIODevice::write" to deliver audio data to the sink ASAP.
+ auto syncGuard = qScopeGuard([&]() { updateSynchronization(syncStamp, frame); });
+
+ const auto bytesWritten = m_ioDevice->write(m_bufferedData.data(), m_bufferedData.size());
+
+ m_bufferedData.offset += bytesWritten;
+
+ if (m_bufferedData.size() <= 0) {
+ m_bufferedData = {};
+
+ return {};
+ }
+
+ const auto remainingDuration = durationForBytes(m_bufferedData.size());
+
+ return { false,
+ std::min(remainingDuration + DurationBias, m_timings.actualBufferDuration / 2) };
+ }
+
+ return {};
+}
+
+void AudioRenderer::onPlaybackRateChanged()
+{
+ m_resampler.reset();
+}
+
+int AudioRenderer::timerInterval() const
+{
+ constexpr auto MaxFixableInterval = 50; // ms
+
+ const auto interval = Renderer::timerInterval();
+
+ if (m_firstFrame || !m_sink || m_sink->state() != QAudio::IdleState
+ || interval > MaxFixableInterval)
+ return interval;
+
+ return 0;
+}
+
+void AudioRenderer::onPauseChanged()
+{
+ m_firstFrame = true;
+ Renderer::onPauseChanged();
+}
+
+void AudioRenderer::initResempler(const Codec *codec)
+{
+ // We recreate resampler whenever format is changed
+
+ /* AVSampleFormat requiredFormat =
+ QFFmpegMediaFormatInfo::avSampleFormat(m_format.sampleFormat());
+
+ #if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ qCDebug(qLcAudioRenderer) << "init resampler" << requiredFormat
+ << codec->stream()->codecpar->channels;
+ #else
+ qCDebug(qLcAudioRenderer) << "init resampler" << requiredFormat
+ << codec->stream()->codecpar->ch_layout.nb_channels;
+ #endif
+ */
+
+ auto resamplerFormat = m_format;
+ resamplerFormat.setSampleRate(
+ qRound(m_format.sampleRate() / playbackRate() * sampleRateFactor()));
+ m_resampler = std::make_unique<QFFmpegResampler>(codec, resamplerFormat);
+}
+
+void AudioRenderer::freeOutput()
+{
+ qCDebug(qLcAudioRenderer) << "Free audio output";
+ if (m_sink) {
+ m_sink->reset();
+
+ // TODO: inestigate if it's enough to reset the sink without deleting
+ m_sink.reset();
+ }
+
+ m_ioDevice = nullptr;
+
+ m_bufferedData = {};
+ m_deviceChanged = false;
+ m_timings = {};
+ m_bufferLoadingInfo = {};
+}
+
+void AudioRenderer::updateOutput(const Codec *codec)
+{
+ if (m_deviceChanged) {
+ freeOutput();
+ m_format = {};
+ m_resampler.reset();
+ }
+
+ if (!m_output)
+ return;
+
+ if (!m_format.isValid()) {
+ m_format =
+ QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(codec->stream()->codecpar);
+ m_format.setChannelConfig(m_output->device().channelConfiguration());
+ }
+
+ if (!m_sink) {
+ // Insert a delay here to test time offset synchronization, e.g. QThread::sleep(1)
+ m_sink = std::make_unique<QAudioSink>(m_output->device(), m_format);
+ updateVolume();
+ m_sink->setBufferSize(m_format.bytesForDuration(DesiredBufferTime.count()));
+ m_ioDevice = m_sink->start();
+ m_firstFrame = true;
+
+ connect(m_sink.get(), &QAudioSink::stateChanged, this,
+ &AudioRenderer::onAudioSinkStateChanged);
+
+ m_timings.actualBufferDuration = durationForBytes(m_sink->bufferSize());
+ m_timings.maxSoundDelay = qMin(MaxDesiredBufferTime,
+ m_timings.actualBufferDuration - MinDesiredFreeBufferTime);
+ m_timings.minSoundDelay = MinDesiredBufferTime;
+
+ Q_ASSERT(DurationBias < m_timings.minSoundDelay
+ && m_timings.maxSoundDelay < m_timings.actualBufferDuration);
+ }
+
+ if (!m_resampler) {
+ initResempler(codec);
+ }
+}
+
+void AudioRenderer::updateSynchronization(const SynchronizationStamp &stamp, const Frame &frame)
+{
+ if (!frame.isValid())
+ return;
+
+ Q_ASSERT(m_sink);
+
+ const auto bufferLoadingTime = this->bufferLoadingTime(stamp);
+ const auto currentFrameDelay = frameDelay(frame, stamp.timePoint);
+ const auto writtenTime = durationForBytes(stamp.bufferBytesWritten);
+ const auto soundDelay = currentFrameDelay + bufferLoadingTime - writtenTime;
+
+ auto synchronize = [&](microseconds fixedDelay, microseconds targetSoundDelay) {
+ // TODO: investigate if we need sample compensation here
+
+ changeRendererTime(fixedDelay - targetSoundDelay);
+ if (qLcAudioRenderer().isDebugEnabled()) {
+ // clang-format off
+ qCDebug(qLcAudioRenderer)
+ << "Change rendering time:"
+ << "\n First frame:" << m_firstFrame
+ << "\n Delay (frame+buffer-written):" << currentFrameDelay << "+"
+ << bufferLoadingTime << "-"
+ << writtenTime << "="
+ << soundDelay
+ << "\n Fixed delay:" << fixedDelay
+ << "\n Target delay:" << targetSoundDelay
+ << "\n Buffer durations (min/max/limit):" << m_timings.minSoundDelay
+ << m_timings.maxSoundDelay
+ << m_timings.actualBufferDuration
+ << "\n Audio sink state:" << stamp.audioSinkState;
+ // clang-format on
+ }
+ };
+
+ const auto loadingType = soundDelay > m_timings.maxSoundDelay ? BufferLoadingInfo::High
+ : soundDelay < m_timings.minSoundDelay ? BufferLoadingInfo::Low
+ : BufferLoadingInfo::Moderate;
+
+ if (loadingType != m_bufferLoadingInfo.type) {
+ // qCDebug(qLcAudioRenderer) << "Change buffer loading type:" <<
+ // m_bufferLoadingInfo.type
+ // << "->" << loadingType << "soundDelay:" << soundDelay;
+ m_bufferLoadingInfo = { loadingType, stamp.timePoint, soundDelay };
+ }
+
+ if (loadingType != BufferLoadingInfo::Moderate) {
+ const auto isHigh = loadingType == BufferLoadingInfo::High;
+ const auto shouldHandleIdle = stamp.audioSinkState == QAudio::IdleState && !isHigh;
+
+ auto &fixedDelay = m_bufferLoadingInfo.delay;
+
+ fixedDelay = shouldHandleIdle ? soundDelay
+ : isHigh ? qMin(soundDelay, fixedDelay)
+ : qMax(soundDelay, fixedDelay);
+
+ if (stamp.timePoint - m_bufferLoadingInfo.timePoint > BufferLoadingMeasureTime
+ || (m_firstFrame && isHigh) || shouldHandleIdle) {
+ const auto targetDelay = isHigh
+ ? (m_timings.maxSoundDelay + m_timings.minSoundDelay) / 2
+ : m_timings.minSoundDelay + DurationBias;
+
+ synchronize(fixedDelay, targetDelay);
+ m_bufferLoadingInfo = { BufferLoadingInfo::Moderate, stamp.timePoint, targetDelay };
+ }
+ }
+}
+
+microseconds AudioRenderer::bufferLoadingTime(const SynchronizationStamp &syncStamp) const
+{
+ Q_ASSERT(m_sink);
+
+ if (syncStamp.audioSinkState == QAudio::IdleState)
+ return microseconds(0);
+
+ const auto bytes = qMax(m_sink->bufferSize() - syncStamp.audioSinkBytesFree, 0);
+
+#ifdef Q_OS_ANDROID
+ // The hack has been added due to QAndroidAudioSink issues (QTBUG-118609).
+ // The method QAndroidAudioSink::bytesFree returns 0 or bufferSize, intermediate values are not
+ // available now; to be fixed.
+ if (bytes == 0)
+ return m_timings.minSoundDelay + MinDesiredBufferTime;
+#endif
+
+ return durationForBytes(bytes);
+}
+
+void AudioRenderer::onAudioSinkStateChanged(QAudio::State state)
+{
+ if (state == QAudio::IdleState && !m_firstFrame)
+ scheduleNextStep();
+}
+
+microseconds AudioRenderer::durationForBytes(qsizetype bytes) const
+{
+ return microseconds(m_format.durationForBytes(static_cast<qint32>(bytes)));
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegaudiorenderer_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer_p.h
new file mode 100644
index 000000000..196cd4fd0
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegaudiorenderer_p.h
@@ -0,0 +1,120 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGAUDIORENDERER_P_H
+#define QFFMPEGAUDIORENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "playbackengine/qffmpegrenderer_p.h"
+
+#include "qaudiobuffer.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAudioOutput;
+class QAudioSink;
+class QFFmpegResampler;
+
+namespace QFFmpeg {
+
+class AudioRenderer : public Renderer
+{
+ Q_OBJECT
+public:
+ AudioRenderer(const TimeController &tc, QAudioOutput *output);
+
+ void setOutput(QAudioOutput *output);
+
+ ~AudioRenderer() override;
+
+protected:
+ using Microseconds = std::chrono::microseconds;
+ struct SynchronizationStamp
+ {
+ QAudio::State audioSinkState = QAudio::IdleState;
+ qsizetype audioSinkBytesFree = 0;
+ qsizetype bufferBytesWritten = 0;
+ TimePoint timePoint = TimePoint::max();
+ };
+
+ struct BufferLoadingInfo
+ {
+ enum Type { Low, Moderate, High };
+ Type type = Moderate;
+ TimePoint timePoint = TimePoint::max();
+ Microseconds delay = Microseconds(0);
+ };
+
+ struct AudioTimings
+ {
+ Microseconds actualBufferDuration = Microseconds(0);
+ Microseconds maxSoundDelay = Microseconds(0);
+ Microseconds minSoundDelay = Microseconds(0);
+ };
+
+ struct BufferedDataWithOffset
+ {
+ QAudioBuffer buffer;
+ qsizetype offset = 0;
+
+ bool isValid() const { return buffer.isValid(); }
+ qsizetype size() const { return buffer.byteCount() - offset; }
+ const char *data() const { return buffer.constData<char>() + offset; }
+ };
+
+ RenderingResult renderInternal(Frame frame) override;
+
+ void onPlaybackRateChanged() override;
+
+ int timerInterval() const override;
+
+ void onPauseChanged() override;
+
+ void freeOutput();
+
+ void updateOutput(const Codec *codec);
+
+ void initResempler(const Codec *codec);
+
+ void onDeviceChanged();
+
+ void updateVolume();
+
+ void updateSynchronization(const SynchronizationStamp &stamp, const Frame &frame);
+
+ Microseconds bufferLoadingTime(const SynchronizationStamp &syncStamp) const;
+
+ void onAudioSinkStateChanged(QAudio::State state);
+
+ Microseconds durationForBytes(qsizetype bytes) const;
+
+private:
+ QPointer<QAudioOutput> m_output;
+ std::unique_ptr<QAudioSink> m_sink;
+ AudioTimings m_timings;
+ BufferLoadingInfo m_bufferLoadingInfo;
+ std::unique_ptr<QFFmpegResampler> m_resampler;
+ QAudioFormat m_format;
+
+ BufferedDataWithOffset m_bufferedData;
+ QIODevice *m_ioDevice = nullptr;
+
+ bool m_deviceChanged = false;
+ bool m_drained = false;
+ bool m_firstFrame = true;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGAUDIORENDERER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec.cpp
new file mode 100644
index 000000000..457b3603d
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec.cpp
@@ -0,0 +1,82 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegcodec_p.h"
+#include "qloggingcategory.h"
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcPlaybackEngineCodec, "qt.multimedia.playbackengine.codec");
+
+namespace QFFmpeg {
+
+Codec::Data::Data(AVCodecContextUPtr context, AVStream *stream, AVFormatContext *formatContext,
+ std::unique_ptr<QFFmpeg::HWAccel> hwAccel)
+ : context(std::move(context)), stream(stream), hwAccel(std::move(hwAccel))
+{
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ pixelAspectRatio = av_guess_sample_aspect_ratio(formatContext, stream, nullptr);
+}
+
+QMaybe<Codec> Codec::create(AVStream *stream, AVFormatContext *formatContext)
+{
+ if (!stream)
+ return { "Invalid stream" };
+
+ const AVCodec *decoder = nullptr;
+ std::unique_ptr<QFFmpeg::HWAccel> hwAccel;
+
+ if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ std::tie(decoder, hwAccel) = HWAccel::findDecoderWithHwAccel(stream->codecpar->codec_id);
+
+ if (!decoder)
+ decoder = QFFmpeg::findAVDecoder(stream->codecpar->codec_id);
+
+ if (!decoder)
+ return { "Failed to find a valid FFmpeg decoder" };
+
+ qCDebug(qLcPlaybackEngineCodec) << "found decoder" << decoder->name << "for id" << decoder->id;
+
+ AVCodecContextUPtr context(avcodec_alloc_context3(decoder));
+ if (!context)
+ return { "Failed to allocate a FFmpeg codec context" };
+
+ if (hwAccel)
+ context->hw_device_ctx = av_buffer_ref(hwAccel->hwDeviceContextAsBuffer());
+
+ if (context->codec_type != AVMEDIA_TYPE_AUDIO && context->codec_type != AVMEDIA_TYPE_VIDEO
+ && context->codec_type != AVMEDIA_TYPE_SUBTITLE) {
+ return { "Unknown codec type" };
+ }
+
+ int ret = avcodec_parameters_to_context(context.get(), stream->codecpar);
+ if (ret < 0)
+ return { "Failed to set FFmpeg codec parameters" };
+
+ // ### This still gives errors about wrong HW formats (as we accept all of them)
+ // But it would be good to get so we can filter out pixel format we don't support natively
+ context->get_format = QFFmpeg::getFormat;
+
+ /* Init the decoder, with reference counting and threading */
+ AVDictionaryHolder opts;
+ av_dict_set(opts, "refcounted_frames", "1", 0);
+ av_dict_set(opts, "threads", "auto", 0);
+ applyExperimentalCodecOptions(decoder, opts);
+
+ ret = avcodec_open2(context.get(), decoder, opts);
+ if (ret < 0)
+ return QString("Failed to open FFmpeg codec context " + err2str(ret));
+
+ return Codec(new Data(std::move(context), stream, formatContext, std::move(hwAccel)));
+}
+
+AVRational Codec::pixelAspectRatio(AVFrame *frame) const
+{
+ // does the same as av_guess_sample_aspect_ratio, but more efficient
+ return d->pixelAspectRatio.num && d->pixelAspectRatio.den ? d->pixelAspectRatio
+ : frame->sample_aspect_ratio;
+}
+
+QT_END_NAMESPACE
+
+} // namespace QFFmpeg
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec_p.h
new file mode 100644
index 000000000..449fb1f65
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec_p.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGCODEC_P_H
+#define QFFMPEGCODEC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qshareddata.h"
+#include "qqueue.h"
+#include "private/qmultimediautils_p.h"
+#include "qffmpeg_p.h"
+#include "qffmpeghwaccel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+class Codec
+{
+ struct Data
+ {
+ Data(AVCodecContextUPtr context, AVStream *stream, AVFormatContext *formatContext,
+ std::unique_ptr<QFFmpeg::HWAccel> hwAccel);
+ QAtomicInt ref;
+ AVCodecContextUPtr context;
+ AVStream *stream = nullptr;
+ AVRational pixelAspectRatio = { 0, 1 };
+ std::unique_ptr<QFFmpeg::HWAccel> hwAccel;
+ };
+
+public:
+ static QMaybe<Codec> create(AVStream *stream, AVFormatContext *formatContext);
+
+ AVRational pixelAspectRatio(AVFrame *frame) const;
+
+ AVCodecContext *context() const { return d->context.get(); }
+ AVStream *stream() const { return d->stream; }
+ uint streamIndex() const { return d->stream->index; }
+ HWAccel *hwAccel() const { return d->hwAccel.get(); }
+ qint64 toMs(qint64 ts) const { return timeStampMs(ts, d->stream->time_base).value_or(0); }
+ qint64 toUs(qint64 ts) const { return timeStampUs(ts, d->stream->time_base).value_or(0); }
+
+private:
+ Codec(Data *data) : d(data) { }
+ QExplicitlySharedDataPointer<Data> d;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGCODEC_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegdemuxer.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegdemuxer.cpp
new file mode 100644
index 000000000..8cced835c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegdemuxer.cpp
@@ -0,0 +1,228 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegdemuxer_p.h"
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+// 4 sec for buffering. TODO: maybe move to env var customization
+static constexpr qint64 MaxBufferedDurationUs = 4'000'000;
+
+// around 4 sec of hdr video
+static constexpr qint64 MaxBufferedSize = 32 * 1024 * 1024;
+
+namespace QFFmpeg {
+
+static Q_LOGGING_CATEGORY(qLcDemuxer, "qt.multimedia.ffmpeg.demuxer");
+
+static qint64 streamTimeToUs(const AVStream *stream, qint64 time)
+{
+ Q_ASSERT(stream);
+
+ const auto res = mul(time * 1000000, stream->time_base);
+ return res ? *res : time;
+}
+
+static qint64 packetEndPos(const AVStream *stream, const Packet &packet)
+{
+ return packet.loopOffset().pos
+ + streamTimeToUs(stream, packet.avPacket()->pts + packet.avPacket()->duration);
+}
+
+Demuxer::Demuxer(AVFormatContext *context, const PositionWithOffset &posWithOffset,
+ const StreamIndexes &streamIndexes, int loops)
+ : m_context(context), m_posWithOffset(posWithOffset), m_loops(loops)
+{
+ qCDebug(qLcDemuxer) << "Create demuxer."
+ << "pos:" << posWithOffset.pos << "loop offset:" << posWithOffset.offset.pos
+ << "loop index:" << posWithOffset.offset.index << "loops:" << loops;
+
+ Q_ASSERT(m_context);
+
+ for (auto i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
+ if (streamIndexes[i] >= 0) {
+ const auto trackType = static_cast<QPlatformMediaPlayer::TrackType>(i);
+ qCDebug(qLcDemuxer) << "Activate demuxing stream" << i << ", trackType:" << trackType;
+ m_streams[streamIndexes[i]] = { trackType };
+ }
+ }
+}
+
+void Demuxer::doNextStep()
+{
+ ensureSeeked();
+
+ Packet packet(m_posWithOffset.offset, AVPacketUPtr{ av_packet_alloc() }, id());
+ if (av_read_frame(m_context, packet.avPacket()) < 0) {
+ ++m_posWithOffset.offset.index;
+
+ const auto loops = m_loops.loadAcquire();
+ if (loops >= 0 && m_posWithOffset.offset.index >= loops) {
+ qCDebug(qLcDemuxer) << "finish demuxing";
+
+ if (!std::exchange(m_buffered, true))
+ emit packetsBuffered();
+
+ setAtEnd(true);
+ } else {
+ m_seeked = false;
+ m_posWithOffset.pos = 0;
+ m_posWithOffset.offset.pos = m_maxPacketsEndPos;
+ m_maxPacketsEndPos = 0;
+
+ ensureSeeked();
+
+ qCDebug(qLcDemuxer) << "Demuxer loops changed. Index:" << m_posWithOffset.offset.index
+ << "Offset:" << m_posWithOffset.offset.pos;
+
+ scheduleNextStep(false);
+ }
+
+ return;
+ }
+
+ auto &avPacket = *packet.avPacket();
+
+ const auto streamIndex = avPacket.stream_index;
+ const auto stream = m_context->streams[streamIndex];
+
+ auto it = m_streams.find(streamIndex);
+ if (it != m_streams.end()) {
+ auto &streamData = it->second;
+
+ const auto endPos = packetEndPos(stream, packet);
+ m_maxPacketsEndPos = qMax(m_maxPacketsEndPos, endPos);
+
+ // Increase buffered metrics as the packet has been processed.
+
+ streamData.bufferedDuration += streamTimeToUs(stream, avPacket.duration);
+ streamData.bufferedSize += avPacket.size;
+ streamData.maxSentPacketsPos = qMax(streamData.maxSentPacketsPos, endPos);
+ updateStreamDataLimitFlag(streamData);
+
+ if (!m_buffered && streamData.isDataLimitReached) {
+ m_buffered = true;
+ emit packetsBuffered();
+ }
+
+ if (!m_firstPacketFound) {
+ m_firstPacketFound = true;
+ const auto pos = streamTimeToUs(stream, avPacket.pts);
+ emit firstPacketFound(std::chrono::steady_clock::now(), pos);
+ }
+
+ auto signal = signalByTrackType(it->second.trackType);
+ emit (this->*signal)(packet);
+ }
+
+ scheduleNextStep(false);
+}
+
+void Demuxer::onPacketProcessed(Packet packet)
+{
+ Q_ASSERT(packet.isValid());
+
+ if (packet.sourceId() != id())
+ return;
+
+ auto &avPacket = *packet.avPacket();
+
+ const auto streamIndex = avPacket.stream_index;
+ const auto stream = m_context->streams[streamIndex];
+ auto it = m_streams.find(streamIndex);
+
+ if (it != m_streams.end()) {
+ auto &streamData = it->second;
+
+ // Decrease buffered metrics as new data (the packet) has been received (buffered)
+
+ streamData.bufferedDuration -= streamTimeToUs(stream, avPacket.duration);
+ streamData.bufferedSize -= avPacket.size;
+ streamData.maxProcessedPacketPos =
+ qMax(streamData.maxProcessedPacketPos, packetEndPos(stream, packet));
+
+ Q_ASSERT(it->second.bufferedDuration >= 0);
+ Q_ASSERT(it->second.bufferedSize >= 0);
+
+ updateStreamDataLimitFlag(streamData);
+ }
+
+ scheduleNextStep();
+}
+
+bool Demuxer::canDoNextStep() const
+{
+ auto isDataLimitReached = [](const auto &streamIndexToData) {
+ return streamIndexToData.second.isDataLimitReached;
+ };
+
+ // Demuxer waits:
+ // - if it's paused
+ // - if the end has been reached
+ // - if streams are empty (probably, should be handled on the initialization)
+ // - if at least one of the streams has reached the data limit (duration or size)
+
+ return PlaybackEngineObject::canDoNextStep() && !isAtEnd() && !m_streams.empty()
+ && std::none_of(m_streams.begin(), m_streams.end(), isDataLimitReached);
+}
+
+void Demuxer::ensureSeeked()
+{
+ if (std::exchange(m_seeked, true))
+ return;
+
+ if ((m_context->ctx_flags & AVFMTCTX_UNSEEKABLE) == 0) {
+ const qint64 seekPos = m_posWithOffset.pos * AV_TIME_BASE / 1000000;
+ auto err = av_seek_frame(m_context, -1, seekPos, AVSEEK_FLAG_BACKWARD);
+
+ if (err < 0) {
+ qCWarning(qLcDemuxer) << "Failed to seek, pos" << seekPos;
+
+ // Drop an error of seeking to initial position of streams with undefined duration.
+ // This needs improvements.
+ if (seekPos != 0 || m_context->duration > 0)
+ emit error(QMediaPlayer::ResourceError,
+ QLatin1StringView("Failed to seek: ") + err2str(err));
+ }
+ }
+
+ setAtEnd(false);
+}
+
+Demuxer::RequestingSignal Demuxer::signalByTrackType(QPlatformMediaPlayer::TrackType trackType)
+{
+ switch (trackType) {
+ case QPlatformMediaPlayer::TrackType::VideoStream:
+ return &Demuxer::requestProcessVideoPacket;
+ case QPlatformMediaPlayer::TrackType::AudioStream:
+ return &Demuxer::requestProcessAudioPacket;
+ case QPlatformMediaPlayer::TrackType::SubtitleStream:
+ return &Demuxer::requestProcessSubtitlePacket;
+ default:
+ Q_ASSERT(!"Unknown track type");
+ }
+
+ return nullptr;
+}
+
+void Demuxer::setLoops(int loopsCount)
+{
+ qCDebug(qLcDemuxer) << "setLoops to demuxer" << loopsCount;
+ m_loops.storeRelease(loopsCount);
+}
+
+void Demuxer::updateStreamDataLimitFlag(StreamData &streamData)
+{
+ const auto packetsPosDiff = streamData.maxSentPacketsPos - streamData.maxProcessedPacketPos;
+ streamData.isDataLimitReached =
+ streamData.bufferedDuration >= MaxBufferedDurationUs
+ || (streamData.bufferedDuration == 0 && packetsPosDiff >= MaxBufferedDurationUs)
+ || streamData.bufferedSize >= MaxBufferedSize;
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegdemuxer_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegdemuxer_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegdemuxer_p.h
new file mode 100644
index 000000000..b72056185
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegdemuxer_p.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGDEMUXER_P_H
+#define QFFMPEGDEMUXER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "playbackengine/qffmpegplaybackengineobject_p.h"
+#include "private/qplatformmediaplayer_p.h"
+#include "playbackengine/qffmpegpacket_p.h"
+#include "playbackengine/qffmpegpositionwithoffset_p.h"
+
+#include <unordered_map>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+class Demuxer : public PlaybackEngineObject
+{
+ Q_OBJECT
+public:
+ Demuxer(AVFormatContext *context, const PositionWithOffset &posWithOffset,
+ const StreamIndexes &streamIndexes, int loops);
+
+ using RequestingSignal = void (Demuxer::*)(Packet);
+ static RequestingSignal signalByTrackType(QPlatformMediaPlayer::TrackType trackType);
+
+ void setLoops(int loopsCount);
+
+public slots:
+ void onPacketProcessed(Packet);
+
+signals:
+ void requestProcessAudioPacket(Packet);
+ void requestProcessVideoPacket(Packet);
+ void requestProcessSubtitlePacket(Packet);
+ void firstPacketFound(TimePoint tp, qint64 trackPos);
+ void packetsBuffered();
+
+private:
+ bool canDoNextStep() const override;
+
+ void doNextStep() override;
+
+ void ensureSeeked();
+
+private:
+ struct StreamData
+ {
+ QPlatformMediaPlayer::TrackType trackType = QPlatformMediaPlayer::TrackType::NTrackTypes;
+ qint64 bufferedDuration = 0;
+ qint64 bufferedSize = 0;
+
+ qint64 maxSentPacketsPos = 0;
+ qint64 maxProcessedPacketPos = 0;
+
+ bool isDataLimitReached = false;
+ };
+
+ void updateStreamDataLimitFlag(StreamData &streamData);
+
+private:
+ AVFormatContext *m_context = nullptr;
+ bool m_seeked = false;
+ bool m_firstPacketFound = false;
+ std::unordered_map<int, StreamData> m_streams;
+ PositionWithOffset m_posWithOffset;
+ qint64 m_maxPacketsEndPos = 0;
+ QAtomicInt m_loops = QMediaPlayer::Once;
+ bool m_buffered = false;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE // QFFMPEGDEMUXER_P_H
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegframe_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegframe_p.h
new file mode 100644
index 000000000..84fe2fead
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegframe_p.h
@@ -0,0 +1,109 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGFRAME_P_H
+#define QFFMPEGFRAME_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpeg_p.h"
+#include "playbackengine/qffmpegcodec_p.h"
+#include "playbackengine/qffmpegpositionwithoffset_p.h"
+#include "QtCore/qsharedpointer.h"
+#include "qpointer.h"
+#include "qobject.h"
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+struct Frame
+{
+ struct Data
+ {
+ Data(const LoopOffset &offset, AVFrameUPtr f, const Codec &codec, qint64, quint64 sourceId)
+ : loopOffset(offset), codec(codec), frame(std::move(f)), sourceId(sourceId)
+ {
+ Q_ASSERT(frame);
+ if (frame->pts != AV_NOPTS_VALUE)
+ pts = codec.toUs(frame->pts);
+ else
+ pts = codec.toUs(frame->best_effort_timestamp);
+
+ if (auto frameDuration = getAVFrameDuration(*frame)) {
+ duration = codec.toUs(frameDuration);
+ } else {
+ const auto &avgFrameRate = codec.stream()->avg_frame_rate;
+ duration = mul(qint64(1000000), { avgFrameRate.den, avgFrameRate.num }).value_or(0);
+ }
+ }
+ Data(const LoopOffset &offset, const QString &text, qint64 pts, qint64 duration,
+ quint64 sourceId)
+ : loopOffset(offset), text(text), pts(pts), duration(duration), sourceId(sourceId)
+ {
+ }
+
+ QAtomicInt ref;
+ LoopOffset loopOffset;
+ std::optional<Codec> codec;
+ AVFrameUPtr frame;
+ QString text;
+ qint64 pts = -1;
+ qint64 duration = -1;
+ quint64 sourceId = 0;
+ };
+ Frame() = default;
+
+ Frame(const LoopOffset &offset, AVFrameUPtr f, const Codec &codec, qint64 pts,
+ quint64 sourceIndex)
+ : d(new Data(offset, std::move(f), codec, pts, sourceIndex))
+ {
+ }
+ Frame(const LoopOffset &offset, const QString &text, qint64 pts, qint64 duration,
+ quint64 sourceIndex)
+ : d(new Data(offset, text, pts, duration, sourceIndex))
+ {
+ }
+ bool isValid() const { return !!d; }
+
+ AVFrame *avFrame() const { return data().frame.get(); }
+ AVFrameUPtr takeAVFrame() { return std::move(data().frame); }
+ const Codec *codec() const { return data().codec ? &data().codec.value() : nullptr; }
+ qint64 pts() const { return data().pts; }
+ qint64 duration() const { return data().duration; }
+ qint64 end() const { return data().pts + data().duration; }
+ QString text() const { return data().text; }
+ quint64 sourceId() const { return data().sourceId; };
+ const LoopOffset &loopOffset() const { return data().loopOffset; };
+ qint64 absolutePts() const { return pts() + loopOffset().pos; }
+ qint64 absoluteEnd() const { return end() + loopOffset().pos; }
+
+private:
+ Data &data() const
+ {
+ Q_ASSERT(d);
+ return *d;
+ }
+
+private:
+ QExplicitlySharedDataPointer<Data> d;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QFFmpeg::Frame);
+
+#endif // QFFMPEGFRAME_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder.cpp
new file mode 100644
index 000000000..fbb75dd44
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder.cpp
@@ -0,0 +1,365 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegmediadataholder_p.h"
+
+#include "qffmpegmediametadata_p.h"
+#include "qffmpegmediaformatinfo_p.h"
+#include "qffmpegioutils_p.h"
+#include "qiodevice.h"
+#include "qdatetime.h"
+#include "qloggingcategory.h"
+
+#include <math.h>
+#include <optional>
+
+extern "C" {
+#include "libavutil/display.h"
+}
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcMediaDataHolder, "qt.multimedia.ffmpeg.mediadataholder")
+
+namespace QFFmpeg {
+
+static std::optional<qint64> streamDuration(const AVStream &stream)
+{
+ const auto &factor = stream.time_base;
+
+ if (stream.duration > 0 && factor.num > 0 && factor.den > 0) {
+ return qint64(1000000) * stream.duration * factor.num / factor.den;
+ }
+
+ // In some cases ffmpeg reports negative duration that is definitely invalid.
+ // However, the correct duration may be read from the metadata.
+
+ if (stream.duration < 0) {
+ qCWarning(qLcMediaDataHolder) << "AVStream duration" << stream.duration
+ << "is invalid. Taking it from the metadata";
+ }
+
+ if (const auto duration = av_dict_get(stream.metadata, "DURATION", nullptr, 0)) {
+ const auto time = QTime::fromString(QString::fromUtf8(duration->value));
+ return qint64(1000) * time.msecsSinceStartOfDay();
+ }
+
+ return {};
+}
+
+static int streamOrientation(const AVStream *stream)
+{
+ Q_ASSERT(stream);
+
+ using SideDataSize = decltype(AVPacketSideData::size);
+ constexpr SideDataSize displayMatrixSize = sizeof(int32_t) * 9;
+ const auto *sideData = streamSideData(stream, AV_PKT_DATA_DISPLAYMATRIX);
+ if (!sideData || sideData->size < displayMatrixSize)
+ return 0;
+
+ auto displayMatrix = reinterpret_cast<const int32_t *>(sideData->data);
+ auto rotation = static_cast<int>(std::round(av_display_rotation_get(displayMatrix)));
+ // Convert counterclockwise rotation angle to clockwise, restricted to 0, 90, 180 and 270
+ if (rotation % 90 != 0)
+ return 0;
+ return rotation < 0 ? -rotation % 360 : -rotation % 360 + 360;
+}
+
+QtVideo::Rotation MediaDataHolder::rotation() const
+{
+ int orientation = m_metaData.value(QMediaMetaData::Orientation).toInt();
+ return static_cast<QtVideo::Rotation>(orientation);
+}
+
+AVFormatContext *MediaDataHolder::avContext()
+{
+ return m_context.get();
+}
+
+int MediaDataHolder::currentStreamIndex(QPlatformMediaPlayer::TrackType trackType) const
+{
+ return m_currentAVStreamIndex[trackType];
+}
+
+static void insertMediaData(QMediaMetaData &metaData, QPlatformMediaPlayer::TrackType trackType,
+ const AVStream *stream)
+{
+ Q_ASSERT(stream);
+ const auto *codecPar = stream->codecpar;
+
+ switch (trackType) {
+ case QPlatformMediaPlayer::VideoStream:
+ metaData.insert(QMediaMetaData::VideoBitRate, (int)codecPar->bit_rate);
+ metaData.insert(QMediaMetaData::VideoCodec,
+ QVariant::fromValue(QFFmpegMediaFormatInfo::videoCodecForAVCodecId(
+ codecPar->codec_id)));
+ metaData.insert(QMediaMetaData::Resolution, QSize(codecPar->width, codecPar->height));
+ metaData.insert(QMediaMetaData::VideoFrameRate,
+ qreal(stream->avg_frame_rate.num) / qreal(stream->avg_frame_rate.den));
+ metaData.insert(QMediaMetaData::Orientation, QVariant::fromValue(streamOrientation(stream)));
+ break;
+ case QPlatformMediaPlayer::AudioStream:
+ metaData.insert(QMediaMetaData::AudioBitRate, (int)codecPar->bit_rate);
+ metaData.insert(QMediaMetaData::AudioCodec,
+ QVariant::fromValue(QFFmpegMediaFormatInfo::audioCodecForAVCodecId(
+ codecPar->codec_id)));
+ break;
+ default:
+ break;
+ }
+};
+
+QPlatformMediaPlayer::TrackType MediaDataHolder::trackTypeFromMediaType(int mediaType)
+{
+ switch (mediaType) {
+ case AVMEDIA_TYPE_AUDIO:
+ return QPlatformMediaPlayer::AudioStream;
+ case AVMEDIA_TYPE_VIDEO:
+ return QPlatformMediaPlayer::VideoStream;
+ case AVMEDIA_TYPE_SUBTITLE:
+ return QPlatformMediaPlayer::SubtitleStream;
+ default:
+ return QPlatformMediaPlayer::NTrackTypes;
+ }
+}
+
+namespace {
+QMaybe<AVFormatContextUPtr, MediaDataHolder::ContextError>
+loadMedia(const QUrl &mediaUrl, QIODevice *stream, const std::shared_ptr<ICancelToken> &cancelToken)
+{
+ const QByteArray url = mediaUrl.toString(QUrl::PreferLocalFile).toUtf8();
+
+ AVFormatContextUPtr context{ avformat_alloc_context() };
+
+ if (stream) {
+ if (!stream->isOpen()) {
+ if (!stream->open(QIODevice::ReadOnly))
+ return MediaDataHolder::ContextError{
+ QMediaPlayer::ResourceError, QLatin1String("Could not open source device.")
+ };
+ }
+ if (!stream->isSequential())
+ stream->seek(0);
+
+ constexpr int bufferSize = 32768;
+ unsigned char *buffer = (unsigned char *)av_malloc(bufferSize);
+ context->pb = avio_alloc_context(buffer, bufferSize, false, stream, &readQIODevice, nullptr,
+ &seekQIODevice);
+ }
+
+ AVDictionaryHolder dict;
+ constexpr auto NetworkTimeoutUs = "5000000";
+ av_dict_set(dict, "timeout", NetworkTimeoutUs, 0);
+
+ context->interrupt_callback.opaque = cancelToken.get();
+ context->interrupt_callback.callback = [](void *opaque) {
+ const auto *cancelToken = static_cast<const ICancelToken *>(opaque);
+ if (cancelToken && cancelToken->isCancelled())
+ return 1;
+ return 0;
+ };
+
+ int ret = 0;
+ {
+ AVFormatContext *contextRaw = context.release();
+ ret = avformat_open_input(&contextRaw, url.constData(), nullptr, dict);
+ context.reset(contextRaw);
+ }
+
+ if (ret < 0) {
+ auto code = QMediaPlayer::ResourceError;
+ if (ret == AVERROR(EACCES))
+ code = QMediaPlayer::AccessDeniedError;
+ else if (ret == AVERROR(EINVAL))
+ code = QMediaPlayer::FormatError;
+
+ return MediaDataHolder::ContextError{ code, QMediaPlayer::tr("Could not open file") };
+ }
+
+ ret = avformat_find_stream_info(context.get(), nullptr);
+ if (ret < 0) {
+ return MediaDataHolder::ContextError{
+ QMediaPlayer::FormatError,
+ QMediaPlayer::tr("Could not find stream information for media file")
+ };
+ }
+
+#ifndef QT_NO_DEBUG
+ av_dump_format(context.get(), 0, url.constData(), 0);
+#endif
+ return context;
+}
+} // namespace
+
+MediaDataHolder::Maybe MediaDataHolder::create(const QUrl &url, QIODevice *stream,
+ const std::shared_ptr<ICancelToken> &cancelToken)
+{
+ QMaybe context = loadMedia(url, stream, cancelToken);
+ if (context) {
+ // MediaDataHolder is wrapped in a shared pointer to interop with signal/slot mechanism
+ return QSharedPointer<MediaDataHolder>{ new MediaDataHolder{ std::move(context.value()), cancelToken } };
+ }
+ return context.error();
+}
+
+MediaDataHolder::MediaDataHolder(AVFormatContextUPtr context,
+ const std::shared_ptr<ICancelToken> &cancelToken)
+ : m_cancelToken{ cancelToken }
+{
+ Q_ASSERT(context);
+
+ m_context = std::move(context);
+ m_isSeekable = !(m_context->ctx_flags & AVFMTCTX_UNSEEKABLE);
+
+ for (unsigned int i = 0; i < m_context->nb_streams; ++i) {
+
+ const auto *stream = m_context->streams[i];
+ const auto trackType = trackTypeFromMediaType(stream->codecpar->codec_type);
+
+ if (trackType == QPlatformMediaPlayer::NTrackTypes)
+ continue;
+
+ if (stream->disposition & AV_DISPOSITION_ATTACHED_PIC)
+ continue; // Ignore attached picture streams because we treat them as metadata
+
+ auto metaData = QFFmpegMetaData::fromAVMetaData(stream->metadata);
+ const bool isDefault = stream->disposition & AV_DISPOSITION_DEFAULT;
+
+ if (trackType != QPlatformMediaPlayer::SubtitleStream) {
+ insertMediaData(metaData, trackType, stream);
+
+ if (isDefault && m_requestedStreams[trackType] < 0)
+ m_requestedStreams[trackType] = m_streamMap[trackType].size();
+ }
+
+ if (auto duration = streamDuration(*stream)) {
+ m_duration = qMax(m_duration, *duration);
+ metaData.insert(QMediaMetaData::Duration, *duration / qint64(1000));
+ }
+
+ m_streamMap[trackType].append({ (int)i, isDefault, metaData });
+ }
+
+ // With some media files, streams may be lacking duration info. Let's
+ // get it from ffmpeg's duration estimation instead.
+ if (m_duration == 0 && m_context->duration > 0ll) {
+ m_duration = m_context->duration;
+ }
+
+ for (auto trackType :
+ { QPlatformMediaPlayer::VideoStream, QPlatformMediaPlayer::AudioStream }) {
+ auto &requestedStream = m_requestedStreams[trackType];
+ auto &streamMap = m_streamMap[trackType];
+
+ if (requestedStream < 0 && !streamMap.empty())
+ requestedStream = 0;
+
+ if (requestedStream >= 0)
+ m_currentAVStreamIndex[trackType] = streamMap[requestedStream].avStreamIndex;
+ }
+
+ updateMetaData();
+}
+
+namespace {
+
+/*!
+ \internal
+
+ Attempt to find an attached picture from the context's streams.
+ This will find ID3v2 pictures on audio files, and also pictures
+ attached to videos.
+ */
+QImage getAttachedPicture(const AVFormatContext *context)
+{
+ if (!context)
+ return {};
+
+ for (unsigned int i = 0; i < context->nb_streams; ++i) {
+ const AVStream* stream = context->streams[i];
+ if (!stream || !(stream->disposition & AV_DISPOSITION_ATTACHED_PIC))
+ continue;
+
+ const AVPacket *compressedImage = &stream->attached_pic;
+ if (!compressedImage || !compressedImage->data || compressedImage->size <= 0)
+ continue;
+
+ // Feed raw compressed data to QImage::fromData, which will decompress it
+ // if it is a recognized format.
+ QImage image = QImage::fromData({ compressedImage->data, compressedImage->size });
+ if (!image.isNull())
+ return image;
+ }
+
+ return {};
+}
+
+}
+
+void MediaDataHolder::updateMetaData()
+{
+ m_metaData = {};
+
+ if (!m_context)
+ return;
+
+ m_metaData = QFFmpegMetaData::fromAVMetaData(m_context->metadata);
+ m_metaData.insert(QMediaMetaData::FileFormat,
+ QVariant::fromValue(QFFmpegMediaFormatInfo::fileFormatForAVInputFormat(
+ m_context->iformat)));
+ m_metaData.insert(QMediaMetaData::Duration, m_duration / qint64(1000));
+
+ if (!m_cachedThumbnail.has_value())
+ m_cachedThumbnail = getAttachedPicture(m_context.get());
+
+ if (!m_cachedThumbnail->isNull())
+ m_metaData.insert(QMediaMetaData::ThumbnailImage, m_cachedThumbnail.value());
+
+ for (auto trackType :
+ { QPlatformMediaPlayer::AudioStream, QPlatformMediaPlayer::VideoStream }) {
+ const auto streamIndex = m_currentAVStreamIndex[trackType];
+ if (streamIndex >= 0)
+ insertMediaData(m_metaData, trackType, m_context->streams[streamIndex]);
+ }
+}
+
+bool MediaDataHolder::setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber)
+{
+ if (!m_context)
+ return false;
+
+ if (streamNumber < 0 || streamNumber >= m_streamMap[type].size())
+ streamNumber = -1;
+ if (m_requestedStreams[type] == streamNumber)
+ return false;
+ m_requestedStreams[type] = streamNumber;
+ const int avStreamIndex = m_streamMap[type].value(streamNumber).avStreamIndex;
+
+ const int oldIndex = m_currentAVStreamIndex[type];
+ qCDebug(qLcMediaDataHolder) << ">>>>> change track" << type << "from" << oldIndex << "to"
+ << avStreamIndex;
+
+ // TODO: maybe add additional verifications
+ m_currentAVStreamIndex[type] = avStreamIndex;
+
+ updateMetaData();
+
+ return true;
+}
+
+int MediaDataHolder::activeTrack(QPlatformMediaPlayer::TrackType type) const
+{
+ return type < QPlatformMediaPlayer::NTrackTypes ? m_requestedStreams[type] : -1;
+}
+
+const QList<MediaDataHolder::StreamInfo> &MediaDataHolder::streamInfo(
+ QPlatformMediaPlayer::TrackType trackType) const
+{
+ Q_ASSERT(trackType < QPlatformMediaPlayer::NTrackTypes);
+
+ return m_streamMap[trackType];
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder_p.h
new file mode 100644
index 000000000..a55b0766a
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegmediadataholder_p.h
@@ -0,0 +1,107 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGMEDIADATAHOLDER_P_H
+#define QFFMPEGMEDIADATAHOLDER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmediametadata.h"
+#include "private/qplatformmediaplayer_p.h"
+#include "qffmpeg_p.h"
+#include "qvideoframe.h"
+#include <private/qmultimediautils_p.h>
+
+#include <array>
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+struct ICancelToken
+{
+ virtual ~ICancelToken() = default;
+ virtual bool isCancelled() const = 0;
+};
+
+using AVFormatContextUPtr = std::unique_ptr<AVFormatContext, AVDeleter<decltype(&avformat_close_input), &avformat_close_input>>;
+
+class MediaDataHolder
+{
+public:
+ struct StreamInfo
+ {
+ int avStreamIndex = -1;
+ bool isDefault = false;
+ QMediaMetaData metaData;
+ };
+
+ struct ContextError
+ {
+ int code = 0;
+ QString description;
+ };
+
+ using StreamsMap = std::array<QList<StreamInfo>, QPlatformMediaPlayer::NTrackTypes>;
+ using StreamIndexes = std::array<int, QPlatformMediaPlayer::NTrackTypes>;
+
+ MediaDataHolder() = default;
+ MediaDataHolder(AVFormatContextUPtr context, const std::shared_ptr<ICancelToken> &cancelToken);
+
+ static QPlatformMediaPlayer::TrackType trackTypeFromMediaType(int mediaType);
+
+ int activeTrack(QPlatformMediaPlayer::TrackType type) const;
+
+ const QList<StreamInfo> &streamInfo(QPlatformMediaPlayer::TrackType trackType) const;
+
+ qint64 duration() const { return m_duration; }
+
+ const QMediaMetaData &metaData() const { return m_metaData; }
+
+ bool isSeekable() const { return m_isSeekable; }
+
+ QtVideo::Rotation rotation() const;
+
+ AVFormatContext *avContext();
+
+ int currentStreamIndex(QPlatformMediaPlayer::TrackType trackType) const;
+
+ using Maybe = QMaybe<QSharedPointer<MediaDataHolder>, ContextError>;
+ static Maybe create(const QUrl &url, QIODevice *stream,
+ const std::shared_ptr<ICancelToken> &cancelToken);
+
+ bool setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber);
+
+private:
+ void updateMetaData();
+
+ std::shared_ptr<ICancelToken> m_cancelToken; // NOTE: Cancel token may be accessed by
+ // AVFormatContext during destruction and
+ // must outlive the context object
+ AVFormatContextUPtr m_context;
+
+ bool m_isSeekable = false;
+
+ StreamIndexes m_currentAVStreamIndex = { -1, -1, -1 };
+ StreamsMap m_streamMap;
+ StreamIndexes m_requestedStreams = { -1, -1, -1 };
+ qint64 m_duration = 0;
+ QMediaMetaData m_metaData;
+ std::optional<QImage> m_cachedThumbnail;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGMEDIADATAHOLDER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegpacket_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegpacket_p.h
new file mode 100644
index 000000000..5e15bf012
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegpacket_p.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGPACKET_P_H
+#define QFFMPEGPACKET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpeg_p.h"
+#include "QtCore/qsharedpointer.h"
+#include "playbackengine/qffmpegpositionwithoffset_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+struct Packet
+{
+ struct Data
+ {
+ Data(const LoopOffset &offset, AVPacketUPtr p, quint64 sourceId)
+ : loopOffset(offset), packet(std::move(p)), sourceId(sourceId)
+ {
+ }
+
+ QAtomicInt ref;
+ LoopOffset loopOffset;
+ AVPacketUPtr packet;
+ quint64 sourceId;
+ };
+ Packet() = default;
+ Packet(const LoopOffset &offset, AVPacketUPtr p, quint64 sourceId)
+ : d(new Data(offset, std::move(p), sourceId))
+ {
+ }
+
+ bool isValid() const { return !!d; }
+ AVPacket *avPacket() const { return d->packet.get(); }
+ const LoopOffset &loopOffset() const { return d->loopOffset; }
+ quint64 sourceId() const { return d->sourceId; }
+
+private:
+ QExplicitlySharedDataPointer<Data> d;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QFFmpeg::Packet)
+
+#endif // QFFMPEGPACKET_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackenginedefs_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackenginedefs_p.h
new file mode 100644
index 000000000..18254ef64
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackenginedefs_p.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGPLAYBACKENGINEDEFS_P_H
+#define QFFMPEGPLAYBACKENGINEDEFS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include "qobject.h"
+#include "qpointer.h"
+
+#include <memory>
+#include <array>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+class PlaybackEngine;
+}
+
+namespace QFFmpeg {
+
+using StreamIndexes = std::array<int, 3>;
+
+class PlaybackEngineObjectsController;
+class PlaybackEngineObject;
+class Demuxer;
+class StreamDecoder;
+class Renderer;
+class SubtitleRenderer;
+class AudioRenderer;
+class VideoRenderer;
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGPLAYBACKENGINEDEFS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackengineobject.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackengineobject.cpp
new file mode 100644
index 000000000..2d23802de
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackengineobject.cpp
@@ -0,0 +1,109 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegplaybackengineobject_p.h"
+
+#include "qtimer.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+static QAtomicInteger<PlaybackEngineObject::Id> PersistentId = 0;
+
+PlaybackEngineObject::PlaybackEngineObject() : m_id(PersistentId.fetchAndAddRelaxed(1)) { }
+
+PlaybackEngineObject::~PlaybackEngineObject()
+{
+ if (thread() != QThread::currentThread())
+ qWarning() << "The playback engine object is being removed in an unexpected thread";
+}
+
+bool PlaybackEngineObject::isPaused() const
+{
+ return m_paused;
+}
+
+void PlaybackEngineObject::setAtEnd(bool isAtEnd)
+{
+ if (m_atEnd.testAndSetRelease(!isAtEnd, isAtEnd) && isAtEnd)
+ emit atEnd();
+}
+
+bool PlaybackEngineObject::isAtEnd() const
+{
+ return m_atEnd;
+}
+
+PlaybackEngineObject::Id PlaybackEngineObject::id() const
+{
+ return m_id;
+}
+
+void PlaybackEngineObject::setPaused(bool isPaused)
+{
+ if (m_paused.testAndSetRelease(!isPaused, isPaused))
+ QMetaObject::invokeMethod(this, &PlaybackEngineObject::onPauseChanged);
+}
+
+void PlaybackEngineObject::kill()
+{
+ m_deleting.storeRelease(true);
+
+ disconnect();
+ deleteLater();
+}
+
+bool PlaybackEngineObject::canDoNextStep() const
+{
+ return !m_paused;
+}
+
+QTimer &PlaybackEngineObject::timer()
+{
+ if (!m_timer) {
+ m_timer = std::make_unique<QTimer>();
+ m_timer->setTimerType(Qt::PreciseTimer);
+ m_timer->setSingleShot(true);
+ connect(m_timer.get(), &QTimer::timeout, this, &PlaybackEngineObject::onTimeout);
+ }
+
+ return *m_timer;
+}
+
+void PlaybackEngineObject::onTimeout()
+{
+ if (!m_deleting && canDoNextStep())
+ doNextStep();
+}
+
+int PlaybackEngineObject::timerInterval() const
+{
+ return 0;
+}
+
+void PlaybackEngineObject::onPauseChanged()
+{
+ scheduleNextStep();
+}
+
+void PlaybackEngineObject::scheduleNextStep(bool allowDoImmediatelly)
+{
+ if (!m_deleting && canDoNextStep()) {
+ const auto interval = timerInterval();
+ if (interval == 0 && allowDoImmediatelly) {
+ timer().stop();
+ doNextStep();
+ } else {
+ timer().start(interval);
+ }
+ } else {
+ timer().stop();
+ }
+}
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegplaybackengineobject_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackengineobject_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackengineobject_p.h
new file mode 100644
index 000000000..02943a55b
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegplaybackengineobject_p.h
@@ -0,0 +1,84 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGPLAYBACKENGINEOBJECT_P_H
+#define QFFMPEGPLAYBACKENGINEOBJECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "playbackengine/qffmpegplaybackenginedefs_p.h"
+#include "qthread.h"
+#include "qatomic.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTimer;
+
+namespace QFFmpeg {
+
+class PlaybackEngineObject : public QObject
+{
+ Q_OBJECT
+public:
+ using TimePoint = std::chrono::steady_clock::time_point;
+ using TimePointOpt = std::optional<TimePoint>;
+ using Id = quint64;
+
+ PlaybackEngineObject();
+
+ ~PlaybackEngineObject();
+
+ bool isPaused() const;
+
+ bool isAtEnd() const;
+
+ void kill();
+
+ void setPaused(bool isPaused);
+
+ Id id() const;
+
+signals:
+ void atEnd();
+
+ void error(int code, const QString &errorString);
+
+protected:
+ QTimer &timer();
+
+ void scheduleNextStep(bool allowDoImmediatelly = true);
+
+ virtual void onPauseChanged();
+
+ virtual bool canDoNextStep() const;
+
+ virtual int timerInterval() const;
+
+ void setAtEnd(bool isAtEnd);
+
+ virtual void doNextStep() { }
+
+private slots:
+ void onTimeout();
+
+private:
+ std::unique_ptr<QTimer> m_timer;
+
+ QAtomicInteger<bool> m_paused = true;
+ QAtomicInteger<bool> m_atEnd = false;
+ QAtomicInteger<bool> m_deleting = false;
+ const Id m_id;
+};
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGPLAYBACKENGINEOBJECT_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegpositionwithoffset_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegpositionwithoffset_p.h
new file mode 100644
index 000000000..a30fdc119
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegpositionwithoffset_p.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef qffmpegpositionwithoffset_p_H
+#define qffmpegpositionwithoffset_p_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtypes.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+struct LoopOffset
+{
+ qint64 pos = 0;
+ int index = 0;
+};
+
+struct PositionWithOffset
+{
+ qint64 pos = 0;
+ LoopOffset offset;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // qffmpegpositionwithoffset_p_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer.cpp
new file mode 100644
index 000000000..e763c786b
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer.cpp
@@ -0,0 +1,216 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegrenderer_p.h"
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+static Q_LOGGING_CATEGORY(qLcRenderer, "qt.multimedia.ffmpeg.renderer");
+
+Renderer::Renderer(const TimeController &tc, const std::chrono::microseconds &seekPosTimeOffset)
+ : m_timeController(tc),
+ m_lastFrameEnd(tc.currentPosition()),
+ m_lastPosition(m_lastFrameEnd),
+ m_seekPos(tc.currentPosition(-seekPosTimeOffset))
+{
+}
+
+void Renderer::syncSoft(TimePoint tp, qint64 trackTime)
+{
+ QMetaObject::invokeMethod(this, [this, tp, trackTime]() {
+ m_timeController.syncSoft(tp, trackTime);
+ scheduleNextStep(true);
+ });
+}
+
+qint64 Renderer::seekPosition() const
+{
+ return m_seekPos;
+}
+
+qint64 Renderer::lastPosition() const
+{
+ return m_lastPosition;
+}
+
+void Renderer::setPlaybackRate(float rate)
+{
+ QMetaObject::invokeMethod(this, [this, rate]() {
+ m_timeController.setPlaybackRate(rate);
+ onPlaybackRateChanged();
+ scheduleNextStep();
+ });
+}
+
+void Renderer::doForceStep()
+{
+ if (m_isStepForced.testAndSetOrdered(false, true))
+ QMetaObject::invokeMethod(this, [this]() {
+ // maybe set m_forceStepMaxPos
+
+ if (isAtEnd()) {
+ setForceStepDone();
+ }
+ else {
+ m_explicitNextFrameTime = Clock::now();
+ scheduleNextStep();
+ }
+ });
+}
+
+bool Renderer::isStepForced() const
+{
+ return m_isStepForced;
+}
+
+void Renderer::setInitialPosition(TimePoint tp, qint64 trackPos)
+{
+ QMetaObject::invokeMethod(this, [this, tp, trackPos]() {
+ Q_ASSERT(m_loopIndex == 0);
+ Q_ASSERT(m_frames.empty());
+
+ m_loopIndex = 0;
+ m_lastPosition.storeRelease(trackPos);
+ m_seekPos.storeRelease(trackPos);
+
+ m_timeController.sync(tp, trackPos);
+ });
+}
+
+void Renderer::onFinalFrameReceived()
+{
+ render({});
+}
+
+void Renderer::render(Frame frame)
+{
+ const auto isFrameOutdated = frame.isValid() && frame.absoluteEnd() < seekPosition();
+
+ if (isFrameOutdated) {
+ qCDebug(qLcRenderer) << "frame outdated! absEnd:" << frame.absoluteEnd() << "absPts"
+ << frame.absolutePts() << "seekPos:" << seekPosition();
+ emit frameProcessed(frame);
+ return;
+ }
+
+ m_frames.enqueue(frame);
+
+ if (m_frames.size() == 1)
+ scheduleNextStep();
+}
+
+void Renderer::onPauseChanged()
+{
+ m_timeController.setPaused(isPaused());
+ PlaybackEngineObject::onPauseChanged();
+}
+
+bool Renderer::canDoNextStep() const
+{
+ return !m_frames.empty() && (m_isStepForced || PlaybackEngineObject::canDoNextStep());
+}
+
+float Renderer::playbackRate() const
+{
+ return m_timeController.playbackRate();
+}
+
+int Renderer::timerInterval() const
+{
+ if (m_frames.empty())
+ return 0;
+
+ auto calculateInterval = [](const TimePoint &nextTime) {
+ using namespace std::chrono;
+
+ const auto delay = nextTime - Clock::now();
+ return std::max(0, static_cast<int>(duration_cast<milliseconds>(delay).count()));
+ };
+
+ if (m_explicitNextFrameTime)
+ return calculateInterval(*m_explicitNextFrameTime);
+
+ if (m_frames.front().isValid())
+ return calculateInterval(m_timeController.timeFromPosition(m_frames.front().absolutePts()));
+
+ if (m_lastFrameEnd > 0)
+ return calculateInterval(m_timeController.timeFromPosition(m_lastFrameEnd));
+
+ return 0;
+}
+
+bool Renderer::setForceStepDone()
+{
+ if (!m_isStepForced.testAndSetOrdered(true, false))
+ return false;
+
+ m_explicitNextFrameTime.reset();
+ emit forceStepDone();
+ return true;
+}
+
+void Renderer::doNextStep()
+{
+ auto frame = m_frames.front();
+
+ if (setForceStepDone()) {
+ // if (frame.isValid() && frame.pts() > m_forceStepMaxPos) {
+ // scheduleNextStep(false);
+ // return;
+ // }
+ }
+
+ const auto result = renderInternal(frame);
+
+ if (result.done) {
+ m_explicitNextFrameTime.reset();
+ m_frames.dequeue();
+
+ if (frame.isValid()) {
+ m_lastPosition.storeRelease(std::max(frame.absolutePts(), lastPosition()));
+
+ // TODO: get rid of m_lastFrameEnd or m_seekPos
+ m_lastFrameEnd = frame.absoluteEnd();
+ m_seekPos.storeRelaxed(m_lastFrameEnd);
+
+ const auto loopIndex = frame.loopOffset().index;
+ if (m_loopIndex < loopIndex) {
+ m_loopIndex = loopIndex;
+ emit loopChanged(id(), frame.loopOffset().pos, m_loopIndex);
+ }
+
+ emit frameProcessed(frame);
+ } else {
+ m_lastPosition.storeRelease(std::max(m_lastFrameEnd, lastPosition()));
+ }
+ } else {
+ m_explicitNextFrameTime = Clock::now() + result.recheckInterval;
+ }
+
+ setAtEnd(result.done && !frame.isValid());
+
+ scheduleNextStep(false);
+}
+
+std::chrono::microseconds Renderer::frameDelay(const Frame &frame, TimePoint timePoint) const
+{
+ return std::chrono::duration_cast<std::chrono::microseconds>(
+ timePoint - m_timeController.timeFromPosition(frame.absolutePts()));
+}
+
+void Renderer::changeRendererTime(std::chrono::microseconds offset)
+{
+ const auto now = Clock::now();
+ const auto pos = m_timeController.positionFromTime(now);
+ m_timeController.sync(now + offset, pos);
+ emit synchronized(id(), now + offset, pos);
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegrenderer_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer_p.h
new file mode 100644
index 000000000..99c5ef1b1
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer_p.h
@@ -0,0 +1,125 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGRENDERER_P_H
+#define QFFMPEGRENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "playbackengine/qffmpegplaybackengineobject_p.h"
+#include "playbackengine/qffmpegtimecontroller_p.h"
+#include "playbackengine/qffmpegframe_p.h"
+
+#include <QtCore/qpointer.h>
+
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+class Renderer : public PlaybackEngineObject
+{
+ Q_OBJECT
+public:
+ using TimePoint = TimeController::TimePoint;
+ using Clock = TimeController::Clock;
+ Renderer(const TimeController &tc, const std::chrono::microseconds &seekPosTimeOffset = {});
+
+ void syncSoft(TimePoint tp, qint64 trackPos);
+
+ qint64 seekPosition() const;
+
+ qint64 lastPosition() const;
+
+ void setPlaybackRate(float rate);
+
+ void doForceStep();
+
+ bool isStepForced() const;
+
+public slots:
+ void setInitialPosition(TimePoint tp, qint64 trackPos);
+
+ void onFinalFrameReceived();
+
+ void render(Frame);
+
+signals:
+ void frameProcessed(Frame);
+
+ void synchronized(Id id, TimePoint tp, qint64 pos);
+
+ void forceStepDone();
+
+ void loopChanged(Id id, qint64 offset, int index);
+
+protected:
+ bool setForceStepDone();
+
+ void onPauseChanged() override;
+
+ bool canDoNextStep() const override;
+
+ int timerInterval() const override;
+
+ virtual void onPlaybackRateChanged() { }
+
+ struct RenderingResult
+ {
+ bool done = true;
+ std::chrono::microseconds recheckInterval = std::chrono::microseconds(0);
+ };
+
+ virtual RenderingResult renderInternal(Frame frame) = 0;
+
+ float playbackRate() const;
+
+ std::chrono::microseconds frameDelay(const Frame &frame,
+ TimePoint timePoint = Clock::now()) const;
+
+ void changeRendererTime(std::chrono::microseconds offset);
+
+ template<typename Output, typename ChangeHandler>
+ void setOutputInternal(QPointer<Output> &actual, Output *desired, ChangeHandler &&changeHandler)
+ {
+ const auto connectionType = thread() == QThread::currentThread()
+ ? Qt::AutoConnection
+ : Qt::BlockingQueuedConnection;
+ auto doer = [desired, changeHandler, &actual]() {
+ const auto prev = std::exchange(actual, desired);
+ if (prev != desired)
+ changeHandler(prev);
+ };
+ QMetaObject::invokeMethod(this, doer, connectionType);
+ }
+
+private:
+ void doNextStep() override;
+
+private:
+ TimeController m_timeController;
+ qint64 m_lastFrameEnd = 0;
+ QAtomicInteger<qint64> m_lastPosition = 0;
+ QAtomicInteger<qint64> m_seekPos = 0;
+
+ int m_loopIndex = 0;
+ QQueue<Frame> m_frames;
+
+ QAtomicInteger<bool> m_isStepForced = false;
+ std::optional<TimePoint> m_explicitNextFrameTime;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGRENDERER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegstreamdecoder.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegstreamdecoder.cpp
new file mode 100644
index 000000000..2f40c53aa
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegstreamdecoder.cpp
@@ -0,0 +1,240 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegstreamdecoder_p.h"
+#include "playbackengine/qffmpegmediadataholder_p.h"
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcStreamDecoder, "qt.multimedia.ffmpeg.streamdecoder");
+
+namespace QFFmpeg {
+
+StreamDecoder::StreamDecoder(const Codec &codec, qint64 absSeekPos)
+ : m_codec(codec),
+ m_absSeekPos(absSeekPos),
+ m_trackType(MediaDataHolder::trackTypeFromMediaType(codec.context()->codec_type))
+{
+ qCDebug(qLcStreamDecoder) << "Create stream decoder, trackType" << m_trackType
+ << "absSeekPos:" << absSeekPos;
+ Q_ASSERT(m_trackType != QPlatformMediaPlayer::NTrackTypes);
+}
+
+StreamDecoder::~StreamDecoder()
+{
+ avcodec_flush_buffers(m_codec.context());
+}
+
+void StreamDecoder::onFinalPacketReceived()
+{
+ decode({});
+}
+
+void StreamDecoder::setInitialPosition(TimePoint, qint64 trackPos)
+{
+ m_absSeekPos = trackPos;
+}
+
+void StreamDecoder::decode(Packet packet)
+{
+ m_packets.enqueue(packet);
+
+ scheduleNextStep();
+}
+
+void StreamDecoder::doNextStep()
+{
+ auto packet = m_packets.dequeue();
+
+ auto decodePacket = [this](Packet packet) {
+ if (trackType() == QPlatformMediaPlayer::SubtitleStream)
+ decodeSubtitle(packet);
+ else
+ decodeMedia(packet);
+ };
+
+ if (packet.isValid() && packet.loopOffset().index != m_offset.index) {
+ decodePacket({});
+
+ qCDebug(qLcStreamDecoder) << "flush buffers due to new loop:" << packet.loopOffset().index;
+
+ avcodec_flush_buffers(m_codec.context());
+ m_offset = packet.loopOffset();
+ }
+
+ decodePacket(packet);
+
+ setAtEnd(!packet.isValid());
+
+ if (packet.isValid())
+ emit packetProcessed(packet);
+
+ scheduleNextStep(false);
+}
+
+QPlatformMediaPlayer::TrackType StreamDecoder::trackType() const
+{
+ return m_trackType;
+}
+
+qint32 StreamDecoder::maxQueueSize(QPlatformMediaPlayer::TrackType type)
+{
+ switch (type) {
+
+ case QPlatformMediaPlayer::VideoStream:
+ return 3;
+ case QPlatformMediaPlayer::AudioStream:
+ return 9;
+ case QPlatformMediaPlayer::SubtitleStream:
+ return 6; /*main packet and closing packet*/
+ default:
+ Q_UNREACHABLE_RETURN(-1);
+ }
+}
+
+void StreamDecoder::onFrameProcessed(Frame frame)
+{
+ if (frame.sourceId() != id())
+ return;
+
+ --m_pendingFramesCount;
+ Q_ASSERT(m_pendingFramesCount >= 0);
+
+ scheduleNextStep();
+}
+
+bool StreamDecoder::canDoNextStep() const
+{
+ const qint32 maxCount = maxQueueSize(m_trackType);
+
+ return !m_packets.empty() && m_pendingFramesCount < maxCount
+ && PlaybackEngineObject::canDoNextStep();
+}
+
+void StreamDecoder::onFrameFound(Frame frame)
+{
+ if (frame.isValid() && frame.absoluteEnd() < m_absSeekPos)
+ return;
+
+ Q_ASSERT(m_pendingFramesCount >= 0);
+ ++m_pendingFramesCount;
+ emit requestHandleFrame(frame);
+}
+
+void StreamDecoder::decodeMedia(Packet packet)
+{
+ auto sendPacketResult = sendAVPacket(packet);
+
+ if (sendPacketResult == AVERROR(EAGAIN)) {
+ // Doc says:
+ // AVERROR(EAGAIN): input is not accepted in the current state - user
+ // must read output with avcodec_receive_frame() (once
+ // all output is read, the packet should be resent, and
+ // the call will not fail with EAGAIN).
+ receiveAVFrames();
+ sendPacketResult = sendAVPacket(packet);
+
+ if (sendPacketResult != AVERROR(EAGAIN))
+ qWarning() << "Unexpected FFmpeg behavior";
+ }
+
+ if (sendPacketResult == 0)
+ receiveAVFrames();
+}
+
+int StreamDecoder::sendAVPacket(Packet packet)
+{
+ return avcodec_send_packet(m_codec.context(), packet.isValid() ? packet.avPacket() : nullptr);
+}
+
+void StreamDecoder::receiveAVFrames()
+{
+ while (true) {
+ auto avFrame = makeAVFrame();
+
+ const auto receiveFrameResult = avcodec_receive_frame(m_codec.context(), avFrame.get());
+
+ if (receiveFrameResult == AVERROR_EOF || receiveFrameResult == AVERROR(EAGAIN))
+ break;
+
+ if (receiveFrameResult < 0) {
+ emit error(QMediaPlayer::FormatError, err2str(receiveFrameResult));
+ break;
+ }
+
+ onFrameFound({ m_offset, std::move(avFrame), m_codec, 0, id() });
+ }
+}
+
+void StreamDecoder::decodeSubtitle(Packet packet)
+{
+ if (!packet.isValid())
+ return;
+ // qCDebug(qLcDecoder) << " decoding subtitle" << "has delay:" <<
+ // (codec->codec->capabilities & AV_CODEC_CAP_DELAY);
+ AVSubtitle subtitle;
+ memset(&subtitle, 0, sizeof(subtitle));
+ int gotSubtitle = 0;
+
+ const int res =
+ avcodec_decode_subtitle2(m_codec.context(), &subtitle, &gotSubtitle, packet.avPacket());
+ // qCDebug(qLcDecoder) << " subtitle got:" << res << gotSubtitle << subtitle.format <<
+ // Qt::hex << (quint64)subtitle.pts;
+ if (res < 0 || !gotSubtitle)
+ return;
+
+ // apparently the timestamps in the AVSubtitle structure are not always filled in
+ // if they are missing, use the packets pts and duration values instead
+ qint64 start, end;
+ if (subtitle.pts == AV_NOPTS_VALUE) {
+ start = m_codec.toUs(packet.avPacket()->pts);
+ end = start + m_codec.toUs(packet.avPacket()->duration);
+ } else {
+ auto pts = timeStampUs(subtitle.pts, AVRational{ 1, AV_TIME_BASE });
+ start = *pts + qint64(subtitle.start_display_time) * 1000;
+ end = *pts + qint64(subtitle.end_display_time) * 1000;
+ }
+
+ if (end <= start) {
+ qWarning() << "Invalid subtitle time";
+ return;
+ }
+ // qCDebug(qLcDecoder) << " got subtitle (" << start << "--" << end << "):";
+ QString text;
+ for (uint i = 0; i < subtitle.num_rects; ++i) {
+ const auto *r = subtitle.rects[i];
+ // qCDebug(qLcDecoder) << " subtitletext:" << r->text << "/" << r->ass;
+ if (i)
+ text += QLatin1Char('\n');
+ if (r->text)
+ text += QString::fromUtf8(r->text);
+ else {
+ const char *ass = r->ass;
+ int nCommas = 0;
+ while (*ass) {
+ if (nCommas == 8)
+ break;
+ if (*ass == ',')
+ ++nCommas;
+ ++ass;
+ }
+ text += QString::fromUtf8(ass);
+ }
+ }
+ text.replace(QLatin1String("\\N"), QLatin1String("\n"));
+ text.replace(QLatin1String("\\n"), QLatin1String("\n"));
+ text.replace(QLatin1String("\r\n"), QLatin1String("\n"));
+ if (text.endsWith(QLatin1Char('\n')))
+ text.chop(1);
+
+ onFrameFound({ m_offset, text, start, end - start, id() });
+
+ // TODO: maybe optimize
+ onFrameFound({ m_offset, QString(), end, 0, id() });
+}
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegstreamdecoder_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegstreamdecoder_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegstreamdecoder_p.h
new file mode 100644
index 000000000..1acc07983
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegstreamdecoder_p.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGSTREAMDECODER_P_H
+#define QFFMPEGSTREAMDECODER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include "playbackengine/qffmpegplaybackengineobject_p.h"
+#include "playbackengine/qffmpegframe_p.h"
+#include "playbackengine/qffmpegpacket_p.h"
+#include "playbackengine/qffmpegpositionwithoffset_p.h"
+#include "private/qplatformmediaplayer_p.h"
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+class StreamDecoder : public PlaybackEngineObject
+{
+ Q_OBJECT
+public:
+ StreamDecoder(const Codec &codec, qint64 absSeekPos);
+
+ ~StreamDecoder();
+
+ QPlatformMediaPlayer::TrackType trackType() const;
+
+ // Maximum number of frames that we are allowed to keep in render queue
+ static qint32 maxQueueSize(QPlatformMediaPlayer::TrackType type);
+
+public slots:
+ void setInitialPosition(TimePoint tp, qint64 trackPos);
+
+ void decode(Packet);
+
+ void onFinalPacketReceived();
+
+ void onFrameProcessed(Frame frame);
+
+signals:
+ void requestHandleFrame(Frame frame);
+
+ void packetProcessed(Packet);
+
+protected:
+ bool canDoNextStep() const override;
+
+ void doNextStep() override;
+
+private:
+ void decodeMedia(Packet);
+
+ void decodeSubtitle(Packet);
+
+ void onFrameFound(Frame frame);
+
+ int sendAVPacket(Packet);
+
+ void receiveAVFrames();
+
+private:
+ Codec m_codec;
+ qint64 m_absSeekPos = 0;
+ const QPlatformMediaPlayer::TrackType m_trackType;
+
+ qint32 m_pendingFramesCount = 0;
+
+ LoopOffset m_offset;
+
+ QQueue<Packet> m_packets;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGSTREAMDECODER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegsubtitlerenderer.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegsubtitlerenderer.cpp
new file mode 100644
index 000000000..789c9b53b
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegsubtitlerenderer.cpp
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegsubtitlerenderer_p.h"
+
+#include "qvideosink.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+SubtitleRenderer::SubtitleRenderer(const TimeController &tc, QVideoSink *sink)
+ : Renderer(tc), m_sink(sink)
+{
+}
+
+void SubtitleRenderer::setOutput(QVideoSink *sink, bool cleanPrevSink)
+{
+ setOutputInternal(m_sink, sink, [cleanPrevSink](QVideoSink *prev) {
+ if (prev && cleanPrevSink)
+ prev->setSubtitleText({});
+ });
+}
+
+SubtitleRenderer::~SubtitleRenderer()
+{
+ if (m_sink)
+ m_sink->setSubtitleText({});
+}
+
+Renderer::RenderingResult SubtitleRenderer::renderInternal(Frame frame)
+{
+ if (m_sink)
+ m_sink->setSubtitleText(frame.isValid() ? frame.text() : QString());
+
+ return {};
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegsubtitlerenderer_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegsubtitlerenderer_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegsubtitlerenderer_p.h
new file mode 100644
index 000000000..805212e83
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegsubtitlerenderer_p.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGSUBTITLERENDERER_P_H
+#define QFFMPEGSUBTITLERENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "playbackengine/qffmpegrenderer_p.h"
+
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoSink;
+
+namespace QFFmpeg {
+
+class SubtitleRenderer : public Renderer
+{
+ Q_OBJECT
+public:
+ SubtitleRenderer(const TimeController &tc, QVideoSink *sink);
+
+ ~SubtitleRenderer() override;
+
+ void setOutput(QVideoSink *sink, bool cleanPrevSink = false);
+
+protected:
+ RenderingResult renderInternal(Frame frame) override;
+
+private:
+ QPointer<QVideoSink> m_sink;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGSUBTITLERENDERER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegtimecontroller.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegtimecontroller.cpp
new file mode 100644
index 000000000..8352384b4
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegtimecontroller.cpp
@@ -0,0 +1,165 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegtimecontroller_p.h"
+
+#include "qglobal.h"
+#include "qdebug.h"
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+TimeController::TimeController()
+{
+ sync();
+}
+
+TimeController::PlaybackRate TimeController::playbackRate() const
+{
+ return m_playbackRate;
+}
+
+void TimeController::setPlaybackRate(PlaybackRate playbackRate)
+{
+ if (playbackRate == m_playbackRate)
+ return;
+
+ Q_ASSERT(playbackRate > 0.f);
+
+ scrollTimeTillNow();
+ m_playbackRate = playbackRate;
+
+ if (m_softSyncData)
+ m_softSyncData = makeSoftSyncData(m_timePoint, m_position, m_softSyncData->dstTimePoint);
+}
+
+void TimeController::sync(qint64 trackPos)
+{
+ sync(Clock::now(), trackPos);
+}
+
+void TimeController::sync(const TimePoint &tp, qint64 pos)
+{
+ m_softSyncData.reset();
+ m_position = TrackTime(pos);
+ m_timePoint = tp;
+}
+
+void TimeController::syncSoft(const TimePoint &tp, qint64 pos, const Clock::duration &fixingTime)
+{
+ const auto srcTime = Clock::now();
+ const auto srcPos = positionFromTime(srcTime, true);
+ const auto dstTime = srcTime + fixingTime;
+
+ m_position = TrackTime(pos);
+ m_timePoint = tp;
+
+ m_softSyncData = makeSoftSyncData(srcTime, TrackTime(srcPos), dstTime);
+}
+
+qint64 TimeController::currentPosition(const Clock::duration &offset) const
+{
+ return positionFromTime(Clock::now() + offset);
+}
+
+void TimeController::setPaused(bool paused)
+{
+ if (m_paused == paused)
+ return;
+
+ scrollTimeTillNow();
+ m_paused = paused;
+}
+
+qint64 TimeController::positionFromTime(TimePoint tp, bool ignorePause) const
+{
+ tp = m_paused && !ignorePause ? m_timePoint : tp;
+
+ if (m_softSyncData && tp < m_softSyncData->dstTimePoint) {
+ const PlaybackRate rate =
+ tp > m_softSyncData->srcTimePoint ? m_softSyncData->internalRate : m_playbackRate;
+
+ return (m_softSyncData->srcPosition
+ + toTrackTime((tp - m_softSyncData->srcTimePoint) * rate))
+ .count();
+ }
+
+ return positionFromTimeInternal(tp).count();
+}
+
+TimeController::TimePoint TimeController::timeFromPosition(qint64 pos, bool ignorePause) const
+{
+ auto position = m_paused && !ignorePause ? m_position : TrackTime(pos);
+
+ if (m_softSyncData && position < m_softSyncData->dstPosition) {
+ const auto rate = position > m_softSyncData->srcPosition ? m_softSyncData->internalRate
+ : m_playbackRate;
+ return m_softSyncData->srcTimePoint
+ + toClockTime((position - m_softSyncData->srcPosition) / rate);
+ }
+
+ return timeFromPositionInternal(position);
+}
+
+TimeController::SoftSyncData TimeController::makeSoftSyncData(const TimePoint &srcTp,
+ const TrackTime &srcPos,
+ const TimePoint &dstTp) const
+{
+ SoftSyncData result;
+ result.srcTimePoint = srcTp;
+ result.srcPosition = srcPos;
+ result.dstTimePoint = dstTp;
+ result.srcPosOffest = srcPos - positionFromTimeInternal(srcTp);
+ result.dstPosition = positionFromTimeInternal(dstTp);
+ result.internalRate =
+ static_cast<PlaybackRate>(toClockTime(TrackTime(result.dstPosition - srcPos)).count())
+ / (dstTp - srcTp).count();
+
+ return result;
+}
+
+TimeController::TrackTime TimeController::positionFromTimeInternal(const TimePoint &tp) const
+{
+ return m_position + toTrackTime((tp - m_timePoint) * m_playbackRate);
+}
+
+TimeController::TimePoint TimeController::timeFromPositionInternal(const TrackTime &pos) const
+{
+ return m_timePoint + toClockTime(TrackTime(pos - m_position) / m_playbackRate);
+}
+
+void TimeController::scrollTimeTillNow()
+{
+ const auto now = Clock::now();
+ if (!m_paused) {
+ m_position = positionFromTimeInternal(now);
+
+ // let's forget outdated syncronizations
+ if (m_softSyncData && m_softSyncData->dstTimePoint <= now)
+ m_softSyncData.reset();
+ } else if (m_softSyncData) {
+ m_softSyncData->dstTimePoint += now - m_timePoint;
+ m_softSyncData->srcTimePoint += now - m_timePoint;
+ }
+
+ m_timePoint = now;
+}
+
+template<typename T>
+TimeController::Clock::duration TimeController::toClockTime(const T &t)
+{
+ return std::chrono::duration_cast<Clock::duration>(t);
+}
+
+template<typename T>
+TimeController::TrackTime TimeController::toTrackTime(const T &t)
+{
+ return std::chrono::duration_cast<TrackTime>(t);
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegtimecontroller_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegtimecontroller_p.h
new file mode 100644
index 000000000..93ced7e64
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegtimecontroller_p.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGTIMECONTROLLER_P_H
+#define QFFMPEGTIMECONTROLLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qglobal.h"
+
+#include <chrono>
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+class TimeController
+{
+ using TrackTime = std::chrono::microseconds;
+
+public:
+ using Clock = std::chrono::steady_clock;
+ using TimePoint = Clock::time_point;
+ using PlaybackRate = float;
+
+ TimeController();
+
+ PlaybackRate playbackRate() const;
+
+ void setPlaybackRate(PlaybackRate playbackRate);
+
+ void sync(qint64 trackPos = 0);
+
+ void sync(const TimePoint &tp, qint64 pos);
+
+ void syncSoft(const TimePoint &tp, qint64 pos,
+ const Clock::duration &fixingTime = std::chrono::seconds(4));
+
+ qint64 currentPosition(const Clock::duration &offset = Clock::duration{ 0 }) const;
+
+ void setPaused(bool paused);
+
+ qint64 positionFromTime(TimePoint tp, bool ignorePause = false) const;
+
+ TimePoint timeFromPosition(qint64 pos, bool ignorePause = false) const;
+
+private:
+ struct SoftSyncData
+ {
+ TimePoint srcTimePoint;
+ TrackTime srcPosition;
+ TimePoint dstTimePoint;
+ TrackTime srcPosOffest;
+ TrackTime dstPosition;
+ PlaybackRate internalRate = 1;
+ };
+
+ SoftSyncData makeSoftSyncData(const TimePoint &srcTp, const TrackTime &srcPos,
+ const TimePoint &dstTp) const;
+
+ TrackTime positionFromTimeInternal(const TimePoint &tp) const;
+
+ TimePoint timeFromPositionInternal(const TrackTime &pos) const;
+
+ void scrollTimeTillNow();
+
+ template<typename T>
+ static Clock::duration toClockTime(const T &t);
+
+ template<typename T>
+ static TrackTime toTrackTime(const T &t);
+
+private:
+ bool m_paused = true;
+ PlaybackRate m_playbackRate = 1;
+ TrackTime m_position;
+ TimePoint m_timePoint = {};
+ std::optional<SoftSyncData> m_softSyncData;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGTIMECONTROLLER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer.cpp
new file mode 100644
index 000000000..72b13064c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "playbackengine/qffmpegvideorenderer_p.h"
+#include "qffmpegvideobuffer_p.h"
+#include "qvideosink.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+VideoRenderer::VideoRenderer(const TimeController &tc, QVideoSink *sink, QtVideo::Rotation rotation)
+ : Renderer(tc), m_sink(sink), m_rotation(rotation)
+{
+}
+
+void VideoRenderer::setOutput(QVideoSink *sink, bool cleanPrevSink)
+{
+ setOutputInternal(m_sink, sink, [cleanPrevSink](QVideoSink *prev) {
+ if (prev && cleanPrevSink)
+ prev->setVideoFrame({});
+ });
+}
+
+VideoRenderer::RenderingResult VideoRenderer::renderInternal(Frame frame)
+{
+ if (!m_sink)
+ return {};
+
+ if (!frame.isValid()) {
+ m_sink->setVideoFrame({});
+ return {};
+ }
+
+ // qCDebug(qLcVideoRenderer) << "RHI:" << accel.isNull() << accel.rhi() << sink->rhi();
+
+ const auto codec = frame.codec();
+ Q_ASSERT(codec);
+
+#ifdef Q_OS_ANDROID
+ // QTBUG-108446
+ // In general case, just creation of frames context is not correct since
+ // frames may require additional specific data for hw contexts, so
+ // just setting of hw_frames_ctx is not enough.
+ // TODO: investigate the case in order to remove or fix the code.
+ if (codec->hwAccel() && !frame.avFrame()->hw_frames_ctx) {
+ HWAccel *hwaccel = codec->hwAccel();
+ AVFrame *avframe = frame.avFrame();
+ if (!hwaccel->hwFramesContext())
+ hwaccel->createFramesContext(AVPixelFormat(avframe->format),
+ { avframe->width, avframe->height });
+
+ if (hwaccel->hwFramesContext())
+ avframe->hw_frames_ctx = av_buffer_ref(hwaccel->hwFramesContextAsBuffer());
+ }
+#endif
+
+ const auto pixelAspectRatio = codec->pixelAspectRatio(frame.avFrame());
+ auto buffer = std::make_unique<QFFmpegVideoBuffer>(frame.takeAVFrame(), pixelAspectRatio);
+ QVideoFrameFormat format(buffer->size(), buffer->pixelFormat());
+ format.setColorSpace(buffer->colorSpace());
+ format.setColorTransfer(buffer->colorTransfer());
+ format.setColorRange(buffer->colorRange());
+ format.setMaxLuminance(buffer->maxNits());
+ format.setRotation(m_rotation);
+ QVideoFrame videoFrame(buffer.release(), format);
+ videoFrame.setStartTime(frame.pts());
+ videoFrame.setEndTime(frame.end());
+ m_sink->setVideoFrame(videoFrame);
+
+ return {};
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegvideorenderer_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer_p.h
new file mode 100644
index 000000000..4866420e8
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegvideorenderer_p.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGVIDEORENDERER_P_H
+#define QFFMPEGVIDEORENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "playbackengine/qffmpegrenderer_p.h"
+
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoSink;
+
+namespace QFFmpeg {
+
+class VideoRenderer : public Renderer
+{
+ Q_OBJECT
+public:
+ VideoRenderer(const TimeController &tc, QVideoSink *sink, QtVideo::Rotation rotation);
+
+ void setOutput(QVideoSink *sink, bool cleanPrevSink = false);
+
+protected:
+ RenderingResult renderInternal(Frame frame) override;
+
+private:
+ QPointer<QVideoSink> m_sink;
+ QtVideo::Rotation m_rotation;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGVIDEORENDERER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp b/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp
new file mode 100644
index 000000000..bf01a4e30
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp
@@ -0,0 +1,697 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qandroidcamera_p.h"
+
+#include <jni.h>
+#include <QMediaFormat>
+#include <memory>
+#include <optional>
+#include <qmediadevices.h>
+#include <qguiapplication.h>
+#include <qscreen.h>
+#include <QDebug>
+#include <qloggingcategory.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qpermissions.h>
+#include <QtCore/private/qandroidextras_p.h>
+#include <private/qcameradevice_p.h>
+#include <QReadWriteLock>
+#include <private/qvideoframeconverter_p.h>
+#include <private/qvideotexturehelper_p.h>
+#include <qffmpegvideobuffer_p.h>
+
+#include <qandroidcameraframe_p.h>
+#include <utility>
+
+extern "C" {
+#include "libavutil/hwcontext.h"
+#include "libavutil/pixfmt.h"
+}
+
+Q_DECLARE_JNI_CLASS(QtCamera2, "org/qtproject/qt/android/multimedia/QtCamera2");
+Q_DECLARE_JNI_CLASS(QtVideoDeviceManager,
+ "org/qtproject/qt/android/multimedia/QtVideoDeviceManager");
+
+Q_DECLARE_JNI_CLASS(AndroidImageFormat, "android/graphics/ImageFormat");
+
+Q_DECLARE_JNI_CLASS(AndroidImage, "android/media/Image")
+Q_DECLARE_JNI_TYPE(AndroidImagePlaneArray, "[Landroid/media/Image$Plane;")
+Q_DECLARE_JNI_CLASS(JavaByteBuffer, "java/nio/ByteBuffer")
+Q_DECLARE_JNI_TYPE(StringArray, "[Ljava/lang/String;")
+
+QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(qLCAndroidCamera, "qt.multimedia.ffmpeg.androidCamera");
+
+typedef QMap<QString, QAndroidCamera *> QAndroidCameraMap;
+Q_GLOBAL_STATIC(QAndroidCameraMap, g_qcameras)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
+
+namespace {
+
+QCameraFormat getDefaultCameraFormat()
+{
+ // default settings
+ QCameraFormatPrivate *defaultFormat = new QCameraFormatPrivate{
+ .pixelFormat = QVideoFrameFormat::Format_YUV420P,
+ .resolution = { 1920, 1080 },
+ .minFrameRate = 12,
+ .maxFrameRate = 30,
+ };
+ return defaultFormat->create();
+}
+
+bool checkCameraPermission()
+{
+ QCameraPermission permission;
+
+ const bool granted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+ if (!granted)
+ qCWarning(qLCAndroidCamera) << "Access to camera not granted!";
+
+ return granted;
+}
+
+int sensorOrientation(QString cameraId)
+{
+ QJniObject deviceManager(QtJniTypes::Traits<QtJniTypes::QtVideoDeviceManager>::className(),
+ QNativeInterface::QAndroidApplication::context());
+
+ if (!deviceManager.isValid()) {
+ qCWarning(qLCAndroidCamera) << "Failed to connect to Qt Video Device Manager.";
+ return 0;
+ }
+
+ return deviceManager.callMethod<jint>("getSensorOrientation",
+ QJniObject::fromString(cameraId).object<jstring>());
+}
+} // namespace
+
+// QAndroidCamera
+
+QAndroidCamera::QAndroidCamera(QCamera *camera) : QPlatformCamera(camera)
+{
+ m_jniCamera = QJniObject(QtJniTypes::Traits<QtJniTypes::QtCamera2>::className(),
+ QNativeInterface::QAndroidApplication::context());
+
+ m_hwAccel = QFFmpeg::HWAccel::create(AVHWDeviceType::AV_HWDEVICE_TYPE_MEDIACODEC);
+ if (camera) {
+ m_cameraDevice = camera->cameraDevice();
+ m_cameraFormat = !camera->cameraFormat().isNull() ? camera->cameraFormat()
+ : getDefaultCameraFormat();
+ updateCameraCharacteristics();
+ }
+
+ if (qApp) {
+ connect(qApp, &QGuiApplication::applicationStateChanged,
+ this, &QAndroidCamera::onApplicationStateChanged);
+ }
+};
+
+QAndroidCamera::~QAndroidCamera()
+{
+ {
+ QWriteLocker locker(rwLock);
+ g_qcameras->remove(m_cameraDevice.id());
+
+ m_jniCamera.callMethod<void>("stopAndClose");
+ setState(State::Closed);
+ }
+
+ m_jniCamera.callMethod<void>("stopBackgroundThread");
+}
+
+void QAndroidCamera::setCamera(const QCameraDevice &camera)
+{
+ const bool active = isActive();
+ if (active)
+ setActive(false);
+
+ m_cameraDevice = camera;
+ updateCameraCharacteristics();
+ m_cameraFormat = getDefaultCameraFormat();
+
+ if (active)
+ setActive(true);
+}
+
+std::optional<int> QAndroidCamera::ffmpegHWPixelFormat() const
+{
+ return QFFmpegVideoBuffer::toAVPixelFormat(m_androidFramePixelFormat);
+}
+
+static void deleteFrame(void *opaque, uint8_t *data)
+{
+ Q_UNUSED(data);
+
+ auto frame = reinterpret_cast<QAndroidCameraFrame *>(opaque);
+
+ if (frame)
+ delete frame;
+}
+
+void QAndroidCamera::frameAvailable(QJniObject image, bool takePhoto)
+{
+ if (!(m_state == State::WaitingStart || m_state == State::Started) && !m_waitingForFirstFrame) {
+ qCWarning(qLCAndroidCamera) << "Received frame when not active... ignoring";
+ qCWarning(qLCAndroidCamera) << "state:" << m_state;
+ image.callMethod<void>("close");
+ return;
+ }
+
+ auto androidFrame = new QAndroidCameraFrame(image);
+ if (!androidFrame->isParsed()) {
+ qCWarning(qLCAndroidCamera) << "Failed to parse frame.. dropping frame";
+ delete androidFrame;
+ return;
+ }
+
+ int timestamp = androidFrame->timestamp();
+ m_androidFramePixelFormat = androidFrame->format();
+ if (m_waitingForFirstFrame) {
+ m_waitingForFirstFrame = false;
+ setState(State::Started);
+ }
+ auto avframe = QFFmpeg::makeAVFrame();
+
+ avframe->width = androidFrame->size().width();
+ avframe->height = androidFrame->size().height();
+ avframe->format = QFFmpegVideoBuffer::toAVPixelFormat(androidFrame->format());
+
+ avframe->extended_data = avframe->data;
+ avframe->pts = androidFrame->timestamp();
+
+ for (int planeNumber = 0; planeNumber < androidFrame->numberPlanes(); planeNumber++) {
+ QAndroidCameraFrame::Plane plane = androidFrame->plane(planeNumber);
+ avframe->linesize[planeNumber] = plane.rowStride;
+ avframe->data[planeNumber] = plane.data;
+ }
+
+ avframe->data[3] = nullptr;
+ avframe->buf[0] = nullptr;
+
+ avframe->opaque_ref = av_buffer_create(NULL, 1, deleteFrame, androidFrame, 0);
+ avframe->extended_data = avframe->data;
+ avframe->pts = timestamp;
+
+ QVideoFrameFormat format(androidFrame->size(), androidFrame->format());
+
+ QVideoFrame videoFrame(new QFFmpegVideoBuffer(std::move(avframe)), format);
+
+ if (lastTimestamp == 0)
+ lastTimestamp = timestamp;
+
+ videoFrame.setRotation(rotation());
+ videoFrame.setMirrored(m_cameraDevice.position() == QCameraDevice::Position::FrontFace);
+
+ videoFrame.setStartTime(lastTimestamp);
+ videoFrame.setEndTime(timestamp);
+
+ if (!takePhoto)
+ emit newVideoFrame(videoFrame);
+ else
+ emit onCaptured(videoFrame);
+
+ lastTimestamp = timestamp;
+}
+
+QtVideo::Rotation QAndroidCamera::rotation()
+{
+ auto screen = QGuiApplication::primaryScreen();
+ auto screenOrientation = screen->orientation();
+ if (screenOrientation == Qt::PrimaryOrientation)
+ screenOrientation = screen->primaryOrientation();
+
+ // Display rotation is the opposite direction of the physical device rotation. We need the
+ // device rotation, that's why Landscape is 270 and InvertedLandscape is 90
+ int deviceOrientation = 0;
+ switch (screenOrientation) {
+ case Qt::PrimaryOrientation:
+ case Qt::PortraitOrientation:
+ break;
+ case Qt::LandscapeOrientation:
+ deviceOrientation = 270;
+ break;
+ case Qt::InvertedPortraitOrientation:
+ deviceOrientation = 180;
+ break;
+ case Qt::InvertedLandscapeOrientation:
+ deviceOrientation = 90;
+ break;
+ }
+
+ int sign = (m_cameraDevice.position() == QCameraDevice::Position::FrontFace) ? 1 : -1;
+ int rotation = (sensorOrientation(m_cameraDevice.id()) - deviceOrientation * sign + 360) % 360;
+
+ return QtVideo::Rotation(rotation);
+}
+
+void QAndroidCamera::setActive(bool active)
+{
+ if (isActive() == active)
+ return;
+
+ if (!m_jniCamera.isValid()) {
+ emit error(QCamera::CameraError, "No connection to Android Camera2 API");
+ return;
+ }
+
+ if (active && checkCameraPermission()) {
+ QWriteLocker locker(rwLock);
+ int width = m_cameraFormat.resolution().width();
+ int height = m_cameraFormat.resolution().height();
+
+ if (width < 0 || height < 0) {
+ m_cameraFormat = getDefaultCameraFormat();
+ width = m_cameraFormat.resolution().width();
+ height = m_cameraFormat.resolution().height();
+ }
+
+ width = FFALIGN(width, 16);
+ height = FFALIGN(height, 16);
+
+ setState(State::WaitingOpen);
+ g_qcameras->insert(m_cameraDevice.id(), this);
+
+ // this should use the camera format.
+ // but there is only 2 fully supported formats on android - JPG and YUV420P
+ // and JPEG is not supported for encoding in FFmpeg, so it's locked for YUV for now.
+ const static int imageFormat =
+ QJniObject::getStaticField<QtJniTypes::AndroidImageFormat, jint>("YUV_420_888");
+ m_jniCamera.callMethod<void>("prepareCamera", jint(width), jint(height),
+ jint(imageFormat), jint(m_cameraFormat.minFrameRate()),
+ jint(m_cameraFormat.maxFrameRate()));
+
+ bool canOpen = m_jniCamera.callMethod<jboolean>(
+ "open", QJniObject::fromString(m_cameraDevice.id()).object<jstring>());
+
+ if (!canOpen) {
+ g_qcameras->remove(m_cameraDevice.id());
+ setState(State::Closed);
+ emit error(QCamera::CameraError,
+ QString("Failed to start camera: ").append(m_cameraDevice.description()));
+ }
+ } else {
+ m_jniCamera.callMethod<void>("stopAndClose");
+ m_jniCamera.callMethod<void>("clearSurfaces");
+ setState(State::Closed);
+ }
+}
+
+void QAndroidCamera::setState(QAndroidCamera::State newState)
+{
+ if (newState == m_state)
+ return;
+
+ bool wasActive = isActive();
+
+ if (newState == State::Started)
+ m_state = State::Started;
+
+ if (m_state == State::Started && newState == State::Closed)
+ m_state = State::Closed;
+
+ if ((m_state == State::WaitingOpen || m_state == State::WaitingStart)
+ && newState == State::Closed) {
+
+ m_state = State::Closed;
+
+ emit error(QCamera::CameraError,
+ QString("Failed to start Camera %1").arg(m_cameraDevice.description()));
+ }
+
+ if (m_state == State::Closed && newState == State::WaitingOpen)
+ m_state = State::WaitingOpen;
+
+ if (m_state == State::WaitingOpen && newState == State::WaitingStart)
+ m_state = State::WaitingStart;
+
+ if (wasActive != isActive())
+ emit activeChanged(isActive());
+}
+
+bool QAndroidCamera::setCameraFormat(const QCameraFormat &format)
+{
+ const auto chosenFormat = format.isNull() ? getDefaultCameraFormat() : format;
+
+ if (chosenFormat == m_cameraFormat || !m_cameraDevice.videoFormats().contains(chosenFormat))
+ return false;
+
+ m_cameraFormat = chosenFormat;
+
+ if (isActive()) {
+ // Restart the camera to set new camera format
+ setActive(false);
+ setActive(true);
+ }
+
+ return true;
+}
+
+void QAndroidCamera::updateCameraCharacteristics()
+{
+ if (m_cameraDevice.id().isEmpty()) {
+ cleanCameraCharacteristics();
+ return;
+ }
+
+ QJniObject deviceManager(QtJniTypes::Traits<QtJniTypes::QtVideoDeviceManager>::className(),
+ QNativeInterface::QAndroidApplication::context());
+
+ if (!deviceManager.isValid()) {
+ qCWarning(qLCAndroidCamera) << "Failed to connect to Qt Video Device Manager.";
+ cleanCameraCharacteristics();
+ return;
+ }
+
+ const float maxZoom = deviceManager.callMethod<jfloat>(
+ "getMaxZoom", QJniObject::fromString(m_cameraDevice.id()).object<jstring>());
+ maximumZoomFactorChanged(maxZoom);
+ if (maxZoom < zoomFactor()) {
+ zoomTo(1.0, -1.0);
+ }
+
+ m_TorchModeSupported = deviceManager.callMethod<jboolean>(
+ "isTorchModeSupported", QJniObject::fromString(m_cameraDevice.id()).object<jstring>());
+
+ m_supportedFlashModes.clear();
+ m_supportedFlashModes.append(QCamera::FlashOff);
+ QJniObject flashModesObj = deviceManager.callMethod<QtJniTypes::StringArray>(
+ "getSupportedFlashModes",
+ QJniObject::fromString(m_cameraDevice.id()).object<jstring>());
+ QJniEnvironment jniEnv;
+ jobjectArray flashModes = flashModesObj.object<jobjectArray>();
+ int size = jniEnv->GetArrayLength(flashModes);
+ for (int i = 0; i < size; ++i) {
+ QJniObject flashModeObj = jniEnv->GetObjectArrayElement(flashModes, i);
+ QString flashMode = flashModeObj.toString();
+ if (flashMode == QLatin1String("auto"))
+ m_supportedFlashModes.append(QCamera::FlashAuto);
+ else if (flashMode == QLatin1String("on"))
+ m_supportedFlashModes.append(QCamera::FlashOn);
+ }
+}
+
+void QAndroidCamera::cleanCameraCharacteristics()
+{
+ maximumZoomFactorChanged(1.0);
+ if (zoomFactor() != 1.0) {
+ zoomTo(1.0, -1.0);
+ }
+ if (torchMode() != QCamera::TorchOff) {
+ setTorchMode(QCamera::TorchOff);
+ }
+ m_TorchModeSupported = false;
+
+ if (flashMode() != QCamera::FlashOff) {
+ setFlashMode(QCamera::FlashOff);
+ }
+ m_supportedFlashModes.clear();
+ m_supportedFlashModes.append(QCamera::FlashOff);
+}
+
+void QAndroidCamera::setFlashMode(QCamera::FlashMode mode)
+{
+ if (!isFlashModeSupported(mode))
+ return;
+
+ QString flashMode;
+ switch (mode) {
+ case QCamera::FlashAuto:
+ flashMode = QLatin1String("auto");
+ break;
+ case QCamera::FlashOn:
+ flashMode = QLatin1String("on");
+ break;
+ case QCamera::FlashOff:
+ default:
+ flashMode = QLatin1String("off");
+ break;
+ }
+
+ m_jniCamera.callMethod<void>("setFlashMode", QJniObject::fromString(flashMode).object<jstring>());
+ flashModeChanged(mode);
+}
+
+bool QAndroidCamera::isFlashModeSupported(QCamera::FlashMode mode) const
+{
+ return m_supportedFlashModes.contains(mode);
+}
+
+bool QAndroidCamera::isFlashReady() const
+{
+ // Android doesn't have an API for that.
+ // Only check if device supports more flash modes than just FlashOff.
+ return m_supportedFlashModes.size() > 1;
+}
+
+bool QAndroidCamera::isTorchModeSupported(QCamera::TorchMode mode) const
+{
+ if (mode == QCamera::TorchOff)
+ return true;
+ else if (mode == QCamera::TorchOn)
+ return m_TorchModeSupported;
+
+ return false;
+}
+
+void QAndroidCamera::setTorchMode(QCamera::TorchMode mode)
+{
+ bool torchMode;
+ if (mode == QCamera::TorchOff) {
+ torchMode = false;
+ } else if (mode == QCamera::TorchOn) {
+ torchMode = true;
+ } else {
+ qWarning() << "Unknown Torch mode";
+ return;
+ }
+ m_jniCamera.callMethod<void>("setTorchMode", jboolean(torchMode));
+ torchModeChanged(mode);
+}
+
+void QAndroidCamera::zoomTo(float factor, float rate)
+{
+ Q_UNUSED(rate);
+ m_jniCamera.callMethod<void>("zoomTo", factor);
+ zoomFactorChanged(factor);
+}
+
+void QAndroidCamera::onApplicationStateChanged()
+{
+ switch (QGuiApplication::applicationState()) {
+ case Qt::ApplicationInactive:
+ if (isActive()) {
+ setActive(false);
+ m_wasActive = true;
+ }
+ break;
+ case Qt::ApplicationActive:
+ if (m_wasActive) {
+ setActive(true);
+ m_wasActive = false;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void QAndroidCamera::onCaptureSessionConfigured()
+{
+ bool canStart = m_jniCamera.callMethod<jboolean>("start", 3);
+ setState(canStart ? State::WaitingStart : State::Closed);
+}
+
+void QAndroidCamera::onCaptureSessionConfigureFailed()
+{
+ setState(State::Closed);
+}
+
+void QAndroidCamera::onCameraOpened()
+{
+ bool canStart = m_jniCamera.callMethod<jboolean>("createSession");
+ setState(canStart ? State::WaitingStart : State::Closed);
+}
+
+void QAndroidCamera::onCameraDisconnect()
+{
+ setState(State::Closed);
+}
+
+void QAndroidCamera::onCameraError(int reason)
+{
+ emit error(QCamera::CameraError,
+ QString("Capture error with Camera %1. Camera2 Api error code: %2")
+ .arg(m_cameraDevice.description())
+ .arg(reason));
+}
+
+void QAndroidCamera::onSessionActive()
+{
+ m_waitingForFirstFrame = true;
+}
+
+void QAndroidCamera::onSessionClosed()
+{
+ m_waitingForFirstFrame = false;
+ setState(State::Closed);
+}
+
+void QAndroidCamera::capture()
+{
+ m_jniCamera.callMethod<void>("takePhoto");
+}
+
+void QAndroidCamera::updateExif(const QString &filename)
+{
+ m_jniCamera.callMethod<void>("saveExifToFile", QJniObject::fromString(filename).object<jstring>());
+}
+
+void QAndroidCamera::onCaptureSessionFailed(int reason, long frameNumber)
+{
+ Q_UNUSED(frameNumber);
+
+ emit error(QCamera::CameraError,
+ QString("Capture session failure with Camera %1. Camera2 Api error code: %2")
+ .arg(m_cameraDevice.description())
+ .arg(reason));
+}
+
+// JNI logic
+
+#define GET_CAMERA(cameraId) \
+ QString key = QJniObject(cameraId).toString(); \
+ QReadLocker locker(rwLock); \
+ if (!g_qcameras->contains(key)) { \
+ qCWarning(qLCAndroidCamera) << "Calling back a QtCamera2 after being destroyed."; \
+ return; \
+ } \
+ QAndroidCamera *camera = g_qcameras->find(key).value();
+
+static void onFrameAvailable(JNIEnv *env, jobject obj, jstring cameraId,
+ QtJniTypes::AndroidImage image)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->frameAvailable(QJniObject(image));
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onFrameAvailable)
+
+static void onPhotoAvailable(JNIEnv *env, jobject obj, jstring cameraId,
+ QtJniTypes::AndroidImage image)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->frameAvailable(QJniObject(image), true);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onPhotoAvailable)
+
+
+static void onCameraOpened(JNIEnv *env, jobject obj, jstring cameraId)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onCameraOpened();
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onCameraOpened)
+
+static void onCameraDisconnect(JNIEnv *env, jobject obj, jstring cameraId)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onCameraDisconnect();
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onCameraDisconnect)
+
+static void onCameraError(JNIEnv *env, jobject obj, jstring cameraId, jint error)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onCameraError(error);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onCameraError)
+
+static void onCaptureSessionConfigured(JNIEnv *env, jobject obj, jstring cameraId)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onCaptureSessionConfigured();
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onCaptureSessionConfigured)
+
+static void onCaptureSessionConfigureFailed(JNIEnv *env, jobject obj, jstring cameraId)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onCaptureSessionConfigureFailed();
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onCaptureSessionConfigureFailed)
+
+static void onSessionActive(JNIEnv *env, jobject obj, jstring cameraId)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onSessionActive();
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onSessionActive)
+
+static void onSessionClosed(JNIEnv *env, jobject obj, jstring cameraId)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onSessionClosed();
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onSessionClosed)
+
+static void onCaptureSessionFailed(JNIEnv *env, jobject obj, jstring cameraId, jint reason,
+ jlong framenumber)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onCaptureSessionFailed(reason, framenumber);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onCaptureSessionFailed)
+
+bool QAndroidCamera::registerNativeMethods()
+{
+ static const bool registered = []() {
+ return QJniEnvironment().registerNativeMethods(
+ QtJniTypes::Traits<QtJniTypes::QtCamera2>::className(),
+ {
+ Q_JNI_NATIVE_METHOD(onCameraOpened),
+ Q_JNI_NATIVE_METHOD(onCameraDisconnect),
+ Q_JNI_NATIVE_METHOD(onCameraError),
+ Q_JNI_NATIVE_METHOD(onCaptureSessionConfigured),
+ Q_JNI_NATIVE_METHOD(onCaptureSessionConfigureFailed),
+ Q_JNI_NATIVE_METHOD(onCaptureSessionFailed),
+ Q_JNI_NATIVE_METHOD(onFrameAvailable),
+ Q_JNI_NATIVE_METHOD(onPhotoAvailable),
+ Q_JNI_NATIVE_METHOD(onSessionActive),
+ Q_JNI_NATIVE_METHOD(onSessionClosed),
+ });
+ }();
+ return registered;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qandroidcamera_p.h b/src/plugins/multimedia/ffmpeg/qandroidcamera_p.h
new file mode 100644
index 000000000..26606a7e0
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidcamera_p.h
@@ -0,0 +1,91 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QANDROIDCAMERA_H
+#define QANDROIDCAMERA_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpeghwaccel_p.h"
+#include <private/qplatformcamera_p.h>
+#include <QObject>
+#include <QJniObject>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoFrame;
+
+class QAndroidCamera : public QPlatformCamera
+{
+ Q_OBJECT
+public:
+ enum State { Closed, WaitingOpen, WaitingStart, Started };
+ explicit QAndroidCamera(QCamera *camera);
+ ~QAndroidCamera() override;
+
+ bool isActive() const override { return m_state == State::Started; }
+ bool isFlashModeSupported(QCamera::FlashMode mode) const override;
+ bool isFlashReady() const override;
+ bool isTorchModeSupported(QCamera::TorchMode mode) const override;
+ void setActive(bool active) override;
+ void setCamera(const QCameraDevice &camera) override;
+ bool setCameraFormat(const QCameraFormat &format) override;
+ void setFlashMode(QCamera::FlashMode mode) override;
+ void setTorchMode(QCamera::TorchMode mode) override;
+ void zoomTo(float factor, float rate) override;
+
+ std::optional<int> ffmpegHWPixelFormat() const override;
+
+ static bool registerNativeMethods();
+
+ void capture();
+ void updateExif(const QString &filename);
+public slots:
+ void onApplicationStateChanged();
+ void onCameraOpened();
+ void onCameraDisconnect();
+ void onCameraError(int error);
+ void frameAvailable(QJniObject image, bool takePhoto = false);
+ void onCaptureSessionConfigured();
+ void onCaptureSessionConfigureFailed();
+ void onCaptureSessionFailed(int reason, long frameNumber);
+ void onSessionActive();
+ void onSessionClosed();
+
+Q_SIGNALS:
+ void onCaptured(const QVideoFrame&);
+
+private:
+ bool isActivating() const { return m_state != State::Closed; }
+
+ void setState(State newState);
+ QtVideo::Rotation rotation();
+ void updateCameraCharacteristics();
+ void cleanCameraCharacteristics();
+
+ State m_state = State::Closed;
+ QCameraDevice m_cameraDevice;
+ long lastTimestamp = 0;
+ QJniObject m_jniCamera;
+
+ std::unique_ptr<QFFmpeg::HWAccel> m_hwAccel;
+
+ QVideoFrameFormat::PixelFormat m_androidFramePixelFormat;
+ QList<QCamera::FlashMode> m_supportedFlashModes;
+ bool m_waitingForFirstFrame = false;
+ bool m_TorchModeSupported = false;
+ bool m_wasActive = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERA_H
diff --git a/src/plugins/multimedia/ffmpeg/qandroidcameraframe.cpp b/src/plugins/multimedia/ffmpeg/qandroidcameraframe.cpp
new file mode 100644
index 000000000..ef088e6d7
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidcameraframe.cpp
@@ -0,0 +1,198 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qandroidcameraframe_p.h"
+#include <jni.h>
+#include <QDebug>
+#include <QtCore/qjnitypes.h>
+#include <QtCore/QLoggingCategory>
+
+Q_DECLARE_JNI_CLASS(AndroidImageFormat, "android/graphics/ImageFormat");
+
+Q_DECLARE_JNI_CLASS(AndroidImage, "android/media/Image")
+Q_DECLARE_JNI_TYPE(AndroidImagePlaneArray, "[Landroid/media/Image$Plane;")
+Q_DECLARE_JNI_CLASS(JavaByteBuffer, "java/nio/ByteBuffer")
+
+QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(qLCAndroidCameraFrame, "qt.multimedia.ffmpeg.android.camera.frame");
+
+bool QAndroidCameraFrame::parse(const QJniObject &frame)
+{
+ QJniEnvironment jniEnv;
+
+ if (!frame.isValid())
+ return false;
+
+ auto planes = frame.callMethod<QtJniTypes::AndroidImagePlaneArray>("getPlanes");
+ if (!planes.isValid())
+ return false;
+
+ int numberPlanes = jniEnv->GetArrayLength(planes.object<jarray>());
+ // create and populate temporary array structure
+ int pixelStrides[numberPlanes];
+ int rowStrides[numberPlanes];
+ int bufferSize[numberPlanes];
+ uint8_t *buffer[numberPlanes];
+
+ auto resetPlane = [&](int index) {
+ if (index < 0 || index > numberPlanes)
+ return;
+
+ rowStrides[index] = 0;
+ pixelStrides[index] = 0;
+ bufferSize[index] = 0;
+ buffer[index] = nullptr;
+ };
+
+ for (int index = 0; index < numberPlanes; index++) {
+ QJniObject plane = jniEnv->GetObjectArrayElement(planes.object<jobjectArray>(), index);
+ if (jniEnv.checkAndClearExceptions() || !plane.isValid()) {
+ resetPlane(index);
+ continue;
+ }
+
+ rowStrides[index] = plane.callMethod<jint>("getRowStride");
+ pixelStrides[index] = plane.callMethod<jint>("getPixelStride");
+
+ auto byteBuffer = plane.callMethod<QtJniTypes::JavaByteBuffer>("getBuffer");
+ if (!byteBuffer.isValid()) {
+ resetPlane(index);
+ continue;
+ }
+
+ // Uses direct access which is garanteed by android to work with
+ // ImageReader bytebuffer
+ buffer[index] = static_cast<uint8_t *>(jniEnv->GetDirectBufferAddress(byteBuffer.object()));
+ bufferSize[index] = byteBuffer.callMethod<jint>("remaining");
+ }
+
+ QVideoFrameFormat::PixelFormat calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
+
+ // finding the image format
+ // the ImageFormats that can happen here are stated here:
+ // https://developer.android.com/reference/android/media/Image#getFormat()
+ int format = frame.callMethod<jint>("getFormat");
+ AndroidImageFormat imageFormat = AndroidImageFormat(format);
+
+ switch (imageFormat) {
+ case AndroidImageFormat::JPEG:
+ calculedPixelFormat = QVideoFrameFormat::Format_Jpeg;
+ break;
+ case AndroidImageFormat::YUV_420_888:
+ if (numberPlanes < 3) {
+ // something went wrong on parsing. YUV_420_888 format must always have 3 planes
+ calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
+ break;
+ }
+ if (pixelStrides[1] == 1)
+ calculedPixelFormat = QVideoFrameFormat::Format_YUV420P;
+ else if (pixelStrides[1] == 2 && abs(buffer[1] - buffer[2]) == 1)
+ // this can be NV21, but it will converted below
+ calculedPixelFormat = QVideoFrameFormat::Format_NV12;
+ break;
+ case AndroidImageFormat::HEIC:
+ // QImage cannot parse HEIC
+ calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
+ break;
+ case AndroidImageFormat::RAW_PRIVATE:
+ case AndroidImageFormat::RAW_SENSOR:
+ // we cannot know raw formats
+ calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
+ break;
+ case AndroidImageFormat::FLEX_RGBA_8888:
+ case AndroidImageFormat::FLEX_RGB_888:
+ // these formats are only returned by Mediacodec.getOutputImage, they are not used as a
+ // Camera2 Image frame return
+ calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
+ break;
+ case AndroidImageFormat::YUV_422_888:
+ case AndroidImageFormat::YUV_444_888:
+ case AndroidImageFormat::YCBCR_P010:
+ // not dealing with these formats, they require higher API levels than the current Qt min
+ calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
+ break;
+ default:
+ calculedPixelFormat = QVideoFrameFormat::Format_Invalid;
+ break;
+ }
+
+ if (calculedPixelFormat == QVideoFrameFormat::Format_Invalid) {
+ qCWarning(qLCAndroidCameraFrame) << "Cannot determine image format!";
+ return false;
+ }
+
+ auto copyPlane = [&](int mapIndex, int arrayIndex) {
+ if (arrayIndex >= numberPlanes)
+ return;
+
+ m_planes[mapIndex].rowStride = rowStrides[arrayIndex];
+ m_planes[mapIndex].size = bufferSize[arrayIndex];
+ m_planes[mapIndex].data = buffer[arrayIndex];
+ };
+
+ switch (calculedPixelFormat) {
+ case QVideoFrameFormat::Format_YUV420P:
+ m_numberPlanes = 3;
+ copyPlane(0, 0);
+ copyPlane(1, 1);
+ copyPlane(2, 2);
+ m_pixelFormat = QVideoFrameFormat::Format_YUV420P;
+ break;
+ case QVideoFrameFormat::Format_NV12:
+ m_numberPlanes = 2;
+ copyPlane(0, 0);
+ copyPlane(1, 1);
+ m_pixelFormat = QVideoFrameFormat::Format_NV12;
+ break;
+ case QVideoFrameFormat::Format_Jpeg:
+ qCWarning(qLCAndroidCameraFrame)
+ << "FFmpeg HW Mediacodec does not encode other than YCbCr formats";
+ // we still parse it to preview the frame
+ m_image = QImage::fromData(buffer[0], bufferSize[0]);
+ m_planes[0].rowStride = m_image.bytesPerLine();
+ m_planes[0].size = m_image.sizeInBytes();
+ m_planes[0].data = m_image.bits();
+ m_pixelFormat = QVideoFrameFormat::pixelFormatFromImageFormat(m_image.format());
+ break;
+ default:
+ break;
+ }
+
+ long timestamp = frame.callMethod<jlong>("getTimestamp");
+ m_timestamp = timestamp / 1000;
+
+ int width = frame.callMethod<jint>("getWidth");
+ int height = frame.callMethod<jint>("getHeight");
+ m_size = QSize(width, height);
+
+ return true;
+}
+
+QAndroidCameraFrame::QAndroidCameraFrame(QJniObject frame)
+ : m_pixelFormat(QVideoFrameFormat::Format_Invalid), m_parsed(parse(frame))
+{
+ if (isParsed()) {
+ // holding the frame java object
+ QJniEnvironment jniEnv;
+ m_frame = jniEnv->NewGlobalRef(frame.object());
+ jniEnv.checkAndClearExceptions();
+ } else if (frame.isValid()) {
+ frame.callMethod<void>("close");
+ }
+}
+
+QAndroidCameraFrame::~QAndroidCameraFrame()
+{
+ if (!isParsed()) // nothing to clean
+ return;
+
+ QJniObject qFrame(m_frame);
+ if (qFrame.isValid())
+ qFrame.callMethod<void>("close");
+
+ QJniEnvironment jniEnv;
+ if (m_frame)
+ jniEnv->DeleteGlobalRef(m_frame);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qandroidcameraframe_p.h b/src/plugins/multimedia/ffmpeg/qandroidcameraframe_p.h
new file mode 100644
index 000000000..23a737f7d
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidcameraframe_p.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QANDROIDCAMERAFRAME_H
+#define QANDROIDCAMERAFRAME_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QVideoFrameFormat>
+#include <QJniObject>
+
+class QAndroidCameraFrame
+{
+public:
+ struct Plane
+ {
+ int pixelStride = 0;
+ int rowStride = 0;
+ int size = 0;
+ uint8_t *data;
+ };
+
+ QAndroidCameraFrame(QJniObject frame);
+ ~QAndroidCameraFrame();
+
+ QVideoFrameFormat::PixelFormat format() const { return m_pixelFormat; }
+ int numberPlanes() const { return m_numberPlanes; }
+ Plane plane(int index) const
+ {
+ if (index < 0 || index > numberPlanes())
+ return {};
+
+ return m_planes[index];
+ }
+ QSize size() const { return m_size; }
+ long timestamp() const { return m_timestamp; }
+
+ bool isParsed() const { return m_parsed; }
+
+private:
+ bool parse(const QJniObject &frame);
+ QVideoFrameFormat::PixelFormat m_pixelFormat;
+
+ QSize m_size = {};
+ long m_timestamp = 0;
+ int m_numberPlanes = 0;
+ Plane m_planes[3]; // 3 max number planes
+ jobject m_frame = nullptr;
+ bool m_parsed = false;
+ QImage m_image;
+
+ enum AndroidImageFormat {
+ RAW_SENSOR = 32,
+ YUV_420_888 = 35,
+ RAW_PRIVATE = 36,
+ YUV_422_888 = 39,
+ YUV_444_888 = 40,
+ FLEX_RGB_888 = 41,
+ FLEX_RGBA_8888 = 42,
+ YCBCR_P010 = 54,
+ JPEG = 256,
+ HEIC = 1212500294
+ };
+};
+
+#endif // QANDROIDCAMERAFRAME_H
diff --git a/src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp b/src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp
new file mode 100644
index 000000000..ed0f2de9d
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qandroidimagecapture_p.h"
+#include <qandroidcamera_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidImageCapture::QAndroidImageCapture(QImageCapture *parent)
+ : QFFmpegImageCapture(parent)
+{
+ connect(this, &QPlatformImageCapture::imageSaved, this, &QAndroidImageCapture::updateExif);
+}
+
+QAndroidImageCapture::~QAndroidImageCapture()
+{
+}
+
+int QAndroidImageCapture::doCapture(const QString &fileName)
+{
+ auto ret = QFFmpegImageCapture::doCapture(fileName);
+ if (ret >= 0) {
+ auto androidCamera = qobject_cast<QAndroidCamera *>(videoSource());
+ if (androidCamera)
+ androidCamera->capture();
+ }
+
+ return ret;
+}
+
+void QAndroidImageCapture::setupVideoSourceConnections()
+{
+ auto androidCamera = qobject_cast<QAndroidCamera *>(videoSource());
+ if (androidCamera)
+ connect(androidCamera, &QAndroidCamera::onCaptured, this, &QAndroidImageCapture::newVideoFrame);
+ else
+ QFFmpegImageCapture::setupVideoSourceConnections();
+}
+
+void QAndroidImageCapture::updateExif(int id, const QString &filename)
+{
+ Q_UNUSED(id);
+ auto androidCamera = qobject_cast<QAndroidCamera *>(videoSource());
+ if (androidCamera)
+ androidCamera->updateExif(filename);
+}
diff --git a/src/plugins/multimedia/ffmpeg/qandroidimagecapture_p.h b/src/plugins/multimedia/ffmpeg/qandroidimagecapture_p.h
new file mode 100644
index 000000000..8a997b595
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidimagecapture_p.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QANDROIDIMAGECAPTURE_H
+#define QANDROIDIMAGECAPTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpegimagecapture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidImageCapture : public QFFmpegImageCapture
+{
+public:
+ QAndroidImageCapture(QImageCapture *parent);
+ ~QAndroidImageCapture() override;
+
+protected:
+ void setupVideoSourceConnections() override;
+ int doCapture(const QString &fileName) override;
+
+private slots:
+ void updateExif(int id, const QString &filename);
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDIMAGECAPTURE_H
diff --git a/src/plugins/multimedia/ffmpeg/qandroidvideodevices.cpp b/src/plugins/multimedia/ffmpeg/qandroidvideodevices.cpp
new file mode 100644
index 000000000..fd4221d55
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidvideodevices.cpp
@@ -0,0 +1,150 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qandroidvideodevices_p.h"
+
+#include <private/qcameradevice_p.h>
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/private/qandroidextras_p.h>
+#include <QtCore/qcoreapplication_platform.h>
+#include <QJniEnvironment>
+#include <jni.h>
+
+static Q_LOGGING_CATEGORY(qLCAndroidVideoDevices, "qt.multimedia.ffmpeg.android.videoDevices")
+
+Q_DECLARE_JNI_CLASS(QtVideoDeviceManager,
+ "org/qtproject/qt/android/multimedia/QtVideoDeviceManager");
+Q_DECLARE_JNI_TYPE(StringArray, "[Ljava/lang/String;")
+Q_DECLARE_JNI_CLASS(AndroidImageFormat, "android/graphics/ImageFormat");
+
+QCameraFormat createCameraFormat(int width, int height, int fpsMin, int fpsMax)
+{
+ QCameraFormatPrivate *format = new QCameraFormatPrivate();
+
+ format->resolution = { width, height };
+
+ format->minFrameRate = fpsMin;
+ format->maxFrameRate = fpsMax;
+
+ format->pixelFormat = QVideoFrameFormat::PixelFormat::Format_YUV420P;
+
+ return format->create();
+}
+
+QList<QCameraDevice> QAndroidVideoDevices::findVideoDevices()
+{
+ QList<QCameraDevice> devices;
+
+ QJniObject deviceManager(QtJniTypes::Traits<QtJniTypes::QtVideoDeviceManager>::className(),
+ QNativeInterface::QAndroidApplication::context());
+
+ if (!deviceManager.isValid()) {
+ qCWarning(qLCAndroidVideoDevices) << "Failed to connect to Qt Video Device Manager.";
+ return devices;
+ }
+
+ QJniObject cameraIdList = deviceManager.callMethod<QtJniTypes::StringArray>("getCameraIdList");
+
+ QJniEnvironment jniEnv;
+ int numCameras = jniEnv->GetArrayLength(cameraIdList.object<jarray>());
+ if (jniEnv.checkAndClearExceptions())
+ return devices;
+
+ for (int cameraIndex = 0; cameraIndex < numCameras; cameraIndex++) {
+
+ QJniObject cameraIdObject =
+ jniEnv->GetObjectArrayElement(cameraIdList.object<jobjectArray>(), cameraIndex);
+ if (jniEnv.checkAndClearExceptions())
+ continue;
+
+ jstring cameraId = cameraIdObject.object<jstring>();
+
+ QCameraDevicePrivate *info = new QCameraDevicePrivate;
+ info->id = cameraIdObject.toString().toUtf8();
+
+ info->orientation = deviceManager.callMethod<jint>("getSensorOrientation", cameraId);
+
+ int facing = deviceManager.callMethod<jint>("getLensFacing", cameraId);
+
+ const int LENS_FACING_FRONT = 0;
+ const int LENS_FACING_BACK = 1;
+ const int LENS_FACING_EXTERNAL = 2;
+
+ switch (facing) {
+ case LENS_FACING_EXTERNAL:
+ case LENS_FACING_BACK:
+ info->position = QCameraDevice::BackFace;
+ info->description = QString("Rear Camera: %1").arg(cameraIndex);
+ break;
+ case LENS_FACING_FRONT:
+ info->position = QCameraDevice::FrontFace;
+ info->description = QString("Front Camera: %1").arg(cameraIndex);
+ break;
+ }
+
+ QJniObject fpsRangesObject =
+ deviceManager.callMethod<QtJniTypes::StringArray>("getFpsRange", cameraId);
+ jobjectArray fpsRanges = fpsRangesObject.object<jobjectArray>();
+
+ int numRanges = jniEnv->GetArrayLength(fpsRanges);
+ if (jniEnv.checkAndClearExceptions())
+ continue;
+
+ int maxFps = 0, minFps = 0;
+
+ for (int rangeIndex = 0; rangeIndex < numRanges; rangeIndex++) {
+
+ QJniObject rangeString = jniEnv->GetObjectArrayElement(fpsRanges, rangeIndex);
+ if (jniEnv.checkAndClearExceptions())
+ continue;
+
+ QString range = rangeString.toString();
+
+ range = range.remove("[");
+ range = range.remove("]");
+
+ auto split = range.split(",");
+
+ int min = split[0].toInt();
+ int max = split[1].toInt();
+
+ if (max > maxFps) {
+ maxFps = max;
+ minFps = min;
+ }
+ }
+
+ const static int imageFormat =
+ QJniObject::getStaticField<QtJniTypes::AndroidImageFormat, jint>("YUV_420_888");
+
+ QJniObject sizesObject = deviceManager.callMethod<QtJniTypes::StringArray>(
+ "getStreamConfigurationsSizes", cameraId, imageFormat);
+
+ jobjectArray streamSizes = sizesObject.object<jobjectArray>();
+ int numSizes = jniEnv->GetArrayLength(streamSizes);
+ if (jniEnv.checkAndClearExceptions())
+ continue;
+
+ for (int sizesIndex = 0; sizesIndex < numSizes; sizesIndex++) {
+
+ QJniObject sizeStringObject = jniEnv->GetObjectArrayElement(streamSizes, sizesIndex);
+ if (jniEnv.checkAndClearExceptions())
+ continue;
+
+ QString sizeString = sizeStringObject.toString();
+
+ auto split = sizeString.split("x");
+
+ int width = split[0].toInt();
+ int height = split[1].toInt();
+
+ info->videoFormats.append(createCameraFormat(width, height, minFps, maxFps));
+ }
+
+ devices.push_back(info->create());
+ }
+
+ return devices;
+}
diff --git a/src/plugins/multimedia/ffmpeg/qandroidvideodevices_p.h b/src/plugins/multimedia/ffmpeg/qandroidvideodevices_p.h
new file mode 100644
index 000000000..f89ea7f05
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qandroidvideodevices_p.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFANDROIDVIDEODEVICES_H
+#define QFANDROIDVIDEODEVICES_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QObject>
+#include <private/qplatformvideodevices_p.h>
+
+class QAndroidVideoDevices : public QPlatformVideoDevices
+{
+ Q_OBJECT
+public:
+ QAndroidVideoDevices(QPlatformMediaIntegration *integration)
+ : QPlatformVideoDevices(integration), m_videoDevices(findVideoDevices()){};
+
+ QList<QCameraDevice> videoDevices() const override { return m_videoDevices; }
+
+private:
+ QList<QCameraDevice> findVideoDevices();
+ QList<QCameraDevice> m_videoDevices;
+};
+
+#endif // QFANDROIDVIDEODEVICES_H
diff --git a/src/plugins/multimedia/ffmpeg/qavfcamera.mm b/src/plugins/multimedia/ffmpeg/qavfcamera.mm
index 11eb4dd6a..38b743e65 100644
--- a/src/plugins/multimedia/ffmpeg/qavfcamera.mm
+++ b/src/plugins/multimedia/ffmpeg/qavfcamera.mm
@@ -1,49 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qavfcamera_p.h>
#include <qpointer.h>
#include <qmediacapturesession.h>
#include <private/qplatformmediacapture_p.h>
#include "avfcamerautility_p.h"
#include "qavfhelpers_p.h"
+#include "avfcameradebug_p.h"
+#include "qavfsamplebufferdelegate_p.h"
#include <qvideosink.h>
-#include <private/qrhi_p.h>
+#include <rhi/qrhi.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qpermissions.h>
#define AVMediaType XAVMediaType
#include "qffmpegvideobuffer_p.h"
#include "qffmpegvideosink_p.h"
@@ -53,144 +21,16 @@ extern "C" {
}
#undef AVMediaType
-
-
-#import <AVFoundation/AVFoundation.h>
-#include <CoreVideo/CoreVideo.h>
-
-static void releaseHwFrame(void */*opaque*/, uint8_t *data)
-{
- CVPixelBufferRelease(CVPixelBufferRef(data));
-}
-
-// Make sure this is compatible with the layout used in ffmpeg's hwcontext_videotoolbox
-static AVFrame *allocHWFrame(AVBufferRef *hwContext, const CVPixelBufferRef &pixbuf)
-{
- AVHWFramesContext *ctx = (AVHWFramesContext*)hwContext->data;
- AVFrame *frame = av_frame_alloc();
- frame->hw_frames_ctx = av_buffer_ref(hwContext);
- frame->extended_data = frame->data;
-
- frame->buf[0] = av_buffer_create((uint8_t *)pixbuf, 1, releaseHwFrame, NULL, 0);
- frame->data[3] = (uint8_t *)pixbuf;
- CVPixelBufferRetain(pixbuf);
- frame->width = ctx->width;
- frame->height = ctx->height;
- frame->format = AV_PIX_FMT_VIDEOTOOLBOX;
- if (frame->width != (int)CVPixelBufferGetWidth(pixbuf) ||
- frame->height != (int)CVPixelBufferGetHeight(pixbuf)) {
- // This can happen while changing camera format
- av_frame_free(&frame);
- return nullptr;
- }
- return frame;
-}
-
-static AVAuthorizationStatus m_cameraAuthorizationStatus = AVAuthorizationStatusNotDetermined;
-
-@interface QAVFSampleBufferDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
-
-- (QAVFSampleBufferDelegate *) initWithCamera:(QAVFCamera *)renderer;
-
-- (void) captureOutput:(AVCaptureOutput *)captureOutput
- didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
- fromConnection:(AVCaptureConnection *)connection;
-
-- (void) setHWAccel:(QFFmpeg::HWAccel *)accel;
-
-@end
-
-@implementation QAVFSampleBufferDelegate
-{
-@private
- QAVFCamera *m_camera;
- AVBufferRef *hwFramesContext;
- QFFmpeg::HWAccel m_accel;
- qint64 startTime;
- qint64 baseTime;
-}
-
-- (QAVFSampleBufferDelegate *) initWithCamera:(QAVFCamera *)renderer
-{
- if (!(self = [super init]))
- return nil;
-
- m_camera = renderer;
- hwFramesContext = nullptr;
- startTime = 0;
- baseTime = 0;
- return self;
-}
-
-- (void)captureOutput:(AVCaptureOutput *)captureOutput
- didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
- fromConnection:(AVCaptureConnection *)connection
-{
- Q_UNUSED(connection);
- Q_UNUSED(captureOutput);
-
- // NB: on iOS captureOutput/connection can be nil (when recording a video -
- // avfmediaassetwriter).
-
- CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
-
- CMTime time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
- qint64 frameTime = time.timescale ? time.value*1000/time.timescale : 0;
- if (baseTime == 0) {
- // drop the first frame to get a valid frame start time
- baseTime = frameTime;
- startTime = 0;
- return;
- }
-
- AVFrame *avFrame = allocHWFrame(m_accel.hwFramesContextAsBuffer(), imageBuffer);
- if (!avFrame)
- return;
-
-#ifdef USE_SW_FRAMES
- auto *swFrame = av_frame_alloc();
- /* retrieve data from GPU to CPU */
- int ret = av_hwframe_transfer_data(swFrame, avFrame, 0);
- if (ret < 0) {
- qWarning() << "Error transferring the data to system memory\n";
- av_frame_unref(swFrame);
- } else {
- av_frame_unref(avFrame);
- avFrame = swFrame;
- }
-#endif
-
- QVideoFrameFormat format = QAVFHelpers::videoFormatForImageBuffer(imageBuffer);
- if (!format.isValid()) {
- av_frame_unref(avFrame);
- return;
- }
-
- avFrame->pts = startTime;
-
- QFFmpegVideoBuffer *buffer = new QFFmpegVideoBuffer(avFrame);
- QVideoFrame frame(buffer, format);
- frame.setStartTime(startTime);
- frame.setEndTime(frameTime);
- startTime = frameTime;
-
- m_camera->syncHandleFrame(frame);
-}
-
-- (void) setHWAccel:(QFFmpeg::HWAccel *)accel
-{
- m_accel = *accel;
-}
-
-@end
-
QT_BEGIN_NAMESPACE
+using namespace QFFmpeg;
+
QAVFCamera::QAVFCamera(QCamera *parent)
: QAVFCameraBase(parent)
{
m_captureSession = [[AVCaptureSession alloc] init];
- m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc] initWithCamera:this];
+ m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc]
+ initWithFrameHandler:[this](const QVideoFrame &frame) { syncHandleFrame(frame); }];
}
QAVFCamera::~QAVFCamera()
@@ -201,53 +41,19 @@ QAVFCamera::~QAVFCamera()
[m_captureSession release];
}
-void QAVFCamera::requestCameraPermissionIfNeeded()
+bool QAVFCamera::checkCameraPermission()
{
- if (m_cameraAuthorizationStatus == AVAuthorizationStatusAuthorized)
- return;
+ const QCameraPermission permission;
+ const bool granted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
+ if (!granted)
+ qWarning() << "Access to camera not granted";
- switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
- {
- case AVAuthorizationStatusAuthorized:
- {
- m_cameraAuthorizationStatus = AVAuthorizationStatusAuthorized;
- break;
- }
- case AVAuthorizationStatusNotDetermined:
- {
- m_cameraAuthorizationStatus = AVAuthorizationStatusNotDetermined;
- QPointer<QAVFCamera> guard(this);
- [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (guard)
- cameraAuthorizationChanged(granted);
- });
- }];
- break;
- }
- case AVAuthorizationStatusDenied:
- case AVAuthorizationStatusRestricted:
- {
- m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
- return;
- }
- }
-}
-
-void QAVFCamera::cameraAuthorizationChanged(bool authorized)
-{
- if (authorized) {
- m_cameraAuthorizationStatus = AVAuthorizationStatusAuthorized;
- } else {
- m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
- qWarning() << "User has denied access to camera";
- }
+ return granted;
}
void QAVFCamera::updateVideoInput()
{
- requestCameraPermissionIfNeeded();
- if (m_cameraAuthorizationStatus != AVAuthorizationStatusAuthorized)
+ if (!checkCameraPermission())
return;
[m_captureSession beginConfiguration];
@@ -255,7 +61,7 @@ void QAVFCamera::updateVideoInput()
attachVideoInputDevice();
if (!m_videoDataOutput) {
- m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
+ m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
// Configure video output
m_delegateQueue = dispatch_queue_create("vf_queue", nullptr);
@@ -342,8 +148,7 @@ void QAVFCamera::setActive(bool active)
{
if (m_active == active)
return;
- requestCameraPermissionIfNeeded();
- if (m_cameraAuthorizationStatus != AVAuthorizationStatusAuthorized)
+ if (!checkCameraPermission())
return;
m_active = active;
@@ -374,8 +179,7 @@ void QAVFCamera::setCamera(const QCameraDevice &camera)
m_cameraDevice = camera;
- requestCameraPermissionIfNeeded();
- if (m_cameraAuthorizationStatus == AVAuthorizationStatusAuthorized)
+ if (checkCameraPermission())
updateVideoInput();
setCameraFormat({});
}
@@ -385,58 +189,138 @@ bool QAVFCamera::setCameraFormat(const QCameraFormat &format)
if (m_cameraFormat == format && !format.isNull())
return true;
- QAVFCameraBase::setCameraFormat(format);
+ if (!QAVFCameraBase::setCameraFormat(format))
+ return false;
+
updateCameraFormat();
return true;
}
void QAVFCamera::updateCameraFormat()
{
+ m_framePixelFormat = QVideoFrameFormat::Format_Invalid;
+
AVCaptureDevice *captureDevice = device();
if (!captureDevice)
return;
- uint avPixelFormat = 0;
- AVCaptureDeviceFormat *newFormat = qt_convert_to_capture_device_format(captureDevice, m_cameraFormat);
+ AVCaptureDeviceFormat *newFormat = qt_convert_to_capture_device_format(
+ captureDevice, m_cameraFormat, &isCVFormatSupported);
+
+ if (!newFormat)
+ newFormat = qt_convert_to_capture_device_format(captureDevice, m_cameraFormat);
+
+ std::uint32_t cvPixelFormat = 0;
if (newFormat) {
qt_set_active_format(captureDevice, newFormat, false);
- avPixelFormat = setPixelFormat(m_cameraFormat.pixelFormat());
+ const auto captureDeviceCVFormat =
+ CMVideoFormatDescriptionGetCodecType(newFormat.formatDescription);
+ cvPixelFormat = setPixelFormat(m_cameraFormat.pixelFormat(), captureDeviceCVFormat);
+ if (captureDeviceCVFormat != cvPixelFormat) {
+ qCWarning(qLcCamera) << "Output CV format differs with capture device format!"
+ << cvPixelFormat << cvFormatToString(cvPixelFormat) << "vs"
+ << captureDeviceCVFormat
+ << cvFormatToString(captureDeviceCVFormat);
+
+ m_framePixelFormat = QAVFHelpers::fromCVPixelFormat(cvPixelFormat);
+ }
+ } else {
+ qWarning() << "Cannot find AVCaptureDeviceFormat; Did you use format from another camera?";
}
- hwAccel = QFFmpeg::HWAccel(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
- hwAccel.createFramesContext(av_map_videotoolbox_format_to_pixfmt(avPixelFormat), m_cameraFormat.resolution());
- [m_sampleBufferDelegate setHWAccel:&hwAccel];
+ const AVPixelFormat avPixelFormat = av_map_videotoolbox_format_to_pixfmt(cvPixelFormat);
+
+ std::unique_ptr<HWAccel> hwAccel;
+
+ if (avPixelFormat == AV_PIX_FMT_NONE) {
+ qCWarning(qLcCamera) << "Videotoolbox doesn't support cvPixelFormat:" << cvPixelFormat
+ << cvFormatToString(cvPixelFormat)
+ << "Camera pix format:" << m_cameraFormat.pixelFormat();
+ } else {
+ hwAccel = HWAccel::create(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
+ qCDebug(qLcCamera) << "Create VIDEOTOOLBOX hw context" << hwAccel.get() << "for camera";
+ }
+
+ if (hwAccel) {
+ hwAccel->createFramesContext(avPixelFormat, adjustedResolution());
+ m_hwPixelFormat = hwAccel->hwFormat();
+ } else {
+ m_hwPixelFormat = AV_PIX_FMT_NONE;
+ }
+
+ [m_sampleBufferDelegate setHWAccel:std::move(hwAccel)];
+ [m_sampleBufferDelegate setVideoFormatFrameRate:m_cameraFormat.maxFrameRate()];
}
-uint QAVFCamera::setPixelFormat(const QVideoFrameFormat::PixelFormat pixelFormat)
+uint32_t QAVFCamera::setPixelFormat(QVideoFrameFormat::PixelFormat cameraPixelFormat,
+ uint32_t inputCvPixFormat)
{
- // Default to 32BGRA pixel formats on the viewfinder, in case the requested
- // format can't be used (shouldn't happen unless the developers sets a wrong camera
- // format on the camera).
- unsigned avPixelFormat = kCVPixelFormatType_32BGRA;
- if (!QAVFHelpers::toCVPixelFormat(pixelFormat, avPixelFormat))
- qWarning() << "QCamera::setCameraFormat: couldn't convert requested pixel format, using ARGB32";
-
- bool isSupported = false;
- NSArray *supportedPixelFormats = m_videoDataOutput.availableVideoCVPixelFormatTypes;
- for (NSNumber *currentPixelFormat in supportedPixelFormats)
- {
- if ([currentPixelFormat unsignedIntValue] == avPixelFormat) {
- isSupported = true;
- break;
+ auto bestScore = MinAVScore;
+ NSNumber *bestFormat = nullptr;
+ for (NSNumber *cvPixFmtNumber in m_videoDataOutput.availableVideoCVPixelFormatTypes) {
+ auto cvPixFmt = [cvPixFmtNumber unsignedIntValue];
+ const auto pixFmt = QAVFHelpers::fromCVPixelFormat(cvPixFmt);
+ if (pixFmt == QVideoFrameFormat::Format_Invalid)
+ continue;
+
+ auto score = DefaultAVScore;
+ if (cvPixFmt == inputCvPixFormat)
+ score += 100;
+ if (pixFmt == cameraPixelFormat)
+ score += 10;
+ // if (cvPixFmt == kCVPixelFormatType_32BGRA)
+ // score += 1;
+
+ // This flag determines priorities of using ffmpeg hw frames or
+ // the exact camera format match.
+ // Maybe configure more, e.g. by some env var?
+ constexpr bool ShouldSuppressNotSupportedByFFmpeg = false;
+
+ if (!isCVFormatSupported(cvPixFmt))
+ score -= ShouldSuppressNotSupportedByFFmpeg ? 100000 : 5;
+
+ // qDebug() << "----FMT:" << pixFmt << cvPixFmt << score;
+
+ if (score > bestScore) {
+ bestScore = score;
+ bestFormat = cvPixFmtNumber;
}
}
- if (isSupported) {
- NSDictionary* outputSettings = @{
- (NSString *)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:avPixelFormat],
- (NSString *)kCVPixelBufferMetalCompatibilityKey: @true
- };
- m_videoDataOutput.videoSettings = outputSettings;
- } else {
- qWarning() << "QCamera::setCameraFormat: requested pixel format not supported. Did you use a camera format from another camera?";
+ if (!bestFormat) {
+ qWarning() << "QCamera::setCameraFormat: availableVideoCVPixelFormatTypes empty";
+ return 0;
}
- return avPixelFormat;
+
+ if (bestScore < DefaultAVScore)
+ qWarning() << "QCamera::setCameraFormat: Cannot find hw FFmpeg supported cv pix format";
+
+ NSDictionary *outputSettings = @{
+ (NSString *)kCVPixelBufferPixelFormatTypeKey : bestFormat,
+ (NSString *)kCVPixelBufferMetalCompatibilityKey : @true
+ };
+ m_videoDataOutput.videoSettings = outputSettings;
+
+ return [bestFormat unsignedIntValue];
+}
+
+QSize QAVFCamera::adjustedResolution() const
+{
+ // Check, that we have matching dimesnions.
+ QSize resolution = m_cameraFormat.resolution();
+ AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
+ if (!connection.supportsVideoOrientation)
+ return resolution;
+
+ // Either portrait but actually sizes of landscape, or
+ // landscape with dimensions of portrait - not what
+ // sample delegate will report (it depends on videoOrientation set).
+ const bool isPortraitOrientation = connection.videoOrientation == AVCaptureVideoOrientationPortrait;
+ const bool isPortraitResolution = resolution.height() > resolution.width();
+ if (isPortraitOrientation != isPortraitResolution)
+ resolution.transpose();
+
+ return resolution;
}
void QAVFCamera::syncHandleFrame(const QVideoFrame &frame)
@@ -444,6 +328,18 @@ void QAVFCamera::syncHandleFrame(const QVideoFrame &frame)
Q_EMIT newVideoFrame(frame);
}
+std::optional<int> QAVFCamera::ffmpegHWPixelFormat() const
+{
+ return m_hwPixelFormat == AV_PIX_FMT_NONE ? std::optional<int>{} : m_hwPixelFormat;
+}
+
+int QAVFCamera::cameraPixelFormatScore(QVideoFrameFormat::PixelFormat pixelFormat,
+ QVideoFrameFormat::ColorRange colorRange) const
+{
+ auto cvFormat = QAVFHelpers::toCVPixelFormat(pixelFormat, colorRange);
+ return static_cast<int>(isCVFormatSupported(cvFormat));
+}
+
QT_END_NAMESPACE
#include "moc_qavfcamera_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qavfcamera_p.h b/src/plugins/multimedia/ffmpeg/qavfcamera_p.h
index 1c0341496..0c88c520c 100644
--- a/src/plugins/multimedia/ffmpeg/qavfcamera_p.h
+++ b/src/plugins/multimedia/ffmpeg/qavfcamera_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAVFCAMERA_H
#define QAVFCAMERA_H
@@ -94,15 +58,18 @@ public:
void deviceOrientationChanged(int angle = -1);
- const void *ffmpegHWAccel() const override { return &hwAccel; }
+ std::optional<int> ffmpegHWPixelFormat() const override;
+
+ int cameraPixelFormatScore(QVideoFrameFormat::PixelFormat pixelFmt,
+ QVideoFrameFormat::ColorRange colorRange) const override;
private:
- void requestCameraPermissionIfNeeded();
- void cameraAuthorizationChanged(bool authorized);
+ bool checkCameraPermission();
void updateCameraFormat();
void updateVideoInput();
void attachVideoInputDevice();
- uint setPixelFormat(const QVideoFrameFormat::PixelFormat pixelFormat);
+ uint32_t setPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat, uint32_t inputCvPixFormat);
+ QSize adjustedResolution() const;
AVCaptureDevice *device() const;
@@ -113,7 +80,7 @@ private:
QAVFSampleBufferDelegate *m_sampleBufferDelegate = nullptr;
dispatch_queue_t m_delegateQueue;
QVideoOutputOrientationHandler m_orientationHandler;
- QFFmpeg::HWAccel hwAccel;
+ AVPixelFormat m_hwPixelFormat = AV_PIX_FMT_NONE;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate.mm b/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate.mm
new file mode 100644
index 000000000..54dc3e578
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate.mm
@@ -0,0 +1,222 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qavfsamplebufferdelegate_p.h"
+
+#define AVMediaType XAVMediaType
+
+#include "qffmpeghwaccel_p.h"
+#include "qavfhelpers_p.h"
+#include "qffmpegvideobuffer_p.h"
+
+#undef AVMediaType
+
+#include <optional>
+
+QT_USE_NAMESPACE
+
+static void releaseHwFrame(void * /*opaque*/, uint8_t *data)
+{
+ CVPixelBufferRelease(CVPixelBufferRef(data));
+}
+
+namespace {
+
+class CVImageVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ CVImageVideoBuffer(CVImageBufferRef imageBuffer)
+ : QAbstractVideoBuffer(QVideoFrame::NoHandle), m_buffer(imageBuffer)
+ {
+ CVPixelBufferRetain(imageBuffer);
+ }
+
+ ~CVImageVideoBuffer()
+ {
+ CVImageVideoBuffer::unmap();
+ CVPixelBufferRelease(m_buffer);
+ }
+
+ CVImageVideoBuffer::MapData map(QVideoFrame::MapMode mode) override
+ {
+ MapData mapData;
+
+ if (m_mode == QVideoFrame::NotMapped) {
+ CVPixelBufferLockBaseAddress(
+ m_buffer, mode == QVideoFrame::ReadOnly ? kCVPixelBufferLock_ReadOnly : 0);
+ m_mode = mode;
+ }
+
+ mapData.nPlanes = CVPixelBufferGetPlaneCount(m_buffer);
+ Q_ASSERT(mapData.nPlanes <= 3);
+
+ if (!mapData.nPlanes) {
+ // single plane
+ mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRow(m_buffer);
+ mapData.data[0] = static_cast<uchar *>(CVPixelBufferGetBaseAddress(m_buffer));
+ mapData.size[0] = CVPixelBufferGetDataSize(m_buffer);
+ mapData.nPlanes = mapData.data[0] ? 1 : 0;
+ return mapData;
+ }
+
+ // For a bi-planar or tri-planar format we have to set the parameters correctly:
+ for (int i = 0; i < mapData.nPlanes; ++i) {
+ mapData.bytesPerLine[i] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, i);
+ mapData.size[i] = mapData.bytesPerLine[i] * CVPixelBufferGetHeightOfPlane(m_buffer, i);
+ mapData.data[i] = static_cast<uchar *>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, i));
+ }
+
+ return mapData;
+ }
+
+ QVideoFrame::MapMode mapMode() const override { return m_mode; }
+
+ void unmap() override
+ {
+ if (m_mode != QVideoFrame::NotMapped) {
+ CVPixelBufferUnlockBaseAddress(
+ m_buffer, m_mode == QVideoFrame::ReadOnly ? kCVPixelBufferLock_ReadOnly : 0);
+ m_mode = QVideoFrame::NotMapped;
+ }
+ }
+
+private:
+ CVImageBufferRef m_buffer;
+ QVideoFrame::MapMode m_mode = QVideoFrame::NotMapped;
+};
+
+}
+
+// Make sure this is compatible with the layout used in ffmpeg's hwcontext_videotoolbox
+static QFFmpeg::AVFrameUPtr allocHWFrame(AVBufferRef *hwContext, const CVPixelBufferRef &pixbuf)
+{
+ AVHWFramesContext *ctx = (AVHWFramesContext *)hwContext->data;
+ auto frame = QFFmpeg::makeAVFrame();
+ frame->hw_frames_ctx = av_buffer_ref(hwContext);
+ frame->extended_data = frame->data;
+
+ frame->buf[0] = av_buffer_create((uint8_t *)pixbuf, 1, releaseHwFrame, NULL, 0);
+ frame->data[3] = (uint8_t *)pixbuf;
+ CVPixelBufferRetain(pixbuf);
+ frame->width = ctx->width;
+ frame->height = ctx->height;
+ frame->format = AV_PIX_FMT_VIDEOTOOLBOX;
+ if (frame->width != (int)CVPixelBufferGetWidth(pixbuf)
+ || frame->height != (int)CVPixelBufferGetHeight(pixbuf)) {
+
+ // This can happen while changing camera format
+ return nullptr;
+ }
+ return frame;
+}
+
+@implementation QAVFSampleBufferDelegate {
+@private
+ std::function<void(const QVideoFrame &)> frameHandler;
+ AVBufferRef *hwFramesContext;
+ std::unique_ptr<QFFmpeg::HWAccel> m_accel;
+ qint64 startTime;
+ std::optional<qint64> baseTime;
+ qreal frameRate;
+}
+
+static QVideoFrame createHwVideoFrame(QAVFSampleBufferDelegate &delegate,
+ CVImageBufferRef imageBuffer, QVideoFrameFormat format)
+{
+ Q_ASSERT(delegate.baseTime);
+
+ if (!delegate.m_accel)
+ return {};
+
+ auto avFrame = allocHWFrame(delegate.m_accel->hwFramesContextAsBuffer(), imageBuffer);
+ if (!avFrame)
+ return {};
+
+#ifdef USE_SW_FRAMES
+ {
+ auto swFrame = QFFmpeg::makeAVFrame();
+ /* retrieve data from GPU to CPU */
+ const int ret = av_hwframe_transfer_data(swFrame.get(), avFrame.get(), 0);
+ if (ret < 0) {
+ qWarning() << "Error transferring the data to system memory:" << ret;
+ } else {
+ avFrame = std::move(swFrame);
+ }
+ }
+#endif
+
+ avFrame->pts = delegate.startTime - *delegate.baseTime;
+
+ return QVideoFrame(new QFFmpegVideoBuffer(std::move(avFrame)), format);
+}
+
+- (instancetype)initWithFrameHandler:(std::function<void(const QVideoFrame &)>)handler
+{
+ if (!(self = [super init]))
+ return nil;
+
+ Q_ASSERT(handler);
+
+ frameHandler = std::move(handler);
+ hwFramesContext = nullptr;
+ startTime = 0;
+ frameRate = 0.;
+ return self;
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection
+{
+ Q_UNUSED(connection);
+ Q_UNUSED(captureOutput);
+
+ // NB: on iOS captureOutput/connection can be nil (when recording a video -
+ // avfmediaassetwriter).
+
+ CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+
+ if (!imageBuffer) {
+ qWarning() << "Cannot get image buffer from sample buffer";
+ return;
+ }
+
+ const CMTime time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
+ const qint64 frameTime = time.timescale ? time.value * 1000000 / time.timescale : 0;
+ if (!baseTime) {
+ baseTime = frameTime;
+ startTime = frameTime;
+ }
+
+ QVideoFrameFormat format = QAVFHelpers::videoFormatForImageBuffer(imageBuffer);
+ if (!format.isValid()) {
+ qWarning() << "Cannot get get video format for image buffer"
+ << CVPixelBufferGetWidth(imageBuffer) << 'x'
+ << CVPixelBufferGetHeight(imageBuffer);
+ return;
+ }
+
+ format.setStreamFrameRate(frameRate);
+
+ auto frame = createHwVideoFrame(*self, imageBuffer, format);
+ if (!frame.isValid())
+ frame = QVideoFrame(new CVImageVideoBuffer(imageBuffer), format);
+
+ frame.setStartTime(startTime - *baseTime);
+ frame.setEndTime(frameTime - *baseTime);
+ startTime = frameTime;
+
+ frameHandler(frame);
+}
+
+- (void)setHWAccel:(std::unique_ptr<QFFmpeg::HWAccel> &&)accel
+{
+ m_accel = std::move(accel);
+}
+
+- (void)setVideoFormatFrameRate:(qreal)rate
+{
+ frameRate = rate;
+}
+
+@end
diff --git a/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate_p.h b/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate_p.h
new file mode 100644
index 000000000..5a7e16c71
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qavfsamplebufferdelegate_p.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAVFSAMPLEBUFFERDELEGATE_P_H
+#define QAVFSAMPLEBUFFERDELEGATE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#import <AVFoundation/AVFoundation.h>
+#import <CoreVideo/CoreVideo.h>
+
+#include <qtconfigmacros.h>
+#include <qtypes.h>
+
+#include <memory>
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+
+class QAVSampleBufferDelegateFrameHandler;
+class QVideoFrame;
+namespace QFFmpeg {
+class HWAccel;
+}
+
+QT_END_NAMESPACE
+
+@interface QAVFSampleBufferDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
+
+- (instancetype)initWithFrameHandler:(std::function<void(const QVideoFrame &)>)handler;
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput
+ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+ fromConnection:(AVCaptureConnection *)connection;
+
+- (void)setHWAccel:(std::unique_ptr<QT_PREPEND_NAMESPACE(QFFmpeg::HWAccel)> &&)accel;
+
+- (void)setVideoFormatFrameRate:(qreal)frameRate;
+
+@end
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qavfscreencapture.mm b/src/plugins/multimedia/ffmpeg/qavfscreencapture.mm
new file mode 100644
index 000000000..715dea09c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qavfscreencapture.mm
@@ -0,0 +1,201 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qavfscreencapture_p.h"
+#include "qavfsamplebufferdelegate_p.h"
+#include "qffmpegsurfacecapturegrabber_p.h"
+
+#include <qscreen.h>
+
+#define AVMediaType XAVMediaType
+#include "qffmpeghwaccel_p.h"
+
+extern "C" {
+#include <libavutil/hwcontext_videotoolbox.h>
+#include <libavutil/hwcontext.h>
+}
+#undef AVMediaType
+
+#include <AppKit/NSScreen.h>
+
+#include <dispatch/dispatch.h>
+
+namespace {
+
+const auto DefaultCVPixelFormat = kCVPixelFormatType_32BGRA;
+
+CGDirectDisplayID findDisplayByName(const QString &name)
+{
+ for (NSScreen *screen in NSScreen.screens) {
+ if (name == QString::fromNSString(screen.localizedName))
+ return [screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
+ }
+ return kCGNullDirectDisplay;
+}
+}
+
+QT_BEGIN_NAMESPACE
+
+QAVFScreenCapture::QAVFScreenCapture() : QPlatformSurfaceCapture(ScreenSource{})
+{
+ CGRequestScreenCaptureAccess();
+}
+
+QAVFScreenCapture::~QAVFScreenCapture()
+{
+ resetCapture();
+}
+
+bool QAVFScreenCapture::setActiveInternal(bool active)
+{
+ if (active) {
+ if (!CGPreflightScreenCaptureAccess()) {
+ updateError(CaptureFailed, QLatin1String("Permissions denied"));
+ return false;
+ }
+
+ auto screen = source<ScreenSource>();
+
+ if (!checkScreenWithError(screen))
+ return false;
+
+ return initScreenCapture(screen);
+ } else {
+ resetCapture();
+ }
+
+ return true;
+}
+
+void QAVFScreenCapture::onNewFrame(const QVideoFrame &frame)
+{
+ // Since writing of the format is supposed to be only from one thread,
+ // the read-only comparison without a mutex is thread-safe
+ if (!m_format || m_format != frame.surfaceFormat()) {
+ QMutexLocker<QMutex> locker(&m_formatMutex);
+
+ m_format = frame.surfaceFormat();
+
+ locker.unlock();
+
+ m_waitForFormat.notify_one();
+ }
+
+ emit newVideoFrame(frame);
+}
+
+QVideoFrameFormat QAVFScreenCapture::frameFormat() const
+{
+ if (!m_grabber)
+ return {};
+
+ QMutexLocker<QMutex> locker(&m_formatMutex);
+ while (!m_format)
+ m_waitForFormat.wait(&m_formatMutex);
+ return *m_format;
+}
+
+std::optional<int> QAVFScreenCapture::ffmpegHWPixelFormat() const
+{
+ return AV_PIX_FMT_VIDEOTOOLBOX;
+}
+
+class QAVFScreenCapture::Grabber
+{
+public:
+ Grabber(QAVFScreenCapture &capture, QScreen *screen, CGDirectDisplayID screenID,
+ std::unique_ptr<QFFmpeg::HWAccel> hwAccel)
+ {
+ m_captureSession = [[AVCaptureSession alloc] init];
+
+ m_sampleBufferDelegate = [[QAVFSampleBufferDelegate alloc]
+ initWithFrameHandler:[&capture](const QVideoFrame &frame) {
+ capture.onNewFrame(frame);
+ }];
+
+ m_videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
+
+ NSDictionary *videoSettings = [NSDictionary
+ dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:DefaultCVPixelFormat],
+ kCVPixelBufferPixelFormatTypeKey, nil];
+
+ [m_videoDataOutput setVideoSettings:videoSettings];
+ [m_videoDataOutput setAlwaysDiscardsLateVideoFrames:true];
+
+ // Configure video output
+ m_dispatchQueue = dispatch_queue_create("vf_queue", nullptr);
+ [m_videoDataOutput setSampleBufferDelegate:m_sampleBufferDelegate queue:m_dispatchQueue];
+
+ [m_captureSession addOutput:m_videoDataOutput];
+
+ [m_sampleBufferDelegate setHWAccel:std::move(hwAccel)];
+
+ const auto frameRate = std::min(screen->refreshRate(), MaxScreenCaptureFrameRate);
+ [m_sampleBufferDelegate setVideoFormatFrameRate:frameRate];
+
+ m_screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:screenID];
+ [m_screenInput setMinFrameDuration:CMTimeMake(1, static_cast<int32_t>(frameRate))];
+ [m_captureSession addInput:m_screenInput];
+
+ [m_captureSession startRunning];
+ }
+
+ ~Grabber()
+ {
+ if (m_captureSession)
+ [m_captureSession stopRunning];
+
+ if (m_dispatchQueue)
+ dispatch_release(m_dispatchQueue);
+
+ [m_sampleBufferDelegate release];
+ [m_screenInput release];
+ [m_videoDataOutput release];
+ [m_captureSession release];
+ }
+
+private:
+ AVCaptureSession *m_captureSession = nullptr;
+ AVCaptureScreenInput *m_screenInput = nullptr;
+ AVCaptureVideoDataOutput *m_videoDataOutput = nullptr;
+ QAVFSampleBufferDelegate *m_sampleBufferDelegate = nullptr;
+ dispatch_queue_t m_dispatchQueue = nullptr;
+};
+
+bool QAVFScreenCapture::initScreenCapture(QScreen *screen)
+{
+ const auto screenID = findDisplayByName(screen->name());
+
+ if (screenID == kCGNullDirectDisplay) {
+ updateError(InternalError, QLatin1String("Screen exists but couldn't been found by name"));
+ return false;
+ }
+
+ auto hwAccel = QFFmpeg::HWAccel::create(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
+
+ if (!hwAccel) {
+ updateError(CaptureFailed, QLatin1String("Couldn't create videotoolbox hw acceleration"));
+ return false;
+ }
+
+ hwAccel->createFramesContext(av_map_videotoolbox_format_to_pixfmt(DefaultCVPixelFormat),
+ screen->size() * screen->devicePixelRatio());
+
+ if (!hwAccel->hwFramesContextAsBuffer()) {
+ updateError(CaptureFailed, QLatin1String("Couldn't create hw frames context"));
+ return false;
+ }
+
+ m_grabber = std::make_unique<Grabber>(*this, screen, screenID, std::move(hwAccel));
+ return true;
+}
+
+void QAVFScreenCapture::resetCapture()
+{
+ m_grabber.reset();
+ m_format = {};
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qavfscreencapture_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qavfscreencapture_p.h b/src/plugins/multimedia/ffmpeg/qavfscreencapture_p.h
new file mode 100644
index 000000000..b95aabcf3
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qavfscreencapture_p.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAVFSCREENCAPTURE_H
+#define QAVFSCREENCAPTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qplatformsurfacecapture_p.h"
+#include <qmutex.h>
+#include <qwaitcondition.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegVideoSink;
+
+class QAVFScreenCapture : public QPlatformSurfaceCapture
+{
+ Q_OBJECT
+
+ class Grabber;
+
+public:
+ explicit QAVFScreenCapture();
+ ~QAVFScreenCapture() override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+ std::optional<int> ffmpegHWPixelFormat() const override;
+
+protected:
+ bool setActiveInternal(bool active) override;
+
+private:
+ void onNewFrame(const QVideoFrame &frame);
+
+ bool initScreenCapture(QScreen *screen);
+
+ void resetCapture();
+
+private:
+ std::optional<QVideoFrameFormat> m_format;
+ mutable QMutex m_formatMutex;
+ mutable QWaitCondition m_waitForFormat;
+
+ std::unique_ptr<Grabber> m_grabber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QAVFSCREENCAPTURE_H
diff --git a/src/plugins/multimedia/ffmpeg/qcgcapturablewindows.mm b/src/plugins/multimedia/ffmpeg/qcgcapturablewindows.mm
new file mode 100644
index 000000000..eb2035208
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qcgcapturablewindows.mm
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qcgcapturablewindows_p.h"
+#include "private/qcapturablewindow_p.h"
+#include "QtCore/private/qcore_mac_p.h"
+
+#include <AppKit/NSWindow.h>
+
+QT_BEGIN_NAMESPACE
+
+QList<QCapturableWindow> QCGCapturableWindows::windows() const
+{
+ QList<QCapturableWindow> result;
+ QCFType<CFArrayRef> windowList(
+ CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID));
+
+ // Iterate through the window dictionaries
+ CFIndex count = CFArrayGetCount(windowList);
+ for (CFIndex i = 0; i < count; ++i) {
+ auto windowInfo = (CFDictionaryRef)CFArrayGetValueAtIndex(windowList, i);
+ auto windowNumber = (CFNumberRef)CFDictionaryGetValue(windowInfo, kCGWindowNumber);
+ auto windowName = (CFStringRef)CFDictionaryGetValue(windowInfo, kCGWindowName);
+
+ CGWindowID windowId = 0;
+ static_assert(sizeof(windowId) == 4,
+ "CGWindowID size is not compatible with kCFNumberSInt32Type");
+ CFNumberGetValue(windowNumber, kCFNumberSInt32Type, &windowId);
+
+ auto windowData = std::make_unique<QCapturableWindowPrivate>();
+ windowData->id = static_cast<QCapturableWindowPrivate::Id>(windowId);
+ if (windowName)
+ windowData->description = QString::fromCFString(windowName);
+
+ result.push_back(windowData.release()->create());
+ }
+
+ return result;
+}
+
+bool QCGCapturableWindows::isWindowValid(const QCapturableWindowPrivate &window) const
+{
+ QCFType<CFArrayRef> windowList(
+ CGWindowListCreate(kCGWindowListOptionIncludingWindow, window.id));
+ return CFArrayGetCount(windowList) > 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qcgcapturablewindows_p.h b/src/plugins/multimedia/ffmpeg/qcgcapturablewindows_p.h
new file mode 100644
index 000000000..6f779ae0d
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qcgcapturablewindows_p.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCGCAPTURABLEWINDOWS_P_H
+#define QCGCAPTURABLEWINDOWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qplatformcapturablewindows_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QCGCapturableWindows : public QPlatformCapturableWindows
+{
+public:
+ QList<QCapturableWindow> windows() const override;
+
+ bool isWindowValid(const QCapturableWindowPrivate &window) const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCGCAPTURABLEWINDOWS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qcgwindowcapture.mm b/src/plugins/multimedia/ffmpeg/qcgwindowcapture.mm
new file mode 100644
index 000000000..c10868e76
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qcgwindowcapture.mm
@@ -0,0 +1,200 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qcgwindowcapture_p.h"
+#include "private/qcapturablewindow_p.h"
+#include "qffmpegsurfacecapturegrabber_p.h"
+#include "private/qabstractvideobuffer_p.h"
+
+#include "qscreen.h"
+#include "qguiapplication.h"
+#include <qmutex.h>
+#include <qwaitcondition.h>
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <IOKit/graphics/IOGraphicsLib.h>
+#include <AppKit/NSScreen.h>
+#include <AppKit/NSApplication.h>
+#include <AppKit/NSWindow.h>
+
+namespace {
+
+std::optional<qreal> frameRateForWindow(CGWindowID /*wid*/)
+{
+ // TODO: detect the frame rate
+ // if (window && window.screen) {
+ // CGDirectDisplayID displayID = [window.screen.deviceDescription[@"NSScreenNumber"]
+ // unsignedIntValue]; const auto displayRefreshRate =
+ // CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(displayID)); if (displayRefreshRate
+ // > 0 && displayRefreshRate < frameRate) frameRate = displayRefreshRate;
+ // }
+
+ return {};
+}
+
+}
+
+QT_BEGIN_NAMESPACE
+
+class QCGImageVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ QCGImageVideoBuffer(CGImageRef image) : QAbstractVideoBuffer(QVideoFrame::NoHandle)
+ {
+ auto provider = CGImageGetDataProvider(image);
+ m_data = CGDataProviderCopyData(provider);
+ m_bytesPerLine = CGImageGetBytesPerRow(image);
+ }
+
+ ~QCGImageVideoBuffer() override { CFRelease(m_data); }
+
+ QVideoFrame::MapMode mapMode() const override { return m_mapMode; }
+
+ MapData map(QVideoFrame::MapMode mode) override
+ {
+ MapData mapData;
+ if (m_mapMode == QVideoFrame::NotMapped) {
+ m_mapMode = mode;
+
+ mapData.nPlanes = 1;
+ mapData.bytesPerLine[0] = static_cast<int>(m_bytesPerLine);
+ mapData.data[0] = (uchar *)CFDataGetBytePtr(m_data);
+ mapData.size[0] = static_cast<int>(CFDataGetLength(m_data));
+ }
+
+ return mapData;
+ }
+
+ void unmap() override { m_mapMode = QVideoFrame::NotMapped; }
+
+private:
+ QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped;
+ CFDataRef m_data;
+ size_t m_bytesPerLine = 0;
+};
+
+class QCGWindowCapture::Grabber : public QFFmpegSurfaceCaptureGrabber
+{
+public:
+ Grabber(QCGWindowCapture &capture, CGWindowID wid) : m_capture(capture), m_wid(wid)
+ {
+ addFrameCallback(*this, &Grabber::onNewFrame);
+ connect(this, &Grabber::errorUpdated, &capture, &QCGWindowCapture::updateError);
+
+ if (auto screen = QGuiApplication::primaryScreen())
+ setFrameRate(screen->refreshRate());
+
+ start();
+ }
+
+ ~Grabber() override { stop(); }
+
+ QVideoFrameFormat frameFormat() const
+ {
+ QMutexLocker<QMutex> locker(&m_formatMutex);
+ while (!m_format)
+ m_waitForFormat.wait(&m_formatMutex);
+ return *m_format;
+ }
+
+protected:
+ QVideoFrame grabFrame() override
+ {
+ if (auto rate = frameRateForWindow(m_wid))
+ setFrameRate(*rate);
+
+ auto imageRef = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow,
+ m_wid, kCGWindowImageBoundsIgnoreFraming);
+ if (!imageRef) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot create image by window"));
+ return {};
+ }
+
+ auto imageDeleter = qScopeGuard([imageRef]() { CGImageRelease(imageRef); });
+
+ if (CGImageGetBitsPerPixel(imageRef) != 32
+ || CGImageGetPixelFormatInfo(imageRef) != kCGImagePixelFormatPacked
+ || CGImageGetByteOrderInfo(imageRef) != kCGImageByteOrder32Little) {
+ qWarning() << "Unexpected image format. PixelFormatInfo:"
+ << CGImageGetPixelFormatInfo(imageRef)
+ << "BitsPerPixel:" << CGImageGetBitsPerPixel(imageRef) << "AlphaInfo"
+ << CGImageGetAlphaInfo(imageRef)
+ << "ByteOrderInfo:" << CGImageGetByteOrderInfo(imageRef);
+
+ updateError(QPlatformSurfaceCapture::CapturingNotSupported,
+ QLatin1String("Not supported pixel format"));
+ return {};
+ }
+
+ QVideoFrameFormat format(QSize(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)),
+ QVideoFrameFormat::Format_BGRA8888);
+ format.setStreamFrameRate(frameRate());
+
+ return QVideoFrame(new QCGImageVideoBuffer(imageRef), format);
+ }
+
+ void onNewFrame(QVideoFrame frame)
+ {
+ // Since writing of the format is supposed to be only from one thread,
+ // the read-only comparison without a mutex is thread-safe
+ if (!m_format || m_format != frame.surfaceFormat()) {
+ QMutexLocker<QMutex> locker(&m_formatMutex);
+
+ m_format = frame.surfaceFormat();
+
+ locker.unlock();
+
+ m_waitForFormat.notify_one();
+ }
+
+ emit m_capture.newVideoFrame(frame);
+ }
+
+private:
+ QCGWindowCapture &m_capture;
+ std::optional<QVideoFrameFormat> m_format;
+ mutable QMutex m_formatMutex;
+ mutable QWaitCondition m_waitForFormat;
+ CGWindowID m_wid;
+};
+
+QCGWindowCapture::QCGWindowCapture() : QPlatformSurfaceCapture(WindowSource{})
+{
+ CGRequestScreenCaptureAccess();
+}
+
+QCGWindowCapture::~QCGWindowCapture() = default;
+
+bool QCGWindowCapture::setActiveInternal(bool active)
+{
+ if (active) {
+ if (!CGPreflightScreenCaptureAccess()) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Permissions denied"));
+ return false;
+ }
+
+ auto window = source<WindowSource>();
+
+ auto handle = QCapturableWindowPrivate::handle(window);
+ if (!handle || !handle->id)
+ updateError(QPlatformSurfaceCapture::NotFound, QLatin1String("Invalid window"));
+ else
+ m_grabber = std::make_unique<Grabber>(*this, handle->id);
+
+ } else {
+ m_grabber.reset();
+ }
+
+ return active == static_cast<bool>(m_grabber);
+}
+
+QVideoFrameFormat QCGWindowCapture::frameFormat() const
+{
+ return m_grabber ? m_grabber->frameFormat() : QVideoFrameFormat();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qcgwindowcapture_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qcgwindowcapture_p.h b/src/plugins/multimedia/ffmpeg/qcgwindowcapture_p.h
new file mode 100644
index 000000000..796c01ab3
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qcgwindowcapture_p.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCGWINDOWCAPTURE_H
+#define QCGWINDOWCAPTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qplatformsurfacecapture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QCGWindowCapture : public QPlatformSurfaceCapture
+{
+ Q_OBJECT
+
+ class Grabber;
+
+public:
+ explicit QCGWindowCapture();
+ ~QCGWindowCapture() override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+protected:
+ bool setActiveInternal(bool active) override;
+
+private:
+ std::unique_ptr<Grabber> m_grabber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCGWINDOWCAPTURE_H
diff --git a/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp
new file mode 100644
index 000000000..f73202aba
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture.cpp
@@ -0,0 +1,178 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qeglfsscreencapture_p.h"
+
+#include "qffmpegsurfacecapturegrabber_p.h"
+#include "qguiapplication.h"
+#include "qopenglvideobuffer_p.h"
+#include "private/qimagevideobuffer_p.h"
+
+#include <QtOpenGL/private/qopenglcompositor_p.h>
+#include <QtOpenGL/private/qopenglframebufferobject_p.h>
+
+#include <QtQuick/qquickwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEglfsScreenCapture::Grabber : public QFFmpegSurfaceCaptureGrabber
+{
+public:
+ Grabber(QEglfsScreenCapture &screenCapture, QScreen *screen)
+ : QFFmpegSurfaceCaptureGrabber(QFFmpegSurfaceCaptureGrabber::UseCurrentThread)
+ {
+ addFrameCallback(screenCapture, &QEglfsScreenCapture::newVideoFrame);
+ connect(this, &Grabber::errorUpdated, &screenCapture, &QEglfsScreenCapture::updateError);
+ // Limit frame rate to 30 fps for performance reasons,
+ // to be reviewed at the next optimization round
+ setFrameRate(std::min(screen->refreshRate(), 30.0));
+ }
+
+ ~Grabber() override { stop(); }
+
+ QVideoFrameFormat format() { return m_format; }
+
+protected:
+ QVideoFrame grabFrame() override
+ {
+ auto nativeSize = QOpenGLCompositor::instance()->nativeTargetGeometry().size();
+ auto fbo = std::make_unique<QOpenGLFramebufferObject>(nativeSize);
+
+ if (!QOpenGLCompositor::instance()->grabToFrameBufferObject(
+ fbo.get(), QOpenGLCompositor::NotFlipped)) {
+ updateError(Error::InternalError, QLatin1String("Couldn't grab to framebuffer object"));
+ return {};
+ }
+
+ if (!fbo->isValid()) {
+ updateError(Error::InternalError, QLatin1String("Framebuffer object invalid"));
+ return {};
+ }
+
+ auto videoBuffer = std::make_unique<QOpenGLVideoBuffer>(std::move(fbo));
+
+ if (!m_format.isValid()) {
+ auto image = videoBuffer->ensureImageBuffer().underlyingImage();
+ m_format = { image.size(), QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) };
+ m_format.setStreamFrameRate(frameRate());
+ }
+
+ return QVideoFrame(videoBuffer.release(), m_format);
+ }
+
+ QVideoFrameFormat m_format;
+};
+
+class QEglfsScreenCapture::QuickGrabber : public Grabber
+{
+public:
+ QuickGrabber(QEglfsScreenCapture &screenCapture, QScreen *screen, QQuickWindow *quickWindow)
+ : Grabber(screenCapture, screen), m_quickWindow(quickWindow)
+ {
+ Q_ASSERT(m_quickWindow);
+ }
+
+protected:
+ QVideoFrame grabFrame() override
+ {
+ if (!m_quickWindow) {
+ updateError(Error::InternalError, QLatin1String("Window deleted"));
+ return {};
+ }
+
+ QImage image = m_quickWindow->grabWindow();
+
+ if (image.isNull()) {
+ updateError(Error::InternalError, QLatin1String("Image invalid"));
+ return {};
+ }
+
+ if (!m_format.isValid()) {
+ m_format = { image.size(),
+ QVideoFrameFormat::pixelFormatFromImageFormat(image.format()) };
+ m_format.setStreamFrameRate(frameRate());
+ }
+
+ return QVideoFrame(new QImageVideoBuffer(std::move(image)), m_format);
+ }
+
+private:
+ QPointer<QQuickWindow> m_quickWindow;
+};
+
+QEglfsScreenCapture::QEglfsScreenCapture() : QPlatformSurfaceCapture(ScreenSource{}) { }
+
+QEglfsScreenCapture::~QEglfsScreenCapture() = default;
+
+QVideoFrameFormat QEglfsScreenCapture::frameFormat() const
+{
+ return m_grabber ? m_grabber->format() : QVideoFrameFormat();
+}
+
+bool QEglfsScreenCapture::setActiveInternal(bool active)
+{
+ if (static_cast<bool>(m_grabber) == active)
+ return true;
+
+ if (m_grabber)
+ m_grabber.reset();
+
+ if (!active)
+ return true;
+
+ m_grabber = createGrabber();
+
+ if (!m_grabber) {
+ // TODO: This could mean that the UI is not started yet, so we should wait and try again,
+ // and then give error if still not started. Might not be possible here.
+ return false;
+ }
+
+ m_grabber->start();
+ return true;
+}
+
+bool QEglfsScreenCapture::isSupported()
+{
+ return QGuiApplication::platformName() == QLatin1String("eglfs");
+}
+
+std::unique_ptr<QEglfsScreenCapture::Grabber> QEglfsScreenCapture::createGrabber()
+{
+ auto screen = source<ScreenSource>();
+ if (!checkScreenWithError(screen))
+ return nullptr;
+
+ QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
+
+ if (compositor->context()) {
+ // Create OpenGL grabber
+ if (!compositor->targetWindow()) {
+ updateError(Error::CaptureFailed,
+ QLatin1String("Target window is not set for OpenGL compositor"));
+ return nullptr;
+ }
+
+ return std::make_unique<Grabber>(*this, screen);
+ }
+
+ // Check for QQuickWindow
+ auto windows = QGuiApplication::topLevelWindows();
+ auto it = std::find_if(windows.begin(), windows.end(), [screen](QWindow *window) {
+ auto quickWindow = qobject_cast<QQuickWindow *>(window);
+ if (!quickWindow)
+ return false;
+
+ return quickWindow->screen() == screen;
+ });
+
+ if (it != windows.end()) {
+ // Create grabber that calls QQuickWindow::grabWindow
+ return std::make_unique<QuickGrabber>(*this, screen, qobject_cast<QQuickWindow *>(*it));
+ }
+
+ updateError(Error::CaptureFailed, QLatin1String("No existing OpenGL context or QQuickWindow"));
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h
new file mode 100644
index 000000000..840cdbeb0
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qeglfsscreencapture_p.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QEGLFSSCREENCAPTURE_H
+#define QEGLFSSCREENCAPTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformsurfacecapture_p.h>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QEglfsScreenCapture : public QPlatformSurfaceCapture
+{
+public:
+ QEglfsScreenCapture();
+
+ ~QEglfsScreenCapture() override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+ static bool isSupported();
+
+private:
+ bool setActiveInternal(bool active) override;
+
+private:
+ class Grabber;
+ class QuickGrabber;
+
+ std::unique_ptr<Grabber> createGrabber();
+
+ std::unique_ptr<Grabber> m_grabber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSSCREENCAPTURE_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeg.cpp b/src/plugins/multimedia/ffmpeg/qffmpeg.cpp
new file mode 100644
index 000000000..e998495d8
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeg.cpp
@@ -0,0 +1,612 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpeg_p.h"
+
+#include <qdebug.h>
+#include <qloggingcategory.h>
+#include <qffmpeghwaccel_p.h> // TODO: probably decompose HWAccel and get rid of the header in the base utils
+
+#include <algorithm>
+#include <vector>
+#include <array>
+#include <optional>
+#include <unordered_set>
+
+extern "C" {
+#include <libavutil/pixdesc.h>
+#include <libavutil/samplefmt.h>
+
+#ifdef Q_OS_DARWIN
+#include <libavutil/hwcontext_videotoolbox.h>
+#endif
+}
+
+#ifdef Q_OS_ANDROID
+#include <QtCore/qjniobject.h>
+#include <QtCore/qjniarray.h>
+#include <QtCore/qjnitypes.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_ANDROID
+Q_DECLARE_JNI_CLASS(QtVideoDeviceManager,
+ "org/qtproject/qt/android/multimedia/QtVideoDeviceManager");
+Q_DECLARE_JNI_CLASS(String, "java/lang/String");
+#endif
+
+static Q_LOGGING_CATEGORY(qLcFFmpegUtils, "qt.multimedia.ffmpeg.utils");
+
+namespace QFFmpeg {
+
+namespace {
+
+enum CodecStorageType {
+ ENCODERS,
+ DECODERS,
+
+ // TODO: maybe split sw/hw codecs
+
+ CODEC_STORAGE_TYPE_COUNT
+};
+
+using CodecsStorage = std::vector<const AVCodec *>;
+
+struct CodecsComparator
+{
+ bool operator()(const AVCodec *a, const AVCodec *b) const
+ {
+ return a->id < b->id
+ || (a->id == b->id && isAVCodecExperimental(a) < isAVCodecExperimental(b));
+ }
+
+ bool operator()(const AVCodec *a, AVCodecID id) const { return a->id < id; }
+};
+
+template<typename FlagNames>
+QString flagsToString(int flags, const FlagNames &flagNames)
+{
+ QString result;
+ int leftover = flags;
+ for (const auto &flagAndName : flagNames)
+ if ((flags & flagAndName.first) != 0) {
+ leftover &= ~flagAndName.first;
+ if (!result.isEmpty())
+ result += ", ";
+ result += flagAndName.second;
+ }
+
+ if (leftover) {
+ if (!result.isEmpty())
+ result += ", ";
+ result += QString::number(leftover, 16);
+ }
+ return result;
+}
+
+void dumpCodecInfo(const AVCodec *codec)
+{
+ using FlagNames = std::initializer_list<std::pair<int, const char *>>;
+ const auto mediaType = codec->type == AVMEDIA_TYPE_VIDEO ? "video"
+ : codec->type == AVMEDIA_TYPE_AUDIO ? "audio"
+ : codec->type == AVMEDIA_TYPE_SUBTITLE ? "subtitle"
+ : "other_type";
+
+ const auto type = av_codec_is_encoder(codec)
+ ? av_codec_is_decoder(codec) ? "encoder/decoder:" : "encoder:"
+ : "decoder:";
+
+ static const FlagNames capabilitiesNames = {
+ { AV_CODEC_CAP_DRAW_HORIZ_BAND, "DRAW_HORIZ_BAND" },
+ { AV_CODEC_CAP_DR1, "DRAW_HORIZ_DR1" },
+ { AV_CODEC_CAP_DELAY, "DELAY" },
+ { AV_CODEC_CAP_SMALL_LAST_FRAME, "SMALL_LAST_FRAME" },
+ { AV_CODEC_CAP_SUBFRAMES, "SUBFRAMES" },
+ { AV_CODEC_CAP_EXPERIMENTAL, "EXPERIMENTAL" },
+ { AV_CODEC_CAP_CHANNEL_CONF, "CHANNEL_CONF" },
+ { AV_CODEC_CAP_FRAME_THREADS, "FRAME_THREADS" },
+ { AV_CODEC_CAP_SLICE_THREADS, "SLICE_THREADS" },
+ { AV_CODEC_CAP_PARAM_CHANGE, "PARAM_CHANGE" },
+#ifdef AV_CODEC_CAP_OTHER_THREADS
+ { AV_CODEC_CAP_OTHER_THREADS, "OTHER_THREADS" },
+#endif
+ { AV_CODEC_CAP_VARIABLE_FRAME_SIZE, "VARIABLE_FRAME_SIZE" },
+ { AV_CODEC_CAP_AVOID_PROBING, "AVOID_PROBING" },
+ { AV_CODEC_CAP_HARDWARE, "HARDWARE" },
+ { AV_CODEC_CAP_HYBRID, "HYBRID" },
+ { AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, "ENCODER_REORDERED_OPAQUE" },
+#ifdef AV_CODEC_CAP_ENCODER_FLUSH
+ { AV_CODEC_CAP_ENCODER_FLUSH, "ENCODER_FLUSH" },
+#endif
+ };
+
+ qCDebug(qLcFFmpegUtils) << mediaType << type << codec->name << "id:" << codec->id
+ << "capabilities:"
+ << flagsToString(codec->capabilities, capabilitiesNames);
+
+ if (codec->pix_fmts) {
+ static const FlagNames flagNames = {
+ { AV_PIX_FMT_FLAG_BE, "BE" },
+ { AV_PIX_FMT_FLAG_PAL, "PAL" },
+ { AV_PIX_FMT_FLAG_BITSTREAM, "BITSTREAM" },
+ { AV_PIX_FMT_FLAG_HWACCEL, "HWACCEL" },
+ { AV_PIX_FMT_FLAG_PLANAR, "PLANAR" },
+ { AV_PIX_FMT_FLAG_RGB, "RGB" },
+ { AV_PIX_FMT_FLAG_ALPHA, "ALPHA" },
+ { AV_PIX_FMT_FLAG_BAYER, "BAYER" },
+ { AV_PIX_FMT_FLAG_FLOAT, "FLOAT" },
+ };
+
+ qCDebug(qLcFFmpegUtils) << " pix_fmts:";
+ for (auto f = codec->pix_fmts; *f != AV_PIX_FMT_NONE; ++f) {
+ auto desc = av_pix_fmt_desc_get(*f);
+ qCDebug(qLcFFmpegUtils)
+ << " id:" << *f << desc->name << "depth:" << desc->comp[0].depth
+ << "flags:" << flagsToString(desc->flags, flagNames);
+ }
+ } else if (codec->type == AVMEDIA_TYPE_VIDEO) {
+ qCDebug(qLcFFmpegUtils) << " pix_fmts: null";
+ }
+
+ if (codec->sample_fmts) {
+ qCDebug(qLcFFmpegUtils) << " sample_fmts:";
+ for (auto f = codec->sample_fmts; *f != AV_SAMPLE_FMT_NONE; ++f) {
+ const auto name = av_get_sample_fmt_name(*f);
+ qCDebug(qLcFFmpegUtils) << " id:" << *f << (name ? name : "unknown")
+ << "bytes_per_sample:" << av_get_bytes_per_sample(*f)
+ << "is_planar:" << av_sample_fmt_is_planar(*f);
+ }
+ } else if (codec->type == AVMEDIA_TYPE_AUDIO) {
+ qCDebug(qLcFFmpegUtils) << " sample_fmts: null";
+ }
+
+ if (avcodec_get_hw_config(codec, 0)) {
+ static const FlagNames hwConfigMethodNames = {
+ { AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, "HW_DEVICE_CTX" },
+ { AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, "HW_FRAMES_CTX" },
+ { AV_CODEC_HW_CONFIG_METHOD_INTERNAL, "INTERNAL" },
+ { AV_CODEC_HW_CONFIG_METHOD_AD_HOC, "AD_HOC" }
+ };
+
+ qCDebug(qLcFFmpegUtils) << " hw config:";
+ for (int index = 0; auto config = avcodec_get_hw_config(codec, index); ++index) {
+ const auto pixFmtForDevice = pixelFormatForHwDevice(config->device_type);
+ auto pixFmtDesc = av_pix_fmt_desc_get(config->pix_fmt);
+ auto pixFmtForDeviceDesc = av_pix_fmt_desc_get(pixFmtForDevice);
+ qCDebug(qLcFFmpegUtils)
+ << " device_type:" << config->device_type << "pix_fmt:" << config->pix_fmt
+ << (pixFmtDesc ? pixFmtDesc->name : "unknown")
+ << "pixelFormatForHwDevice:" << pixelFormatForHwDevice(config->device_type)
+ << (pixFmtForDeviceDesc ? pixFmtForDeviceDesc->name : "unknown")
+ << "hw_config_methods:" << flagsToString(config->methods, hwConfigMethodNames);
+ }
+ }
+}
+
+bool isCodecValid(const AVCodec *codec, const std::vector<AVHWDeviceType> &availableHwDeviceTypes,
+ const std::optional<std::unordered_set<AVCodecID>> &codecAvailableOnDevice)
+{
+ if (codec->type != AVMEDIA_TYPE_VIDEO)
+ return true;
+
+ const auto pixFmts = codec->pix_fmts;
+
+ if (!pixFmts) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
+ // Disable V4L2 M2M codecs for encoding for now,
+ // TODO: Investigate on how to get them working
+ if (std::strstr(codec->name, "_v4l2m2m") && av_codec_is_encoder(codec))
+ return false;
+
+ // MediaCodec in Android is used for hardware-accelerated media processing. That is why
+ // before marking it as valid, we need to make sure if it is available on current device.
+ if (std::strstr(codec->name, "_mediacodec")
+ && (codec->capabilities & AV_CODEC_CAP_HARDWARE)
+ && codecAvailableOnDevice && codecAvailableOnDevice->count(codec->id) == 0)
+ return false;
+#endif
+
+ return true; // To be investigated. This happens for RAW_VIDEO, that is supposed to be OK,
+ // and with v4l2m2m codecs, that is suspicious.
+ }
+
+ if (findAVFormat(pixFmts, &isHwPixelFormat) == AV_PIX_FMT_NONE)
+ return true;
+
+ if ((codec->capabilities & AV_CODEC_CAP_HARDWARE) == 0)
+ return true;
+
+ auto checkDeviceType = [pixFmts](AVHWDeviceType type) {
+ return hasAVFormat(pixFmts, pixelFormatForHwDevice(type));
+ };
+
+ if (codecAvailableOnDevice && codecAvailableOnDevice->count(codec->id) == 0)
+ return false;
+
+ return std::any_of(availableHwDeviceTypes.begin(), availableHwDeviceTypes.end(),
+ checkDeviceType);
+}
+
+std::optional<std::unordered_set<AVCodecID>> availableHWCodecs(const CodecStorageType type)
+{
+#ifdef Q_OS_ANDROID
+ using namespace Qt::StringLiterals;
+ std::unordered_set<AVCodecID> availabeCodecs;
+
+ auto getCodecId = [] (const QString& codecName) {
+ if (codecName == "3gpp"_L1) return AV_CODEC_ID_H263;
+ if (codecName == "avc"_L1) return AV_CODEC_ID_H264;
+ if (codecName == "hevc"_L1) return AV_CODEC_ID_HEVC;
+ if (codecName == "mp4v-es"_L1) return AV_CODEC_ID_MPEG4;
+ if (codecName == "x-vnd.on2.vp8"_L1) return AV_CODEC_ID_VP8;
+ if (codecName == "x-vnd.on2.vp9"_L1) return AV_CODEC_ID_VP9;
+ return AV_CODEC_ID_NONE;
+ };
+
+ const QJniObject jniCodecs =
+ QtJniTypes::QtVideoDeviceManager::callStaticMethod<QtJniTypes::String[]>(
+ type == ENCODERS ? "getHWVideoEncoders" : "getHWVideoDecoders");
+
+ QJniArray<QtJniTypes::String> arrCodecs(jniCodecs.object<jobjectArray>());
+ for (int i = 0; i < arrCodecs.size(); ++i) {
+ availabeCodecs.insert(getCodecId(arrCodecs.at(i).toString()));
+ }
+ return availabeCodecs;
+#else
+ Q_UNUSED(type);
+ return {};
+#endif
+}
+
+const CodecsStorage &codecsStorage(CodecStorageType codecsType)
+{
+ static const auto &storages = []() {
+ std::array<CodecsStorage, CODEC_STORAGE_TYPE_COUNT> result;
+ void *opaque = nullptr;
+ const auto platformHwEncoders = availableHWCodecs(ENCODERS);
+ const auto platformHwDecoders = availableHWCodecs(DECODERS);
+
+ while (auto codec = av_codec_iterate(&opaque)) {
+ // TODO: to be investigated
+ // FFmpeg functions avcodec_find_decoder/avcodec_find_encoder
+ // find experimental codecs in the last order,
+ // now we don't consider them at all since they are supposed to
+ // be not stable, maybe we shouldn't.
+ // Currently, it's possible to turn them on for testing purposes.
+
+ static const auto experimentalCodecsEnabled =
+ qEnvironmentVariableIntValue("QT_ENABLE_EXPERIMENTAL_CODECS");
+
+ if (!experimentalCodecsEnabled && isAVCodecExperimental(codec)) {
+ qCDebug(qLcFFmpegUtils) << "Skip experimental codec" << codec->name;
+ continue;
+ }
+
+ if (av_codec_is_decoder(codec)) {
+ if (isCodecValid(codec, HWAccel::decodingDeviceTypes(), platformHwDecoders))
+ result[DECODERS].emplace_back(codec);
+ else
+ qCDebug(qLcFFmpegUtils)
+ << "Skip decoder" << codec->name
+ << "due to disabled matching hw acceleration, or dysfunctional codec";
+ }
+
+ if (av_codec_is_encoder(codec)) {
+ if (isCodecValid(codec, HWAccel::encodingDeviceTypes(), platformHwEncoders))
+ result[ENCODERS].emplace_back(codec);
+ else
+ qCDebug(qLcFFmpegUtils)
+ << "Skip encoder" << codec->name
+ << "due to disabled matching hw acceleration, or dysfunctional codec";
+ }
+ }
+
+ for (auto &storage : result) {
+ storage.shrink_to_fit();
+
+ // we should ensure the original order
+ std::stable_sort(storage.begin(), storage.end(), CodecsComparator{});
+ }
+
+ // It print pretty much logs, so let's print it only for special case
+ const bool shouldDumpCodecsInfo = qLcFFmpegUtils().isEnabled(QtDebugMsg)
+ && qEnvironmentVariableIsSet("QT_FFMPEG_DEBUG");
+
+ if (shouldDumpCodecsInfo) {
+ qCDebug(qLcFFmpegUtils) << "Advanced FFmpeg codecs info:";
+ for (auto &storage : result) {
+ std::for_each(storage.begin(), storage.end(), &dumpCodecInfo);
+ qCDebug(qLcFFmpegUtils) << "---------------------------";
+ }
+ }
+
+ return result;
+ }();
+
+ return storages[codecsType];
+}
+
+const char *preferredHwCodecNameSuffix(bool isEncoder, AVHWDeviceType deviceType)
+{
+ switch (deviceType) {
+ case AV_HWDEVICE_TYPE_VAAPI:
+ return "_vaapi";
+ case AV_HWDEVICE_TYPE_MEDIACODEC:
+ return "_mediacodec";
+ case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
+ return "_videotoolbox";
+ case AV_HWDEVICE_TYPE_D3D11VA:
+ case AV_HWDEVICE_TYPE_DXVA2:
+#if QT_FFMPEG_HAS_D3D12VA
+ case AV_HWDEVICE_TYPE_D3D12VA:
+#endif
+ return "_mf";
+ case AV_HWDEVICE_TYPE_CUDA:
+ case AV_HWDEVICE_TYPE_VDPAU:
+ return isEncoder ? "_nvenc" : "_cuvid";
+ default:
+ return nullptr;
+ }
+}
+
+template<typename CodecScoreGetter>
+const AVCodec *findAVCodec(CodecStorageType codecsType, AVCodecID codecId,
+ const CodecScoreGetter &scoreGetter)
+{
+ const auto &storage = codecsStorage(codecsType);
+ auto it = std::lower_bound(storage.begin(), storage.end(), codecId, CodecsComparator{});
+
+ const AVCodec *result = nullptr;
+ AVScore resultScore = NotSuitableAVScore;
+
+ for (; it != storage.end() && (*it)->id == codecId && resultScore != BestAVScore; ++it) {
+ const auto score = scoreGetter(*it);
+
+ if (score > resultScore) {
+ resultScore = score;
+ result = *it;
+ }
+ }
+
+ return result;
+}
+
+AVScore hwCodecNameScores(const AVCodec *codec, AVHWDeviceType deviceType)
+{
+ if (auto suffix = preferredHwCodecNameSuffix(av_codec_is_encoder(codec), deviceType)) {
+ const auto substr = strstr(codec->name, suffix);
+ if (substr && !substr[strlen(suffix)])
+ return BestAVScore;
+
+ return DefaultAVScore;
+ }
+
+ return BestAVScore;
+}
+
+const AVCodec *findAVCodec(CodecStorageType codecsType, AVCodecID codecId,
+ const std::optional<AVHWDeviceType> &deviceType,
+ const std::optional<PixelOrSampleFormat> &format)
+{
+ return findAVCodec(codecsType, codecId, [&](const AVCodec *codec) {
+ if (format && !isAVFormatSupported(codec, *format))
+ return NotSuitableAVScore;
+
+ if (!deviceType)
+ return BestAVScore; // find any codec with the id
+
+ if (*deviceType == AV_HWDEVICE_TYPE_NONE
+ && findAVFormat(codec->pix_fmts, &isSwPixelFormat) != AV_PIX_FMT_NONE)
+ return BestAVScore;
+
+ if (*deviceType != AV_HWDEVICE_TYPE_NONE) {
+ for (int index = 0; auto config = avcodec_get_hw_config(codec, index); ++index) {
+ if (config->device_type != deviceType)
+ continue;
+
+ if (format && config->pix_fmt != AV_PIX_FMT_NONE && config->pix_fmt != *format)
+ continue;
+
+ return hwCodecNameScores(codec, *deviceType);
+ }
+
+ // The situation happens mostly with encoders
+ // Probably, it's ffmpeg bug: avcodec_get_hw_config returns null even though
+ // hw acceleration is supported
+ if (hasAVFormat(codec->pix_fmts, pixelFormatForHwDevice(*deviceType)))
+ return hwCodecNameScores(codec, *deviceType);
+ }
+
+ return NotSuitableAVScore;
+ });
+}
+
+} // namespace
+
+const AVCodec *findAVDecoder(AVCodecID codecId, const std::optional<AVHWDeviceType> &deviceType,
+ const std::optional<PixelOrSampleFormat> &format)
+{
+ return findAVCodec(DECODERS, codecId, deviceType, format);
+}
+
+const AVCodec *findAVEncoder(AVCodecID codecId, const std::optional<AVHWDeviceType> &deviceType,
+ const std::optional<PixelOrSampleFormat> &format)
+{
+ return findAVCodec(ENCODERS, codecId, deviceType, format);
+}
+
+const AVCodec *findAVEncoder(AVCodecID codecId,
+ const std::function<AVScore(const AVCodec *)> &scoresGetter)
+{
+ return findAVCodec(ENCODERS, codecId, scoresGetter);
+}
+
+bool isAVFormatSupported(const AVCodec *codec, PixelOrSampleFormat format)
+{
+ if (codec->type == AVMEDIA_TYPE_VIDEO)
+ return hasAVFormat(codec->pix_fmts, AVPixelFormat(format));
+
+ if (codec->type == AVMEDIA_TYPE_AUDIO)
+ return hasAVFormat(codec->sample_fmts, AVSampleFormat(format));
+
+ return false;
+}
+
+bool isHwPixelFormat(AVPixelFormat format)
+{
+ const auto desc = av_pix_fmt_desc_get(format);
+ return desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0;
+}
+
+bool isAVCodecExperimental(const AVCodec *codec)
+{
+ return (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) != 0;
+}
+
+void applyExperimentalCodecOptions(const AVCodec *codec, AVDictionary** opts)
+{
+ if (isAVCodecExperimental(codec)) {
+ qCWarning(qLcFFmpegUtils) << "Applying the option 'strict -2' for the experimental codec"
+ << codec->name << ". it's unlikely to work properly";
+ av_dict_set(opts, "strict", "-2", 0);
+ }
+}
+
+AVPixelFormat pixelFormatForHwDevice(AVHWDeviceType deviceType)
+{
+ switch (deviceType) {
+ case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
+ return AV_PIX_FMT_VIDEOTOOLBOX;
+ case AV_HWDEVICE_TYPE_VAAPI:
+ return AV_PIX_FMT_VAAPI;
+ case AV_HWDEVICE_TYPE_MEDIACODEC:
+ return AV_PIX_FMT_MEDIACODEC;
+ case AV_HWDEVICE_TYPE_CUDA:
+ return AV_PIX_FMT_CUDA;
+ case AV_HWDEVICE_TYPE_VDPAU:
+ return AV_PIX_FMT_VDPAU;
+ case AV_HWDEVICE_TYPE_OPENCL:
+ return AV_PIX_FMT_OPENCL;
+ case AV_HWDEVICE_TYPE_QSV:
+ return AV_PIX_FMT_QSV;
+ case AV_HWDEVICE_TYPE_D3D11VA:
+ return AV_PIX_FMT_D3D11;
+#if QT_FFMPEG_HAS_D3D12VA
+ case AV_HWDEVICE_TYPE_D3D12VA:
+ return AV_PIX_FMT_D3D12;
+#endif
+ case AV_HWDEVICE_TYPE_DXVA2:
+ return AV_PIX_FMT_DXVA2_VLD;
+ case AV_HWDEVICE_TYPE_DRM:
+ return AV_PIX_FMT_DRM_PRIME;
+#if QT_FFMPEG_HAS_VULKAN
+ case AV_HWDEVICE_TYPE_VULKAN:
+ return AV_PIX_FMT_VULKAN;
+#endif
+ default:
+ return AV_PIX_FMT_NONE;
+ }
+}
+
+AVPacketSideData *addStreamSideData(AVStream *stream, AVPacketSideData sideData)
+{
+ QScopeGuard freeData([&sideData]() { av_free(sideData.data); });
+#if QT_FFMPEG_STREAM_SIDE_DATA_DEPRECATED
+ AVPacketSideData *result = av_packet_side_data_add(
+ &stream->codecpar->coded_side_data,
+ &stream->codecpar->nb_coded_side_data,
+ sideData.type,
+ sideData.data,
+ sideData.size,
+ 0);
+ if (result) {
+ // If the result is not null, the ownership is taken by AVStream,
+ // otherwise the data must be deleted.
+ freeData.dismiss();
+ return result;
+ }
+#else
+ Q_UNUSED(stream);
+ // TODO: implement for older FFmpeg versions
+ qWarning() << "Adding stream side data is not supported for FFmpeg < 6.1";
+#endif
+
+ return nullptr;
+}
+
+const AVPacketSideData *streamSideData(const AVStream *stream, AVPacketSideDataType type)
+{
+ Q_ASSERT(stream);
+
+#if QT_FFMPEG_STREAM_SIDE_DATA_DEPRECATED
+ return av_packet_side_data_get(stream->codecpar->coded_side_data,
+ stream->codecpar->nb_coded_side_data, type);
+#else
+ auto checkType = [type](const auto &item) { return item.type == type; };
+ const auto end = stream->side_data + stream->nb_side_data;
+ const auto found = std::find_if(stream->side_data, end, checkType);
+ return found == end ? nullptr : found;
+#endif
+}
+
+SwrContextUPtr createResampleContext(const AVAudioFormat &inputFormat,
+ const AVAudioFormat &outputFormat)
+{
+ SwrContext *resampler = nullptr;
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ resampler = swr_alloc_set_opts(nullptr,
+ outputFormat.channelLayoutMask,
+ outputFormat.sampleFormat,
+ outputFormat.sampleRate,
+ inputFormat.channelLayoutMask,
+ inputFormat.sampleFormat,
+ inputFormat.sampleRate,
+ 0,
+ nullptr);
+#else
+
+#if QT_FFMPEG_SWR_CONST_CH_LAYOUT
+ using AVChannelLayoutPrm = const AVChannelLayout*;
+#else
+ using AVChannelLayoutPrm = AVChannelLayout*;
+#endif
+
+ swr_alloc_set_opts2(&resampler,
+ const_cast<AVChannelLayoutPrm>(&outputFormat.channelLayout),
+ outputFormat.sampleFormat,
+ outputFormat.sampleRate,
+ const_cast<AVChannelLayoutPrm>(&inputFormat.channelLayout),
+ inputFormat.sampleFormat,
+ inputFormat.sampleRate,
+ 0,
+ nullptr);
+#endif
+
+ swr_init(resampler);
+ return SwrContextUPtr(resampler);
+}
+
+#ifdef Q_OS_DARWIN
+bool isCVFormatSupported(uint32_t cvFormat)
+{
+ return av_map_videotoolbox_format_to_pixfmt(cvFormat) != AV_PIX_FMT_NONE;
+}
+
+std::string cvFormatToString(uint32_t cvFormat)
+{
+ auto formatDescIt = std::make_reverse_iterator(reinterpret_cast<const char *>(&cvFormat));
+ return std::string(formatDescIt - 4, formatDescIt);
+}
+
+#endif
+
+} // namespace QFFmpeg
+
+QDebug operator<<(QDebug dbg, const AVRational &value)
+{
+ dbg << value.num << "/" << value.den;
+ return dbg;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeg_p.h b/src/plugins/multimedia/ffmpeg/qffmpeg_p.h
index ac9076bbf..6e19b9650 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeg_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpeg_p.h
@@ -1,53 +1,33 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEG_P_H
#define QFFMPEG_P_H
-#include <private/qtmultimediaglobal_p.h>
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpegdefs_p.h"
+#include "qffmpegavaudioformat_p.h"
+
#include <qstring.h>
+#include <optional>
+
+inline bool operator==(const AVRational &lhs, const AVRational &rhs)
+{
+ return lhs.num == rhs.num && lhs.den == rhs.den;
+}
-extern "C" {
-#include <libavformat/avformat.h>
-#include <libavcodec/avcodec.h>
-#include <libswresample/swresample.h>
-#include <libavutil/avutil.h>
-#include <libswscale/swscale.h>
+inline bool operator!=(const AVRational &lhs, const AVRational &rhs)
+{
+ return !(lhs == rhs);
}
QT_BEGIN_NAMESPACE
@@ -55,17 +35,30 @@ QT_BEGIN_NAMESPACE
namespace QFFmpeg
{
-inline qint64 timeStamp(qint64 ts, AVRational base)
+inline std::optional<qint64> mul(qint64 a, AVRational b)
+{
+ return b.den != 0 ? (a * b.num + b.den / 2) / b.den : std::optional<qint64>{};
+}
+
+inline std::optional<qreal> mul(qreal a, AVRational b)
+{
+ return b.den != 0 ? a * qreal(b.num) / qreal(b.den) : std::optional<qreal>{};
+}
+
+inline std::optional<qint64> timeStampMs(qint64 ts, AVRational base)
{
- return (1000*ts*base.num + 500)/base.den;
+ return mul(1'000 * ts, base);
}
-inline qint64 timeStampUs(qint64 ts, AVRational base)
+inline std::optional<qint64> timeStampUs(qint64 ts, AVRational base)
{
- return (1000000*ts*base.num + 500000)/base.den;
+ return mul(1'000'000 * ts, base);
}
-inline float toFloat(AVRational r) { return float(r.num)/float(r.den); }
+inline std::optional<float> toFloat(AVRational r)
+{
+ return r.den != 0 ? float(r.num) / float(r.den) : std::optional<float>{};
+}
inline QString err2str(int errnum)
{
@@ -74,8 +67,183 @@ inline QString err2str(int errnum)
return QString::fromLocal8Bit(buffer);
}
-QT_END_NAMESPACE
+inline void setAVFrameTime(AVFrame &frame, int64_t pts, const AVRational &timeBase)
+{
+ frame.pts = pts;
+#if QT_FFMPEG_HAS_FRAME_TIME_BASE
+ frame.time_base = timeBase;
+#else
+ Q_UNUSED(timeBase);
+#endif
+}
+
+inline void getAVFrameTime(const AVFrame &frame, int64_t &pts, AVRational &timeBase)
+{
+ pts = frame.pts;
+#if QT_FFMPEG_HAS_FRAME_TIME_BASE
+ timeBase = frame.time_base;
+#else
+ timeBase = { 0, 1 };
+#endif
+}
+
+inline int64_t getAVFrameDuration(const AVFrame &frame)
+{
+#if QT_FFMPEG_HAS_FRAME_DURATION
+ return frame.duration;
+#else
+ Q_UNUSED(frame);
+ return 0;
+#endif
+}
+
+struct AVDictionaryHolder
+{
+ AVDictionary *opts = nullptr;
+
+ operator AVDictionary **() { return &opts; }
+
+ AVDictionaryHolder() = default;
+
+ Q_DISABLE_COPY(AVDictionaryHolder)
+
+ AVDictionaryHolder(AVDictionaryHolder &&other) noexcept
+ : opts(std::exchange(other.opts, nullptr))
+ {
+ }
+
+ ~AVDictionaryHolder()
+ {
+ if (opts)
+ av_dict_free(&opts);
+ }
+};
+
+template<typename FunctionType, FunctionType F>
+struct AVDeleter
+{
+ template<typename T>
+ void operator()(T *object) const
+ {
+ if (object)
+ F(&object);
+ }
+};
+
+using AVFrameUPtr = std::unique_ptr<AVFrame, AVDeleter<decltype(&av_frame_free), &av_frame_free>>;
+
+inline AVFrameUPtr makeAVFrame()
+{
+ return AVFrameUPtr(av_frame_alloc());
+}
+using AVPacketUPtr =
+ std::unique_ptr<AVPacket, AVDeleter<decltype(&av_packet_free), &av_packet_free>>;
+
+using AVCodecContextUPtr =
+ std::unique_ptr<AVCodecContext,
+ AVDeleter<decltype(&avcodec_free_context), &avcodec_free_context>>;
+
+using AVBufferUPtr =
+ std::unique_ptr<AVBufferRef, AVDeleter<decltype(&av_buffer_unref), &av_buffer_unref>>;
+
+using AVHWFramesConstraintsUPtr = std::unique_ptr<
+ AVHWFramesConstraints,
+ AVDeleter<decltype(&av_hwframe_constraints_free), &av_hwframe_constraints_free>>;
+
+using SwrContextUPtr = std::unique_ptr<SwrContext, AVDeleter<decltype(&swr_free), &swr_free>>;
+
+using PixelOrSampleFormat = int;
+using AVScore = int;
+constexpr AVScore BestAVScore = std::numeric_limits<AVScore>::max();
+constexpr AVScore DefaultAVScore = 0;
+constexpr AVScore NotSuitableAVScore = std::numeric_limits<AVScore>::min();
+constexpr AVScore MinAVScore = NotSuitableAVScore + 1;
+
+const AVCodec *findAVDecoder(AVCodecID codecId,
+ const std::optional<AVHWDeviceType> &deviceType = {},
+ const std::optional<PixelOrSampleFormat> &format = {});
+
+const AVCodec *findAVEncoder(AVCodecID codecId,
+ const std::optional<AVHWDeviceType> &deviceType = {},
+ const std::optional<PixelOrSampleFormat> &format = {});
+
+const AVCodec *findAVEncoder(AVCodecID codecId,
+ const std::function<AVScore(const AVCodec *)> &scoresGetter);
+
+bool isAVFormatSupported(const AVCodec *codec, PixelOrSampleFormat format);
+
+template<typename Format>
+bool hasAVFormat(const Format *fmts, Format format)
+{
+ return findAVFormat(fmts, [format](Format f) { return f == format; }) != Format(-1);
+}
+
+template<typename Format, typename Predicate>
+Format findAVFormat(const Format *fmts, const Predicate &predicate)
+{
+ auto scoresGetter = [&predicate](Format fmt) {
+ return predicate(fmt) ? BestAVScore : NotSuitableAVScore;
+ };
+ return findBestAVFormat(fmts, scoresGetter).first;
+}
+
+template <typename Value, typename CalculateScore>
+auto findBestAVValue(const Value *values, const CalculateScore &calculateScore,
+ Value invalidValue = {})
+{
+ using Limits = std::numeric_limits<decltype(calculateScore(*values))>;
+ std::pair result(invalidValue, Limits::min());
+ if (values) {
+ for (; *values != invalidValue && result.second != Limits::max(); ++values) {
+ const auto score = calculateScore(*values);
+ if (score > result.second)
+ result = { *values, score };
+ }
+ }
+
+ return result;
}
+template <typename Format, typename CalculateScore>
+std::pair<Format, AVScore> findBestAVFormat(const Format *fmts,
+ const CalculateScore &calculateScore)
+{
+ static_assert(std::is_same_v<Format, AVSampleFormat> || std::is_same_v<Format, AVPixelFormat>,
+ "The input value is not AV format, use findBestAVValue instead.");
+ return findBestAVValue(fmts, calculateScore, Format(-1));
+}
+
+bool isHwPixelFormat(AVPixelFormat format);
+
+inline bool isSwPixelFormat(AVPixelFormat format)
+{
+ return !isHwPixelFormat(format);
+}
+
+bool isAVCodecExperimental(const AVCodec *codec);
+
+void applyExperimentalCodecOptions(const AVCodec *codec, AVDictionary** opts);
+
+AVPixelFormat pixelFormatForHwDevice(AVHWDeviceType deviceType);
+
+AVPacketSideData *addStreamSideData(AVStream *stream, AVPacketSideData sideData);
+
+const AVPacketSideData *streamSideData(const AVStream *stream, AVPacketSideDataType type);
+
+SwrContextUPtr createResampleContext(const AVAudioFormat &inputFormat,
+ const AVAudioFormat &outputFormat);
+
+#ifdef Q_OS_DARWIN
+bool isCVFormatSupported(uint32_t format);
+
+std::string cvFormatToString(uint32_t format);
+
+#endif
+}
+
+QDebug operator<<(QDebug, const AVRational &);
+
+QT_END_NAMESPACE
+
#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp
index 86508f24d..69820cc79 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder.cpp
@@ -1,54 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-//#define DEBUG_DECODER
-
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegaudiodecoder_p.h"
-#include "qffmpegdecoder_p.h"
-#include "qffmpegmediaformatinfo_p.h"
#include "qffmpegresampler_p.h"
#include "qaudiobuffer.h"
-#include <qloggingcategory.h>
+#include "qffmpegplaybackengine_p.h"
+#include "playbackengine/qffmpegrenderer_p.h"
-Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder")
+#include <qloggingcategory.h>
-#define MAX_BUFFERS_IN_QUEUE 4
+static Q_LOGGING_CATEGORY(qLcAudioDecoder, "qt.multimedia.ffmpeg.audioDecoder")
QT_BEGIN_NAMESPACE
@@ -57,96 +18,67 @@ namespace QFFmpeg
class SteppingAudioRenderer : public Renderer
{
+ Q_OBJECT
public:
- SteppingAudioRenderer(AudioDecoder *decoder, const QAudioFormat &format);
- ~SteppingAudioRenderer()
+ SteppingAudioRenderer(const QAudioFormat &format) : Renderer({}), m_format(format) { }
+
+ RenderingResult renderInternal(Frame frame) override
{
+ if (!frame.isValid())
+ return {};
+
+ if (!m_resampler)
+ m_resampler = std::make_unique<QFFmpegResampler>(frame.codec(), m_format);
+
+ emit newAudioBuffer(m_resampler->resample(frame.avFrame()));
+
+ return {};
}
- void loop() override;
- AudioDecoder *m_decoder;
+signals:
+ void newAudioBuffer(QAudioBuffer);
+
+private:
QAudioFormat m_format;
- std::unique_ptr<Resampler> resampler;
- bool atEndEmitted = false;
+ std::unique_ptr<QFFmpegResampler> m_resampler;
};
-class AudioDecoder : public Decoder
+class AudioDecoder : public PlaybackEngine
{
Q_OBJECT
public:
- explicit AudioDecoder(QFFmpegAudioDecoder *audioDecoder)
- : Decoder(audioDecoder)
- {}
+ explicit AudioDecoder(const QAudioFormat &format) : m_format(format) { }
- void setup(const QAudioFormat &format)
+ RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType) override
{
- connect(this, &AudioDecoder::newAudioBuffer, audioDecoder, &QFFmpegAudioDecoder::newAudioBuffer);
- connect(this, &AudioDecoder::isAtEnd, audioDecoder, &QFFmpegAudioDecoder::done);
- m_format = format;
- audioRenderer = new SteppingAudioRenderer(this, format);
- audioRenderer->start();
- auto *stream = demuxer->addStream(m_currentAVStreamIndex[QPlatformMediaPlayer::AudioStream]);
- audioRenderer->setStream(stream);
+ if (trackType != QPlatformMediaPlayer::AudioStream)
+ return RendererPtr{ {}, {} };
+
+ auto result = createPlaybackEngineObject<SteppingAudioRenderer>(m_format);
+ m_audioRenderer = result.get();
+
+ connect(result.get(), &SteppingAudioRenderer::newAudioBuffer, this,
+ &AudioDecoder::newAudioBuffer);
+
+ return result;
}
void nextBuffer()
{
- audioRenderer->setPaused(false);
+ Q_ASSERT(m_audioRenderer);
+ Q_ASSERT(!m_audioRenderer->isStepForced());
+
+ m_audioRenderer->doForceStep();
+ // updateObjectsPausedState();
}
-Q_SIGNALS:
- void newAudioBuffer(const QAudioBuffer &b);
- void isAtEnd();
+signals:
+ void newAudioBuffer(QAudioBuffer);
private:
+ QPointer<Renderer> m_audioRenderer;
QAudioFormat m_format;
};
-
-SteppingAudioRenderer::SteppingAudioRenderer(AudioDecoder *decoder, const QAudioFormat &format)
- : Renderer(QPlatformMediaPlayer::AudioStream)
- , m_decoder(decoder)
- , m_format(format)
-{
-}
-
-
-void SteppingAudioRenderer::loop()
-{
- if (!streamDecoder) {
- qCDebug(qLcAudioDecoder) << "no stream";
- timeOut = -1; // Avoid CPU load before play()
- return;
- }
-
- Frame frame = streamDecoder->takeFrame();
- if (!frame.isValid()) {
- if (streamDecoder->isAtEnd()) {
- if (!atEndEmitted)
- emit m_decoder->isAtEnd();
- atEndEmitted = true;
- paused = true;
- doneStep();
- timeOut = -1;
- return;
- }
- timeOut = 10;
- streamDecoder->wake();
- return;
- }
- qCDebug(qLcAudioDecoder) << " got frame";
-
- doneStep();
-
- if (!resampler)
- resampler.reset(new Resampler(frame.codec(), m_format));
-
- auto buffer = resampler->resample(frame.avFrame());
- paused = true;
- timeOut = -1;
-
- emit m_decoder->newAudioBuffer(buffer);
-}
-
}
@@ -155,10 +87,7 @@ QFFmpegAudioDecoder::QFFmpegAudioDecoder(QAudioDecoder *parent)
{
}
-QFFmpegAudioDecoder::~QFFmpegAudioDecoder()
-{
- delete decoder;
-}
+QFFmpegAudioDecoder::~QFFmpegAudioDecoder() = default;
QUrl QFFmpegAudioDecoder::source() const
{
@@ -170,11 +99,8 @@ void QFFmpegAudioDecoder::setSource(const QUrl &fileName)
stop();
m_sourceDevice = nullptr;
- if (m_url == fileName)
- return;
- m_url = fileName;
-
- emit sourceChanged();
+ if (std::exchange(m_url, fileName) != fileName)
+ sourceChanged();
}
QIODevice *QFFmpegAudioDecoder::sourceDevice() const
@@ -186,47 +112,65 @@ void QFFmpegAudioDecoder::setSourceDevice(QIODevice *device)
{
stop();
m_url.clear();
- bool isSignalRequired = (m_sourceDevice != device);
- m_sourceDevice = device;
- if (isSignalRequired)
+ if (std::exchange(m_sourceDevice, device) != device)
sourceChanged();
}
void QFFmpegAudioDecoder::start()
{
qCDebug(qLcAudioDecoder) << "start";
- delete decoder;
- decoder = new QFFmpeg::AudioDecoder(this);
- decoder->setMedia(m_url, m_sourceDevice);
- if (error() != QAudioDecoder::NoError)
- goto error;
-
- decoder->setup(m_audioFormat);
- if (error() != QAudioDecoder::NoError)
- goto error;
- decoder->play();
- if (error() != QAudioDecoder::NoError)
- goto error;
- decoder->nextBuffer();
- if (error() != QAudioDecoder::NoError)
- goto error;
+ auto checkNoError = [this]() {
+ if (error() == QAudioDecoder::NoError)
+ return true;
+
+ durationChanged(-1);
+ positionChanged(-1);
+
+ m_decoder.reset();
+
+ return false;
+ };
+
+ m_decoder = std::make_unique<AudioDecoder>(m_audioFormat);
+ connect(m_decoder.get(), &AudioDecoder::errorOccured, this, &QFFmpegAudioDecoder::errorSignal);
+ connect(m_decoder.get(), &AudioDecoder::endOfStream, this, &QFFmpegAudioDecoder::done);
+ connect(m_decoder.get(), &AudioDecoder::newAudioBuffer, this,
+ &QFFmpegAudioDecoder::newAudioBuffer);
+
+ QFFmpeg::MediaDataHolder::Maybe media = QFFmpeg::MediaDataHolder::create(m_url, m_sourceDevice, nullptr);
+
+ if (media) {
+ Q_ASSERT(media.value());
+ if (media.value()->streamInfo(QPlatformMediaPlayer::AudioStream).isEmpty())
+ error(QAudioDecoder::FormatError,
+ QLatin1String("The media doesn't contain an audio stream"));
+ else
+ m_decoder->setMedia(std::move(*media.value()));
+ } else {
+ auto [code, description] = media.error();
+ errorSignal(code, description);
+ }
- setIsDecoding(true);
- return;
+ if (!checkNoError())
+ return;
+
+ m_decoder->setState(QMediaPlayer::PausedState);
+ if (!checkNoError())
+ return;
- error:
- durationChanged(-1);
- positionChanged(-1);
- delete decoder;
- decoder = nullptr;
+ m_decoder->nextBuffer();
+ if (!checkNoError())
+ return;
+ durationChanged(m_decoder->duration() / 1000);
+ setIsDecoding(true);
}
void QFFmpegAudioDecoder::stop()
{
qCDebug(qLcAudioDecoder) << ">>>>> stop";
- if (decoder) {
- decoder->stop();
+ if (m_decoder) {
+ m_decoder.reset();
done();
}
}
@@ -238,26 +182,28 @@ QAudioFormat QFFmpegAudioDecoder::audioFormat() const
void QFFmpegAudioDecoder::setAudioFormat(const QAudioFormat &format)
{
- if (m_audioFormat == format)
- return;
-
- m_audioFormat = format;
- formatChanged(m_audioFormat);
+ if (std::exchange(m_audioFormat, format) != format)
+ formatChanged(m_audioFormat);
}
QAudioBuffer QFFmpegAudioDecoder::read()
{
- auto b = m_audioBuffer;
- qCDebug(qLcAudioDecoder) << "reading buffer" << b.startTime();
- m_audioBuffer = {};
+ auto buffer = std::exchange(m_audioBuffer, QAudioBuffer{});
+ if (!buffer.isValid())
+ return buffer;
+ qCDebug(qLcAudioDecoder) << "reading buffer" << buffer.startTime();
bufferAvailableChanged(false);
- if (decoder)
- decoder->nextBuffer();
- return b;
+ if (m_decoder)
+ m_decoder->nextBuffer();
+ return buffer;
}
void QFFmpegAudioDecoder::newAudioBuffer(const QAudioBuffer &b)
{
+ Q_ASSERT(b.isValid());
+ Q_ASSERT(!m_audioBuffer.isValid());
+ Q_ASSERT(!bufferAvailable());
+
qCDebug(qLcAudioDecoder) << "new audio buffer" << b.startTime();
m_audioBuffer = b;
const qint64 pos = b.startTime();
@@ -272,6 +218,30 @@ void QFFmpegAudioDecoder::done()
finished();
}
+void QFFmpegAudioDecoder::errorSignal(int err, const QString &errorString)
+{
+ // unfortunately the error enums for QAudioDecoder and QMediaPlayer aren't identical.
+ // Map them.
+ switch (QMediaPlayer::Error(err)) {
+ case QMediaPlayer::NoError:
+ error(QAudioDecoder::NoError, errorString);
+ break;
+ case QMediaPlayer::ResourceError:
+ error(QAudioDecoder::ResourceError, errorString);
+ break;
+ case QMediaPlayer::FormatError:
+ error(QAudioDecoder::FormatError, errorString);
+ break;
+ case QMediaPlayer::NetworkError:
+ // fall through, Network error doesn't exist in QAudioDecoder
+ case QMediaPlayer::AccessDeniedError:
+ error(QAudioDecoder::AccessDeniedError, errorString);
+ break;
+ }
+}
+
QT_END_NAMESPACE
+#include "moc_qffmpegaudiodecoder_p.cpp"
+
#include "qffmpegaudiodecoder.moc"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h
index 790a29b04..11816cd6f 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegaudiodecoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGAUDIODECODER_H
#define QFFMPEGAUDIODECODER_H
@@ -53,10 +17,7 @@
#include "private/qplatformaudiodecoder_p.h"
#include <qffmpeg_p.h>
-
-#include <qmutex.h>
#include <qurl.h>
-#include <qqueue.h>
QT_BEGIN_NAMESPACE
@@ -89,11 +50,14 @@ public:
public Q_SLOTS:
void newAudioBuffer(const QAudioBuffer &b);
void done();
+ void errorSignal(int err, const QString &errorString);
private:
+ using AudioDecoder = QFFmpeg::AudioDecoder;
+
QUrl m_url;
QIODevice *m_sourceDevice = nullptr;
- QFFmpeg::AudioDecoder *decoder = nullptr;
+ std::unique_ptr<AudioDecoder> m_decoder;
QAudioFormat m_audioFormat;
QAudioBuffer m_audioBuffer;
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp
index 71979e04f..014c53ca4 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput.cpp
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegaudioinput_p.h"
#include <qiodevice.h>
#include <qaudiosource.h>
#include <qaudiobuffer.h>
+#include <qatomic.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
@@ -49,37 +14,33 @@ namespace QFFmpeg {
class AudioSourceIO : public QIODevice
{
Q_OBJECT
- public:
- AudioSourceIO(QFFmpegAudioInput *audioInput)
- : QIODevice()
- , input(audioInput)
+public:
+ AudioSourceIO(QFFmpegAudioInput *audioInput) : QIODevice(), m_input(audioInput)
{
- m_muted = input->muted;
- m_volume = input->volume;
+ m_muted = m_input->muted;
+ m_volume = m_input->volume;
updateVolume();
open(QIODevice::WriteOnly);
}
- ~AudioSourceIO()
- {
- delete m_src;
- }
+
+ ~AudioSourceIO() override = default;
void setDevice(const QAudioDevice &device)
{
- QMutexLocker locker(&mutex);
+ QMutexLocker locker(&m_mutex);
if (m_device == device)
return;
m_device = device;
QMetaObject::invokeMethod(this, "updateSource");
}
- void setFrameSize(int s)
+ void setFrameSize(int frameSize)
{
- QMutexLocker locker(&mutex);
- frameSize = s;
- bufferSize = m_format.bytesForFrames(frameSize);
+ m_bufferSize.storeRelease((frameSize > 0 && m_format.isValid())
+ ? m_format.bytesForFrames(frameSize)
+ : DefaultAudioInputBufferSize);
}
void setRunning(bool r) {
- QMutexLocker locker(&mutex);
+ QMutexLocker locker(&m_mutex);
if (m_running == r)
return;
m_running = r;
@@ -87,16 +48,17 @@ class AudioSourceIO : public QIODevice
}
void setVolume(float vol) {
- QMutexLocker locker(&mutex);
+ QMutexLocker locker(&m_mutex);
m_volume = vol;
QMetaObject::invokeMethod(this, "updateVolume");
}
void setMuted(bool muted) {
- QMutexLocker locker(&mutex);
+ QMutexLocker locker(&m_mutex);
m_muted = muted;
QMetaObject::invokeMethod(this, "updateVolume");
}
+ int bufferSize() const { return m_bufferSize.loadAcquire(); }
protected:
qint64 readData(char *, qint64) override
@@ -107,11 +69,12 @@ protected:
{
int l = len;
while (len > 0) {
- int toAppend = qMin(len, bufferSize - pcm.size());
- pcm.append(data, toAppend);
+ const auto bufferSize = m_bufferSize.loadAcquire();
+ int toAppend = qMin(len, bufferSize - m_pcm.size());
+ m_pcm.append(data, toAppend);
data += toAppend;
len -= toAppend;
- if (pcm.size() == bufferSize)
+ if (m_pcm.size() == bufferSize)
sendBuffer();
}
@@ -120,13 +83,12 @@ protected:
private Q_SLOTS:
void updateSource() {
- QMutexLocker locker(&mutex);
+ QMutexLocker locker(&m_mutex);
m_format = m_device.preferredFormat();
- if (m_src) {
- delete m_src;
- pcm.clear();
- }
- m_src = new QAudioSource(m_device, m_format);
+ if (std::exchange(m_src, nullptr))
+ m_pcm.clear();
+
+ m_src = std::make_unique<QAudioSource>(m_device, m_format);
updateVolume();
if (m_running)
m_src->start(this);
@@ -138,7 +100,7 @@ private Q_SLOTS:
}
void updateRunning()
{
- QMutexLocker locker(&mutex);
+ QMutexLocker locker(&m_mutex);
if (m_running) {
if (!m_src)
updateSource();
@@ -153,26 +115,25 @@ private:
void sendBuffer()
{
QAudioFormat fmt = m_src->format();
- qint64 time = fmt.durationForBytes(processed);
- QAudioBuffer buffer(pcm, fmt, time);
- emit input->newAudioBuffer(buffer);
- processed += bufferSize;
- pcm.clear();
+ qint64 time = fmt.durationForBytes(m_processed);
+ QAudioBuffer buffer(m_pcm, fmt, time);
+ emit m_input->newAudioBuffer(buffer);
+ m_processed += m_pcm.size();
+ m_pcm.clear();
}
- QMutex mutex;
+ QMutex m_mutex;
QAudioDevice m_device;
float m_volume = 1.;
bool m_muted = false;
bool m_running = false;
- QFFmpegAudioInput *input = nullptr;
- QAudioSource *m_src = nullptr;
+ QFFmpegAudioInput *m_input = nullptr;
+ std::unique_ptr<QAudioSource> m_src;
QAudioFormat m_format;
- int frameSize = 0;
- int bufferSize = 0;
- qint64 processed = 0;
- QByteArray pcm;
+ QAtomicInt m_bufferSize = DefaultAudioInputBufferSize;
+ qint64 m_processed = 0;
+ QByteArray m_pcm;
};
}
@@ -182,17 +143,19 @@ QFFmpegAudioInput::QFFmpegAudioInput(QAudioInput *qq)
{
qRegisterMetaType<QAudioBuffer>();
- inputThread = new QThread;
+ inputThread = std::make_unique<QThread>();
audioIO = new QFFmpeg::AudioSourceIO(this);
- audioIO->moveToThread(inputThread);
+ audioIO->moveToThread(inputThread.get());
inputThread->start();
}
QFFmpegAudioInput::~QFFmpegAudioInput()
{
+ // Ensure that COM is uninitialized by nested QWindowsResampler
+ // on the same thread that initialized it.
+ audioIO->deleteLater();
inputThread->exit();
inputThread->wait();
- delete inputThread;
}
void QFFmpegAudioInput::setAudioDevice(const QAudioDevice &device)
@@ -220,6 +183,13 @@ void QFFmpegAudioInput::setRunning(bool b)
audioIO->setRunning(b);
}
+int QFFmpegAudioInput::bufferSize() const
+{
+ return audioIO->bufferSize();
+}
+
QT_END_NAMESPACE
+#include "moc_qffmpegaudioinput_p.cpp"
+
#include "qffmpegaudioinput.moc"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h
index c4f87df9d..a232978f6 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegaudioinput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGAUDIOINPUT_H
#define QFFMPEGAUDIOINPUT_H
@@ -62,6 +26,8 @@ namespace QFFmpeg {
class AudioSourceIO;
}
+constexpr int DefaultAudioInputBufferSize = 4096;
+
class QFFmpegAudioInput : public QObject, public QPlatformAudioInput
{
Q_OBJECT
@@ -73,15 +39,17 @@ public:
void setMuted(bool /*muted*/) override;
void setVolume(float /*volume*/) override;
- void setFrameSize(int s);
+ void setFrameSize(int frameSize);
void setRunning(bool b);
+ int bufferSize() const;
+
Q_SIGNALS:
void newAudioBuffer(const QAudioBuffer &buffer);
private:
- QThread *inputThread = nullptr;
QFFmpeg::AudioSourceIO *audioIO = nullptr;
+ std::unique_ptr<QThread> inputThread;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegavaudioformat.cpp b/src/plugins/multimedia/ffmpeg/qffmpegavaudioformat.cpp
new file mode 100644
index 000000000..417219a48
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegavaudioformat.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegavaudioformat_p.h"
+#include "qaudioformat.h"
+#include "qffmpegmediaformatinfo_p.h"
+
+extern "C" {
+#include <libavutil/opt.h>
+}
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+AVAudioFormat::AVAudioFormat(const AVCodecContext *context)
+ : sampleFormat(context->sample_fmt), sampleRate(context->sample_rate)
+{
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ if (context->channel_layout) {
+ channelLayoutMask = context->channel_layout;
+ } else {
+ const auto channelConfig =
+ QAudioFormat::defaultChannelConfigForChannelCount(context->channels);
+ channelLayoutMask = QFFmpegMediaFormatInfo::avChannelLayout(channelConfig);
+ }
+#else
+ channelLayout = context->ch_layout;
+#endif
+}
+
+AVAudioFormat::AVAudioFormat(const AVCodecParameters *codecPar)
+ : sampleFormat(AVSampleFormat(codecPar->format)), sampleRate(codecPar->sample_rate)
+{
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ if (codecPar->channel_layout) {
+ channelLayoutMask = codecPar->channel_layout;
+ } else {
+ const auto channelConfig =
+ QAudioFormat::defaultChannelConfigForChannelCount(codecPar->channels);
+ channelLayoutMask = QFFmpegMediaFormatInfo::avChannelLayout(channelConfig);
+ }
+#else
+ channelLayout = codecPar->ch_layout;
+#endif
+}
+
+AVAudioFormat::AVAudioFormat(const QAudioFormat &audioFormat)
+ : sampleFormat(QFFmpegMediaFormatInfo::avSampleFormat(audioFormat.sampleFormat())),
+ sampleRate(audioFormat.sampleRate())
+{
+ const auto channelConfig = audioFormat.channelConfig() == QAudioFormat::ChannelConfigUnknown
+ ? QAudioFormat::defaultChannelConfigForChannelCount(audioFormat.channelCount())
+ : audioFormat.channelConfig();
+
+ const auto mask = QFFmpegMediaFormatInfo::avChannelLayout(channelConfig);
+
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ channelLayoutMask = mask;
+#else
+ av_channel_layout_from_mask(&channelLayout, mask);
+#endif
+}
+
+bool operator==(const AVAudioFormat &lhs, const AVAudioFormat &rhs)
+{
+ return lhs.sampleFormat == rhs.sampleFormat && lhs.sampleRate == rhs.sampleRate &&
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ lhs.channelLayoutMask == rhs.channelLayoutMask
+#else
+ lhs.channelLayout == rhs.channelLayout
+#endif
+ ;
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegavaudioformat_p.h b/src/plugins/multimedia/ffmpeg/qffmpegavaudioformat_p.h
new file mode 100644
index 000000000..9fd4dacdf
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegavaudioformat_p.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGAVAUDIOFORMAT_P_H
+#define QFFMPEGAVAUDIOFORMAT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpegdefs_p.h"
+#include <private/qtmultimediaglobal_p.h>
+
+#if !QT_FFMPEG_OLD_CHANNEL_LAYOUT
+inline bool operator==(const AVChannelLayout &lhs, const AVChannelLayout &rhs)
+{
+ return lhs.order == rhs.order && lhs.nb_channels == rhs.nb_channels && lhs.u.mask == rhs.u.mask;
+}
+
+inline bool operator!=(const AVChannelLayout &lhs, const AVChannelLayout &rhs)
+{
+ return !(lhs == rhs);
+}
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QAudioFormat;
+
+namespace QFFmpeg {
+
+struct AVAudioFormat
+{
+ AVAudioFormat(const AVCodecContext *context);
+
+ AVAudioFormat(const AVCodecParameters *codecPar);
+
+ AVAudioFormat(const QAudioFormat &audioFormat);
+
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ uint64_t channelLayoutMask;
+#else
+ AVChannelLayout channelLayout;
+#endif
+ AVSampleFormat sampleFormat;
+ int sampleRate;
+};
+
+bool operator==(const AVAudioFormat &lhs, const AVAudioFormat &rhs);
+
+inline bool operator!=(const AVAudioFormat &lhs, const AVAudioFormat &rhs)
+{
+ return !(lhs == rhs);
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGAVAUDIOFORMAT_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegclock.cpp b/src/plugins/multimedia/ffmpeg/qffmpegclock.cpp
deleted file mode 100644
index 8d463f081..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegclock.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qffmpegclock_p.h>
-#include <qloggingcategory.h>
-
-Q_LOGGING_CATEGORY(qLcClock, "qt.multimedia.ffmpeg.clock")
-
-QT_BEGIN_NAMESPACE
-
-QFFmpeg::Clock::Clock(ClockController *controller)
- : controller(controller)
-{
- Q_ASSERT(controller);
- controller->addClock(this);
-}
-
-QFFmpeg::Clock::~Clock()
-{
- if (controller)
- controller->removeClock(this);
-}
-
-qint64 QFFmpeg::Clock::currentTime() const
-{
- return controller ? controller->currentTime() : 0.;
-}
-
-void QFFmpeg::Clock::syncTo(qint64 time)
-{
- qCDebug(qLcClock) << "syncTo" << time << isMaster();
-}
-
-void QFFmpeg::Clock::setPlaybackRate(float rate, qint64 currentTime)
-{
- qCDebug(qLcClock) << "Clock::setPlaybackRate" << rate;
- Q_UNUSED(rate)
- Q_UNUSED(currentTime)
-}
-
-void QFFmpeg::Clock::setPaused(bool paused)
-{
- qCDebug(qLcClock) << "Clock::setPaused" << paused;
- Q_UNUSED(paused)
-}
-
-qint64 QFFmpeg::Clock::timeUpdated(qint64 currentTime)
-{
- if (controller)
- return controller->timeUpdated(this, currentTime);
- return currentTime;
-}
-
-qint64 QFFmpeg::Clock::usecsTo(qint64 currentTime, qint64 displayTime)
-{
- if (!controller || controller->m_isPaused)
- return -1;
- int t = qRound64((displayTime - currentTime)/playbackRate());
- return t < 0 ? 0 : t;
-}
-
-QFFmpeg::Clock::Type QFFmpeg::Clock::type() const
-{
- return SystemClock;
-}
-
-QFFmpeg::ClockController::~ClockController()
-{
- QMutexLocker l(&m_mutex);
- for (auto *p : qAsConst(m_clocks))
- p->setController(nullptr);
-}
-
-qint64 QFFmpeg::ClockController::timeUpdated(Clock *clock, qint64 time)
-{
- QMutexLocker l(&m_mutex);
- if (clock != m_master) {
- // If the clock isn't the master clock, simply return the current time
- // so we can make adjustments as needed
- return currentTimeNoLock();
- }
-
- // if the clock is the master, adjust our base timing
- m_baseTime = time;
- m_timer.restart();
-
- // Avoid posting too many updates to the notifyObject, or we can overload
- // the event queue with too many notifications
- if (qAbs(time - m_lastMasterTime) < 5000)
- return time;
- m_lastMasterTime = time;
-// qCDebug(qLcClock) << "ClockController::timeUpdated(master)" << time << "skew" << skew();
- if (notifyObject)
- notify.invoke(notifyObject, Qt::QueuedConnection, Q_ARG(qint64, time));
- return time;
-}
-
-void QFFmpeg::ClockController::addClock(Clock *clock)
-{
- QMutexLocker l(&m_mutex);
- qCDebug(qLcClock) << "addClock" << clock;
- Q_ASSERT(clock != nullptr);
-
- if (m_clocks.contains(clock))
- return;
-
- if (!m_master)
- m_master = clock;
-
- m_clocks.append(clock);
- clock->syncTo(currentTimeNoLock());
- clock->setPaused(m_isPaused);
-
- // update master clock
- if (m_master != clock && clock->type() > m_master->type())
- m_master = clock;
-}
-
-void QFFmpeg::ClockController::removeClock(Clock *clock)
-{
- QMutexLocker l(&m_mutex);
- qCDebug(qLcClock) << "removeClock" << clock;
- m_clocks.removeAll(clock);
- if (m_master == clock) {
- // find a new master clock
- m_master = nullptr;
- for (auto *c : qAsConst(m_clocks)) {
- if (!m_master || m_master->type() < c->type())
- m_master = c;
- }
- }
-}
-
-qint64 QFFmpeg::ClockController::currentTime() const
-{
- QMutexLocker l(&m_mutex);
- return currentTimeNoLock();
-}
-
-void QFFmpeg::ClockController::syncTo(qint64 usecs)
-{
- QMutexLocker l(&m_mutex);
- qCDebug(qLcClock) << "syncTo" << usecs;
- m_baseTime = usecs;
- m_seekTime = usecs;
- m_timer.restart();
- for (auto *p : qAsConst(m_clocks))
- p->syncTo(usecs);
-}
-
-void QFFmpeg::ClockController::setPlaybackRate(float s)
-{
- QMutexLocker l(&m_mutex);
- qCDebug(qLcClock) << "setPlaybackRate" << s;
- m_baseTime = currentTimeNoLock();
- m_timer.restart();
- m_playbackRate = s;
- for (auto *p : qAsConst(m_clocks))
- p->setPlaybackRate(s, m_baseTime);
-}
-
-void QFFmpeg::ClockController::setPaused(bool paused)
-{
- QMutexLocker l(&m_mutex);
- if (m_isPaused == paused)
- return;
- qCDebug(qLcClock) << "setPaused" << paused;
- m_isPaused = paused;
- if (m_isPaused) {
- m_baseTime = currentTimeNoLock();
- m_seekTime = m_baseTime;
- } else {
- m_timer.restart();
- }
- for (auto *p : qAsConst(m_clocks))
- p->setPaused(paused);
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegclock_p.h b/src/plugins/multimedia/ffmpeg/qffmpegclock_p.h
deleted file mode 100644
index 7ca75c20e..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegclock_p.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QFFMPEGCLOCK_P_H
-#define QFFMPEGCLOCK_P_H
-
-#include "qffmpeg_p.h"
-
-#include <qelapsedtimer.h>
-#include <qlist.h>
-#include <qmutex.h>
-#include <qmetaobject.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QFFmpeg {
-
-class ClockController;
-
-// Clock runs in displayTime, ie. if playbackRate is not 1, it runs faster or slower
-// than a regular clock. All methods take displayTime
-// Exception: usecsTo() will return the real time that should pass until we will
-// hit the requested display time
-class Clock
-{
- ClockController *controller = nullptr;
-public:
- enum Type {
- SystemClock,
- AudioClock
- };
- Clock(ClockController *controller);
- virtual ~Clock();
- virtual Type type() const;
-
- float playbackRate() const;
- bool isMaster() const;
-
- // all times in usecs
- qint64 currentTime() const;
- qint64 seekTime() const;
- qint64 usecsTo(qint64 currentTime, qint64 displayTime);
-
-protected:
- virtual void syncTo(qint64 usecs);
- virtual void setPlaybackRate(float rate, qint64 currentTime);
- virtual void setPaused(bool paused);
-
- qint64 timeUpdated(qint64 currentTime);
-
-private:
- friend class ClockController;
- void setController(ClockController *c)
- {
- controller = c;
- }
-};
-
-class ClockController
-{
- mutable QMutex m_mutex;
- QList<Clock *> m_clocks;
- Clock *m_master = nullptr;
-
- QElapsedTimer m_timer;
- qint64 m_baseTime = 0;
- qint64 m_seekTime = 0;
- float m_playbackRate = 1.;
- bool m_isPaused = true;
-
- qint64 m_lastMasterTime = 0;
- QObject *notifyObject = nullptr;
- QMetaMethod notify;
- qint64 currentTimeNoLock() const { return m_isPaused ? m_baseTime : m_baseTime + m_timer.elapsed()/m_playbackRate; }
-
- friend class Clock;
- qint64 timeUpdated(Clock *clock, qint64 time);
- void addClock(Clock *provider);
- void removeClock(Clock *provider);
-public:
- // max 25 msecs tolerance for the clock
- enum { ClockTolerance = 25000 };
- ClockController() = default;
- ~ClockController();
-
-
- qint64 currentTime() const;
-
- void syncTo(qint64 usecs);
-
- void setPlaybackRate(float s);
- float playbackRate() const { return m_playbackRate; }
- void setPaused(bool paused);
-
- void setNotify(QObject *object, QMetaMethod method)
- {
- notifyObject = object;
- notify = method;
- }
-};
-
-inline float Clock::playbackRate() const
-{
- return controller ? controller->m_playbackRate : 1.;
-}
-
-inline bool Clock::isMaster() const
-{
- return controller ? controller->m_master == this : false;
-}
-
-inline qint64 Clock::seekTime() const
-{
- return controller ? controller->m_seekTime : 0;
-}
-
-
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegconverter.cpp b/src/plugins/multimedia/ffmpeg/qffmpegconverter.cpp
new file mode 100644
index 000000000..c3d30c1df
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegconverter.cpp
@@ -0,0 +1,272 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegconverter_p.h"
+#include <QtMultimedia/qvideoframeformat.h>
+#include <QtMultimedia/qvideoframe.h>
+#include <QtCore/qloggingcategory.h>
+#include <private/qvideotexturehelper_p.h>
+
+extern "C" {
+#include <libswscale/swscale.h>
+}
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+Q_LOGGING_CATEGORY(lc, "qt.multimedia.ffmpeg.converter");
+
+
+// Converts to FFmpeg pixel format. This function differs from
+// QFFmpegVideoBuffer::toAVPixelFormat which only covers the subset
+// of pixel formats required for encoding. Here we need to cover more
+// pixel formats to be able to generate test images for decoding/display
+AVPixelFormat toAVPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat)
+{
+ switch (pixelFormat) {
+ default:
+ case QVideoFrameFormat::Format_Invalid:
+ return AV_PIX_FMT_NONE;
+ case QVideoFrameFormat::Format_AYUV:
+ case QVideoFrameFormat::Format_AYUV_Premultiplied:
+ return AV_PIX_FMT_NONE; // TODO: Fixme (No corresponding FFmpeg format available)
+ case QVideoFrameFormat::Format_YV12:
+ case QVideoFrameFormat::Format_IMC1:
+ case QVideoFrameFormat::Format_IMC3:
+ case QVideoFrameFormat::Format_IMC2:
+ case QVideoFrameFormat::Format_IMC4:
+ return AV_PIX_FMT_YUV420P;
+ case QVideoFrameFormat::Format_Jpeg:
+ return AV_PIX_FMT_BGRA;
+ case QVideoFrameFormat::Format_ARGB8888:
+ return AV_PIX_FMT_ARGB;
+ case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
+ case QVideoFrameFormat::Format_XRGB8888:
+ return AV_PIX_FMT_0RGB;
+ case QVideoFrameFormat::Format_BGRA8888:
+ return AV_PIX_FMT_BGRA;
+ case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
+ case QVideoFrameFormat::Format_BGRX8888:
+ return AV_PIX_FMT_BGR0;
+ case QVideoFrameFormat::Format_ABGR8888:
+ return AV_PIX_FMT_ABGR;
+ case QVideoFrameFormat::Format_XBGR8888:
+ return AV_PIX_FMT_0BGR;
+ case QVideoFrameFormat::Format_RGBA8888:
+ return AV_PIX_FMT_RGBA;
+ case QVideoFrameFormat::Format_RGBX8888:
+ return AV_PIX_FMT_RGB0;
+ case QVideoFrameFormat::Format_YUV422P:
+ return AV_PIX_FMT_YUV422P;
+ case QVideoFrameFormat::Format_YUV420P:
+ return AV_PIX_FMT_YUV420P;
+ case QVideoFrameFormat::Format_YUV420P10:
+ return AV_PIX_FMT_YUV420P10;
+ case QVideoFrameFormat::Format_UYVY:
+ return AV_PIX_FMT_UYVY422;
+ case QVideoFrameFormat::Format_YUYV:
+ return AV_PIX_FMT_YUYV422;
+ case QVideoFrameFormat::Format_NV12:
+ return AV_PIX_FMT_NV12;
+ case QVideoFrameFormat::Format_NV21:
+ return AV_PIX_FMT_NV21;
+ case QVideoFrameFormat::Format_Y8:
+ return AV_PIX_FMT_GRAY8;
+ case QVideoFrameFormat::Format_Y16:
+ return AV_PIX_FMT_GRAY16;
+ case QVideoFrameFormat::Format_P010:
+ return AV_PIX_FMT_P010;
+ case QVideoFrameFormat::Format_P016:
+ return AV_PIX_FMT_P016;
+ case QVideoFrameFormat::Format_SamplerExternalOES:
+ return AV_PIX_FMT_MEDIACODEC;
+ }
+}
+
+struct SwsFrameData
+{
+ static constexpr int arraySize = 4; // Array size required by sws_scale
+ std::array<uchar *, arraySize> bits;
+ std::array<int, arraySize> stride;
+};
+
+SwsFrameData getSwsData(QVideoFrame &dst)
+{
+ switch (dst.pixelFormat()) {
+ case QVideoFrameFormat::Format_YV12:
+ case QVideoFrameFormat::Format_IMC1:
+ return { { dst.bits(0), dst.bits(2), dst.bits(1), nullptr },
+ { dst.bytesPerLine(0), dst.bytesPerLine(2), dst.bytesPerLine(1), 0 } };
+
+ case QVideoFrameFormat::Format_IMC2:
+ return { { dst.bits(0), dst.bits(1) + dst.bytesPerLine(1) / 2, dst.bits(1), nullptr },
+ { dst.bytesPerLine(0), dst.bytesPerLine(1), dst.bytesPerLine(1), 0 } };
+
+ case QVideoFrameFormat::Format_IMC4:
+ return { { dst.bits(0), dst.bits(1), dst.bits(1) + dst.bytesPerLine(1) / 2, nullptr },
+ { dst.bytesPerLine(0), dst.bytesPerLine(1), dst.bytesPerLine(1), 0 } };
+ default:
+ return { { dst.bits(0), dst.bits(1), dst.bits(2), nullptr },
+ { dst.bytesPerLine(0), dst.bytesPerLine(1), dst.bytesPerLine(2), 0 } };
+ }
+}
+
+struct SwsColorSpace
+{
+ int colorSpace;
+ int colorRange; // 0 - mpeg/video, 1 - jpeg/full
+};
+
+// Qt heuristics for determining color space requires checking
+// both frame color space and range. This function mimics logic
+// used elsewhere in Qt Multimedia.
+SwsColorSpace toSwsColorSpace(QVideoFrameFormat::ColorRange colorRange,
+ QVideoFrameFormat::ColorSpace colorSpace)
+{
+ const int avRange = colorRange == QVideoFrameFormat::ColorRange_Video ? 0 : 1;
+
+ switch (colorSpace) {
+ case QVideoFrameFormat::ColorSpace_BT601:
+ if (colorRange == QVideoFrameFormat::ColorRange_Full)
+ return { SWS_CS_ITU709, 1 }; // TODO: FIXME - Not exact match
+ return { SWS_CS_ITU601, 0 };
+ case QVideoFrameFormat::ColorSpace_BT709:
+ return { SWS_CS_ITU709, avRange };
+ case QVideoFrameFormat::ColorSpace_AdobeRgb:
+ return { SWS_CS_ITU601, 1 }; // TODO: Why do ITU601 and Adobe RGB match well?
+ case QVideoFrameFormat::ColorSpace_BT2020:
+ return { SWS_CS_BT2020, avRange };
+ case QVideoFrameFormat::ColorSpace_Undefined:
+ default:
+ return { SWS_CS_DEFAULT, avRange };
+ }
+}
+
+using SwsContextUPtr = std::unique_ptr<SwsContext, decltype(&sws_freeContext)>;
+using PixelFormat = QVideoFrameFormat::PixelFormat;
+
+// clang-format off
+
+SwsContextUPtr createConverter(const QSize &srcSize, PixelFormat srcPixFmt,
+ const QSize &dstSize, PixelFormat dstPixFmt)
+{
+ SwsContext* context = sws_getContext(
+ srcSize.width(), srcSize.height(), toAVPixelFormat(srcPixFmt),
+ dstSize.width(), dstSize.height(), toAVPixelFormat(dstPixFmt),
+ SWS_BILINEAR, nullptr, nullptr, nullptr);
+
+ return { context, &sws_freeContext };
+}
+
+bool setColorSpaceDetails(SwsContext *context,
+ const QVideoFrameFormat &srcFormat,
+ const QVideoFrameFormat &dstFormat)
+{
+ const SwsColorSpace src = toSwsColorSpace(srcFormat.colorRange(), srcFormat.colorSpace());
+ const SwsColorSpace dst = toSwsColorSpace(dstFormat.colorRange(), dstFormat.colorSpace());
+
+ constexpr int brightness = 0;
+ constexpr int contrast = 0;
+ constexpr int saturation = 0;
+ const int status = sws_setColorspaceDetails(context,
+ sws_getCoefficients(src.colorSpace), src.colorRange,
+ sws_getCoefficients(dst.colorSpace), dst.colorRange,
+ brightness, contrast, saturation);
+
+ return status == 0;
+}
+
+bool convert(SwsContext *context, QVideoFrame &src, int srcHeight, QVideoFrame &dst)
+{
+ if (!src.map(QVideoFrame::ReadOnly))
+ return false;
+
+ QScopeGuard unmapSrc{[&] {
+ src.unmap();
+ }};
+
+ if (!dst.map(QVideoFrame::WriteOnly))
+ return false;
+
+ QScopeGuard unmapDst{[&] {
+ dst.unmap();
+ }};
+
+ const SwsFrameData srcData = getSwsData(src);
+ const SwsFrameData dstData = getSwsData(dst);
+
+ constexpr int firstSrcSliceRow = 0;
+ const int scaledHeight = sws_scale(context,
+ srcData.bits.data(), srcData.stride.data(),
+ firstSrcSliceRow, srcHeight,
+ dstData.bits.data(), dstData.stride.data());
+
+ if (scaledHeight != srcHeight)
+ return false;
+
+ return true;
+}
+
+// Ensure even size if using planar format with chroma subsampling
+QSize adjustSize(const QSize& size, PixelFormat srcFmt, PixelFormat dstFmt)
+{
+ const auto* srcDesc = QVideoTextureHelper::textureDescription(srcFmt);
+ const auto* dstDesc = QVideoTextureHelper::textureDescription(dstFmt);
+
+ QSize output = size;
+ for (const auto desc : { srcDesc, dstDesc }) {
+ for (int i = 0; i < desc->nplanes; ++i) {
+ // TODO: Assumes that max subsampling is 2
+ if (desc->sizeScale[i].x != 1)
+ output.setWidth(output.width() & ~1); // Make even
+
+ if (desc->sizeScale[i].y != 1)
+ output.setHeight(output.height() & ~1); // Make even
+ }
+ }
+
+ return output;
+}
+
+} // namespace
+
+// Converts a video frame to the dstFormat video frame format.
+QVideoFrame convertFrame(QVideoFrame &src, const QVideoFrameFormat &dstFormat)
+{
+ if (src.size() != dstFormat.frameSize()) {
+ qCCritical(lc) << "Resizing is not supported";
+ return {};
+ }
+
+ // Adjust size to even width/height if we have chroma subsampling
+ const QSize size = adjustSize(src.size(), src.pixelFormat(), dstFormat.pixelFormat());
+ if (size != src.size())
+ qCWarning(lc) << "Input truncated to even width/height";
+
+ const SwsContextUPtr conv = createConverter(
+ size, src.pixelFormat(), size, dstFormat.pixelFormat());
+
+ if (!conv) {
+ qCCritical(lc) << "Failed to create SW converter";
+ return {};
+ }
+
+ if (!setColorSpaceDetails(conv.get(), src.surfaceFormat(), dstFormat)) {
+ qCCritical(lc) << "Failed to set color space details";
+ return {};
+ }
+
+ QVideoFrame dst{ dstFormat };
+
+ if (!convert(conv.get(), src, size.height(), dst)) {
+ qCCritical(lc) << "Frame conversion failed";
+ return {};
+ }
+
+ return dst;
+}
+
+// clang-format on
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegconverter_p.h b/src/plugins/multimedia/ffmpeg/qffmpegconverter_p.h
new file mode 100644
index 000000000..57ee3135f
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegconverter_p.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGCONVERTER_P_H
+#define QFFMPEGCONVERTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qtconfigmacros.h>
+#include <private/qtmultimediaglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoFrameFormat;
+class QVideoFrame;
+
+QVideoFrame convertFrame(QVideoFrame &src, const QVideoFrameFormat &dstFormat);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp
deleted file mode 100644
index 31b334fbc..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegdecoder.cpp
+++ /dev/null
@@ -1,1362 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qffmpegdecoder_p.h"
-#include "qffmpegmediaformatinfo_p.h"
-#include "qffmpeg_p.h"
-#include "qffmpegmediametadata_p.h"
-#include "qffmpegvideobuffer_p.h"
-#include "private/qplatformaudiooutput_p.h"
-#include "qffmpeghwaccel_p.h"
-#include "qffmpegvideosink_p.h"
-#include "qvideosink.h"
-#include "qaudiosink.h"
-#include "qaudiooutput.h"
-#include "qffmpegaudiodecoder_p.h"
-#include "qffmpegresampler_p.h"
-
-#include <qlocale.h>
-#include <qtimer.h>
-
-#include <qloggingcategory.h>
-
-extern "C" {
-#include <libavutil/hwcontext.h>
-}
-
-QT_BEGIN_NAMESPACE
-
-using namespace QFFmpeg;
-
-Q_LOGGING_CATEGORY(qLcDemuxer, "qt.multimedia.ffmpeg.demuxer")
-Q_LOGGING_CATEGORY(qLcDecoder, "qt.multimedia.ffmpeg.decoder")
-Q_LOGGING_CATEGORY(qLcVideoRenderer, "qt.multimedia.ffmpeg.videoRenderer")
-Q_LOGGING_CATEGORY(qLcAudioRenderer, "qt.multimedia.ffmpeg.audioRenderer")
-
-Codec::Data::Data(AVCodecContext *context, AVStream *stream, const HWAccel &hwAccel)
- : context(context)
- , stream(stream)
- , hwAccel(hwAccel)
-{
-}
-
-Codec::Data::~Data()
-{
- if (!context)
- return;
- avcodec_close(context);
- avcodec_free_context(&context);
-}
-
-Codec::Codec(AVFormatContext *format, int streamIndex)
-{
- qCDebug(qLcDecoder) << "Codec::Codec" << streamIndex;
- Q_ASSERT(streamIndex >= 0 && streamIndex < (int)format->nb_streams);
-
- AVStream *stream = format->streams[streamIndex];
- const AVCodec *decoder = avcodec_find_decoder(stream->codecpar->codec_id);
- if (!decoder) {
- qCDebug(qLcDecoder) << "Failed to find a valid FFmpeg decoder";
- return;
- }
-
- QFFmpeg::HWAccel hwAccel;
- if (decoder->type == AVMEDIA_TYPE_VIDEO) {
- hwAccel = QFFmpeg::HWAccel(decoder);
- }
-
- auto *context = avcodec_alloc_context3(decoder);
- if (!context) {
- qCDebug(qLcDecoder) << "Failed to allocate a FFmpeg codec context";
- return;
- }
-
- int ret = avcodec_parameters_to_context(context, stream->codecpar);
- if (ret < 0) {
- qCDebug(qLcDecoder) << "Failed to set FFmpeg codec parameters";
- return;
- }
-
- auto *buf = hwAccel.hwDeviceContextAsBuffer();
- if (buf)
- context->hw_device_ctx = av_buffer_ref(buf);
- // ### This still gives errors about wrong HW formats (as we accept all of them)
- // But it would be good to get so we can filter out pixel format we don't support natively
- context->get_format = QFFmpeg::getFormat;
-
- /* Init the decoder, with reference counting and threading */
- AVDictionary *opts = nullptr;
- av_dict_set(&opts, "refcounted_frames", "1", 0);
- av_dict_set(&opts, "threads", "auto", 0);
- ret = avcodec_open2(context, decoder, &opts);
- if (ret < 0) {
- qCDebug(qLcDecoder) << "Failed to open FFmpeg codec context.";
- avcodec_free_context(&context);
- return;
- }
-
- d = new Data(context, stream, hwAccel);
-}
-
-
-Demuxer::Demuxer(Decoder *decoder, AVFormatContext *context)
- : Thread()
- , decoder(decoder)
- , context(context)
-{
- QString objectName = QLatin1String("Demuxer");
- setObjectName(objectName);
-
- streamDecoders.resize(context->nb_streams);
-}
-
-Demuxer::~Demuxer()
-{
- if (context) {
- if (context->pb) {
- av_free(context->pb);
- context->pb = nullptr;
- }
- avformat_free_context(context);
- }
-}
-
-StreamDecoder *Demuxer::addStream(int streamIndex)
-{
- if (streamIndex < 0)
- return nullptr;
- QMutexLocker locker(&mutex);
- Codec codec(context, streamIndex);
- if (!codec.isValid()) {
- decoder->error(QMediaPlayer::FormatError, "Invalid media file");
- return nullptr;
- }
-
- Q_ASSERT(codec.context()->codec_type == AVMEDIA_TYPE_AUDIO ||
- codec.context()->codec_type == AVMEDIA_TYPE_VIDEO ||
- codec.context()->codec_type == AVMEDIA_TYPE_SUBTITLE);
- auto *stream = new StreamDecoder(this, codec);
- Q_ASSERT(!streamDecoders.at(streamIndex));
- streamDecoders[streamIndex] = stream;
- stream->start();
- updateEnabledStreams();
- return stream;
-}
-
-void Demuxer::removeStream(int streamIndex)
-{
- if (streamIndex < 0)
- return;
- QMutexLocker locker(&mutex);
- Q_ASSERT(streamIndex < (int)context->nb_streams);
- Q_ASSERT(streamDecoders.at(streamIndex) != nullptr);
- streamDecoders[streamIndex] = nullptr;
- updateEnabledStreams();
-}
-
-void Demuxer::stopDecoding()
-{
- qCDebug(qLcDemuxer) << "StopDecoding";
- QMutexLocker locker(&mutex);
- sendFinalPacketToStreams();
-}
-int Demuxer::seek(qint64 pos)
-{
- QMutexLocker locker(&mutex);
- for (StreamDecoder *d : qAsConst(streamDecoders)) {
- if (d)
- d->mutex.lock();
- }
- for (StreamDecoder *d : qAsConst(streamDecoders)) {
- if (d)
- d->flush();
- }
- for (StreamDecoder *d : qAsConst(streamDecoders)) {
- if (d)
- d->mutex.unlock();
- }
- qint64 seekPos = pos*AV_TIME_BASE/1000000; // usecs to AV_TIME_BASE
- av_seek_frame(context, -1, seekPos, AVSEEK_FLAG_BACKWARD);
- last_pts = -1;
- loop();
- qCDebug(qLcDemuxer) << "Demuxer::seek" << pos << last_pts;
- return last_pts;
-}
-
-void Demuxer::updateEnabledStreams()
-{
- if (isStopped())
- return;
- for (uint i = 0; i < context->nb_streams; ++i) {
- AVDiscard discard = AVDISCARD_DEFAULT;
- if (!streamDecoders.at(i))
- discard = AVDISCARD_ALL;
- context->streams[i]->discard = discard;
- }
-}
-
-void Demuxer::sendFinalPacketToStreams()
-{
- if (m_isStopped.loadAcquire())
- return;
- for (auto *streamDecoder : qAsConst(streamDecoders)) {
- qCDebug(qLcDemuxer) << "Demuxer: sending last packet to stream" << streamDecoder;
- if (!streamDecoder)
- continue;
- streamDecoder->addPacket(nullptr);
- }
- m_isStopped.storeRelease(true);
-}
-
-void Demuxer::init()
-{
- qCDebug(qLcDemuxer) << "Demuxer started";
-}
-
-void Demuxer::cleanup()
-{
- qCDebug(qLcDemuxer) << "Demuxer::cleanup";
-#ifndef QT_NO_DEBUG
- for (auto *streamDecoder : qAsConst(streamDecoders)) {
- Q_ASSERT(!streamDecoder);
- }
-#endif
- avformat_close_input(&context);
- Thread::cleanup();
-}
-
-bool Demuxer::shouldWait() const
-{
- if (m_isStopped)
- return true;
-// qCDebug(qLcDemuxer) << "XXXX Demuxer::shouldWait" << this << data->seek_pos.loadRelaxed();
- // require a minimum of 200ms of data
- qint64 queueSize = 0;
- bool buffersFull = true;
- for (auto *d : streamDecoders) {
- if (!d)
- continue;
- if (d->queuedDuration() < 200)
- buffersFull = false;
- queueSize += d->queuedPacketSize();
- }
-// qCDebug(qLcDemuxer) << " queue size" << queueSize << MaxQueueSize;
- if (queueSize > MaxQueueSize)
- return true;
-// qCDebug(qLcDemuxer) << " waiting!";
- return buffersFull;
-
-}
-
-void Demuxer::loop()
-{
- AVPacket *packet = av_packet_alloc();
- if (av_read_frame(context, packet) < 0) {
- sendFinalPacketToStreams();
- av_packet_free(&packet);
- return;
- }
-
- if (last_pts < 0 && packet->pts != AV_NOPTS_VALUE) {
- auto *stream = context->streams[packet->stream_index];
- last_pts = timeStamp(packet->pts, stream->time_base);
- }
-
- auto *streamDecoder = streamDecoders.at(packet->stream_index);
- if (!streamDecoder) {
- av_packet_free(&packet);
- return;
- }
- streamDecoder->addPacket(packet);
-}
-
-
-StreamDecoder::StreamDecoder(Demuxer *demuxer, const Codec &codec)
- : Thread()
- , demuxer(demuxer)
- , codec(codec)
-{
- Q_ASSERT(codec.context()->codec_type == AVMEDIA_TYPE_AUDIO ||
- codec.context()->codec_type == AVMEDIA_TYPE_VIDEO ||
- codec.context()->codec_type == AVMEDIA_TYPE_SUBTITLE);
-
- QString objectName;
- switch (codec.context()->codec_type) {
- case AVMEDIA_TYPE_AUDIO:
- objectName = QLatin1String("AudioDecoderThread");
- // Queue size: 3 frames for video/subtitle, 9 for audio
- frameQueue.maxSize = 9;
- break;
- case AVMEDIA_TYPE_VIDEO:
- objectName = QLatin1String("VideoDecoderThread");
- break;
- case AVMEDIA_TYPE_SUBTITLE:
- objectName = QLatin1String("SubtitleDecoderThread");
- break;
- default:
- Q_UNREACHABLE();
- }
- setObjectName(objectName);
-}
-
-void StreamDecoder::addPacket(AVPacket *packet)
-{
- {
- QMutexLocker locker(&packetQueue.mutex);
-// qCDebug(qLcDecoder) << "enqueuing packet of type" << type()
-// << "size" << packet->size
-// << "stream index" << packet->stream_index
-// << "pts" << codec.toMs(packet->pts)
-// << "duration" << codec.toMs(packet->duration);
- packetQueue.queue.enqueue(Packet(packet));
- if (packet) {
- packetQueue.size += packet->size;
- packetQueue.duration += codec.toMs(packet->duration);
- }
- eos.storeRelease(false);
- }
- wake();
-}
-
-void StreamDecoder::flush()
-{
- qCDebug(qLcDecoder) << ">>>> flushing stream decoder" << type();
- avcodec_flush_buffers(codec.context());
- {
- QMutexLocker locker(&packetQueue.mutex);
- packetQueue.queue.clear();
- packetQueue.size = 0;
- packetQueue.duration = 0;
- }
- {
- QMutexLocker locker(&frameQueue.mutex);
- frameQueue.queue.clear();
- }
- qCDebug(qLcDecoder) << ">>>> done flushing stream decoder" << type();
-}
-
-void StreamDecoder::setRenderer(Renderer *r)
-{
- QMutexLocker locker(&mutex);
- m_renderer = r;
- if (m_renderer)
- m_renderer->wake();
-}
-
-void StreamDecoder::killHelper()
-{
- m_renderer = nullptr;
- demuxer->removeStream(codec.streamIndex());
-}
-
-Packet StreamDecoder::peekPacket()
-{
- QMutexLocker locker(&packetQueue.mutex);
- if (packetQueue.queue.isEmpty()) {
- if (demuxer)
- demuxer->wake();
- return {};
- }
- auto packet = packetQueue.queue.first();
-
- if (demuxer)
- demuxer->wake();
- return packet;
-}
-
-Packet StreamDecoder::takePacket()
-{
- QMutexLocker locker(&packetQueue.mutex);
- if (packetQueue.queue.isEmpty()) {
- if (demuxer)
- demuxer->wake();
- return {};
- }
- auto packet = packetQueue.queue.dequeue();
- if (packet.avPacket()) {
- packetQueue.size -= packet.avPacket()->size;
- packetQueue.duration -= codec.toMs(packet.avPacket()->duration);
- }
-// qCDebug(qLcDecoder) << "<<<< dequeuing packet of type" << type()
-// << "size" << packet.avPacket()->size
-// << "stream index" << packet.avPacket()->stream_index
-// << "pts" << codec.toMs(packet.avPacket()->pts)
-// << "duration" << codec.toMs(packet.avPacket()->duration)
-// << "ts" << decoder->clockController.currentTime();
- if (demuxer)
- demuxer->wake();
- return packet;
-}
-
-void StreamDecoder::addFrame(const Frame &f)
-{
- Q_ASSERT(f.isValid());
- QMutexLocker locker(&frameQueue.mutex);
- frameQueue.queue.append(std::move(f));
- if (m_renderer)
- m_renderer->wake();
-}
-
-Frame StreamDecoder::takeFrame()
-{
- QMutexLocker locker(&frameQueue.mutex);
- // wake up the decoder so it delivers more frames
- if (frameQueue.queue.isEmpty()) {
- wake();
- return {};
- }
- auto f = frameQueue.queue.dequeue();
- wake();
- return f;
-}
-
-void StreamDecoder::init()
-{
- qCDebug(qLcDecoder) << "Starting decoder";
-}
-
-bool StreamDecoder::shouldWait() const
-{
- if (eos.loadAcquire() || (hasNoPackets() && decoderHasNoFrames) || hasEnoughFrames())
- return true;
- return false;
-}
-
-void StreamDecoder::loop()
-{
- if (codec.context()->codec->type == AVMEDIA_TYPE_SUBTITLE)
- decodeSubtitle();
- else
- decode();
-}
-
-void StreamDecoder::decode()
-{
- Q_ASSERT(codec.context());
-
- AVFrame *frame = av_frame_alloc();
-// if (type() == 0)
-// qDebug() << "receiving frame";
- int res = avcodec_receive_frame(codec.context(), frame);
-
- if (res >= 0) {
- qint64 pts;
- if (frame->pts != AV_NOPTS_VALUE)
- pts = codec.toUs(frame->pts);
- else
- pts = codec.toUs(frame->best_effort_timestamp);
- addFrame(Frame{frame, codec, pts});
- } else if (res == AVERROR(EOF) || res == AVERROR_EOF) {
- eos.storeRelease(true);
- av_frame_free(&frame);
- timeOut = -1;
- return;
- } else if (res != AVERROR(EAGAIN)) {
- char buf[512];
- av_make_error_string(buf, 512, res);
- qWarning() << "error in decoder" << res << buf;
- av_frame_free(&frame);
- return;
- } else {
- // EAGAIN
- decoderHasNoFrames = true;
- av_frame_free(&frame);
- }
-
- Packet packet = peekPacket();
- if (!packet.isValid()) {
- timeOut = -1;
- return;
- }
-
- res = avcodec_send_packet(codec.context(), packet.avPacket());
- if (res != AVERROR(EAGAIN)) {
- takePacket();
- }
- decoderHasNoFrames = false;
-}
-
-void StreamDecoder::decodeSubtitle()
-{
- // qCDebug(qLcDecoder) << " decoding subtitle" << "has delay:" << (codec->codec->capabilities & AV_CODEC_CAP_DELAY);
- AVSubtitle subtitle;
- memset(&subtitle, 0, sizeof(subtitle));
- int gotSubtitle = 0;
- Packet packet = takePacket();
- if (!packet.isValid())
- return;
-
- int res = avcodec_decode_subtitle2(codec.context(), &subtitle, &gotSubtitle, packet.avPacket());
- // qCDebug(qLcDecoder) << " subtitle got:" << res << gotSubtitle << subtitle.format << Qt::hex << (quint64)subtitle.pts;
- if (res >= 0 && gotSubtitle) {
- // apparently the timestamps in the AVSubtitle structure are not always filled in
- // if they are missing, use the packets pts and duration values instead
- qint64 start, end;
- if (subtitle.pts == AV_NOPTS_VALUE) {
- start = codec.toUs(packet.avPacket()->pts);
- end = start + codec.toUs(packet.avPacket()->duration);
- } else {
- qint64 pts = timeStampUs(subtitle.pts, AVRational{1, AV_TIME_BASE});
- start = pts + qint64(subtitle.start_display_time)*1000;
- end = pts + qint64(subtitle.end_display_time)*1000;
- }
- // qCDebug(qLcDecoder) << " got subtitle (" << start << "--" << end << "):";
- QString text;
- for (uint i = 0; i < subtitle.num_rects; ++i) {
- const auto *r = subtitle.rects[i];
- // qCDebug(qLcDecoder) << " subtitletext:" << r->text << "/" << r->ass;
- if (i)
- text += QLatin1Char('\n');
- if (r->text)
- text += QString::fromUtf8(r->text);
- else {
- const char *ass = r->ass;
- int nCommas = 0;
- while (*ass) {
- if (nCommas == 9)
- break;
- if (*ass == ',')
- ++nCommas;
- ++ass;
- }
- text += QString::fromUtf8(ass);
- }
- }
- text.replace(QLatin1String("\\N"), QLatin1String("\n"));
- text.replace(QLatin1String("\\n"), QLatin1String("\n"));
- text.replace(QLatin1String("\r\n"), QLatin1String("\n"));
- if (text.endsWith(QLatin1Char('\n')))
- text.chop(1);
-
-// qCDebug(qLcDecoder) << " >>> subtitle adding" << text << start << end;
- Frame sub{text, start, end - start};
- addFrame(sub);
- }
-}
-
-QPlatformMediaPlayer::TrackType StreamDecoder::type() const
-{
- switch (codec.stream()->codecpar->codec_type) {
- case AVMEDIA_TYPE_AUDIO:
- return QPlatformMediaPlayer::AudioStream;
- case AVMEDIA_TYPE_VIDEO:
- return QPlatformMediaPlayer::VideoStream;
- case AVMEDIA_TYPE_SUBTITLE:
- return QPlatformMediaPlayer::SubtitleStream;
- default:
- return QPlatformMediaPlayer::NTrackTypes;
- }
-}
-
-Renderer::Renderer(QPlatformMediaPlayer::TrackType type)
- : Thread()
- , type(type)
-{
- QString objectName;
- if (type == QPlatformMediaPlayer::AudioStream)
- objectName = QLatin1String("AudioRenderThread");
- else
- objectName = QLatin1String("VideoRenderThread");
- setObjectName(objectName);
-}
-
-void Renderer::setStream(StreamDecoder *stream)
-{
- QMutexLocker locker(&mutex);
- if (streamDecoder == stream)
- return;
- if (streamDecoder)
- streamDecoder->kill();
- streamDecoder = stream;
- if (streamDecoder)
- streamDecoder->setRenderer(this);
- streamChanged();
- wake();
-}
-
-void Renderer::killHelper()
-{
- if (streamDecoder)
- streamDecoder->kill();
- streamDecoder = nullptr;
-}
-
-bool Renderer::shouldWait() const
-{
- if (!streamDecoder)
- return true;
- if (!paused)
- return false;
- if (step)
- return false;
- return true;
-}
-
-
-void ClockedRenderer::setPaused(bool paused)
-{
- Clock::setPaused(paused);
- Renderer::setPaused(paused);
-}
-
-VideoRenderer::VideoRenderer(Decoder *decoder, QVideoSink *sink)
- : ClockedRenderer(decoder, QPlatformMediaPlayer::VideoStream)
- , sink(sink)
-{}
-
-void VideoRenderer::killHelper()
-{
- if (subtitleStreamDecoder)
- subtitleStreamDecoder->kill();
- subtitleStreamDecoder = nullptr;
- if (streamDecoder)
- streamDecoder->kill();
- streamDecoder = nullptr;
-}
-
-void VideoRenderer::setSubtitleStream(StreamDecoder *stream)
-{
- QMutexLocker locker(&mutex);
- qCDebug(qLcVideoRenderer) << "setting subtitle stream to" << stream;
- if (stream == subtitleStreamDecoder)
- return;
- if (subtitleStreamDecoder)
- subtitleStreamDecoder->kill();
- subtitleStreamDecoder = stream;
- if (subtitleStreamDecoder)
- subtitleStreamDecoder->setRenderer(this);
- sink->setSubtitleText({});
- wake();
-}
-
-void VideoRenderer::init()
-{
- qCDebug(qLcVideoRenderer) << "starting video renderer";
- ClockedRenderer::init();
-}
-
-void VideoRenderer::loop()
-{
- if (!streamDecoder) {
- timeOut = -1; // Avoid 100% CPU load before play()
- return;
- }
-
- Frame frame = streamDecoder->takeFrame();
- if (!frame.isValid()) {
- if (streamDecoder->isAtEnd()) {
- timeOut = -1;
- eos.storeRelease(true);
- emit atEnd();
- return;
- }
- timeOut = 1;
-// qDebug() << "no valid frame" << timer.elapsed();
- return;
- }
- eos.storeRelease(false);
-// qCDebug(qLcVideoRenderer) << "received video frame" << frame.pts();
- if (frame.pts() < seekTime()) {
- qCDebug(qLcVideoRenderer) << " discarding" << frame.pts() << seekTime();
- return;
- }
-
- AVStream *stream = frame.codec()->stream();
- qint64 startTime = frame.pts();
- qint64 duration = (1000000*stream->avg_frame_rate.den + (stream->avg_frame_rate.num>>1))
- /stream->avg_frame_rate.num;
-
- if (sink) {
- qint64 startTime = frame.pts();
-// qDebug() << "RHI:" << accel.isNull() << accel.rhi() << sink->rhi();
- QFFmpegVideoBuffer *buffer = new QFFmpegVideoBuffer(frame.takeAVFrame());
- QVideoFrameFormat format(buffer->size(), buffer->pixelFormat());
- format.setColorSpace(buffer->colorSpace());
- format.setColorTransfer(buffer->colorTransfer());
- format.setColorRange(buffer->colorRange());
- format.setMaxLuminance(buffer->maxNits());
- QVideoFrame videoFrame(buffer, format);
- videoFrame.setStartTime(startTime);
- videoFrame.setEndTime(startTime + duration);
-// qDebug() << "Creating video frame" << startTime << (startTime + duration) << subtitleStreamDecoder;
-
- // add in subtitles
- const Frame *currentSubtitle = nullptr;
- if (subtitleStreamDecoder)
- currentSubtitle = subtitleStreamDecoder->lockAndPeekFrame();
-
- if (currentSubtitle && currentSubtitle->isValid()) {
-// qDebug() << "frame: subtitle" << currentSubtitle->text() << currentSubtitle->pts() << currentSubtitle->duration();
- qCDebug(qLcVideoRenderer) << " " << currentSubtitle->pts() << currentSubtitle->duration() << currentSubtitle->text();
- if (currentSubtitle->pts() <= startTime && currentSubtitle->end() > startTime) {
-// qCDebug(qLcVideoRenderer) << " setting text";
- sink->setSubtitleText(currentSubtitle->text());
- }
- if (currentSubtitle->end() < startTime) {
-// qCDebug(qLcVideoRenderer) << " removing subtitle item";
- sink->setSubtitleText({});
- subtitleStreamDecoder->removePeekedFrame();
- }
- } else {
- sink->setSubtitleText({});
- }
- if (subtitleStreamDecoder)
- subtitleStreamDecoder->unlockAndReleaseFrame();
-
-// qCDebug(qLcVideoRenderer) << " sending a video frame" << startTime << duration << decoder->baseTimer.elapsed();
- sink->setVideoFrame(videoFrame);
- doneStep();
- }
- const Frame *nextFrame = streamDecoder->lockAndPeekFrame();
- qint64 nextFrameTime = 0;
- if (nextFrame)
- nextFrameTime = nextFrame->pts();
- else
- nextFrameTime = startTime + duration;
- streamDecoder->unlockAndReleaseFrame();
- qint64 mtime = timeUpdated(startTime);
- timeOut = usecsTo(mtime, nextFrameTime)/1000;
-// qDebug() << " next video frame in" << startTime << nextFrameTime << currentTime() << timeOut;
-}
-
-AudioRenderer::AudioRenderer(Decoder *decoder, QAudioOutput *output)
- : ClockedRenderer(decoder, QPlatformMediaPlayer::AudioStream)
- , output(output)
-{
- connect(output, &QAudioOutput::deviceChanged, this, &AudioRenderer::updateAudio);
-}
-
-void AudioRenderer::syncTo(qint64 usecs)
-{
- Clock::syncTo(usecs);
- audioBaseTime = usecs;
- processedBase = processedUSecs;
-}
-
-void AudioRenderer::setPlaybackRate(float rate, qint64 currentTime)
-{
- audioBaseTime = currentTime;
- processedBase = processedUSecs;
- Clock::setPlaybackRate(rate, currentTime);
- deviceChanged = true;
-}
-
-void AudioRenderer::updateOutput(const Codec *codec)
-{
- qCDebug(qLcAudioRenderer) << ">>>>>> updateOutput" << currentTime() << seekTime() << processedUSecs << isMaster();
- freeOutput();
- qCDebug(qLcAudioRenderer) << " " << currentTime() << seekTime() << processedUSecs;
-
- AVStream *audioStream = codec->stream();
-
- auto dev = output->device();
- format = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(audioStream->codecpar);
- format.setChannelConfig(dev.channelConfiguration());
-
- if (playbackRate() < 0.5 || playbackRate() > 2)
- audioMuted = true;
-
- audioSink = new QAudioSink(dev, format);
- audioSink->setBufferSize(format.bytesForDuration(100000));
- audioDevice = audioSink->start();
- latencyUSecs = format.durationForBytes(audioSink->bufferSize()); // ### ideally get full latency
- qCDebug(qLcAudioRenderer) << " -> have an audio sink" << audioDevice;
-
- // init resampler. It's ok to always do this, as the resampler will be a no-op if
- // formats agree.
- AVSampleFormat requiredFormat = QFFmpegMediaFormatInfo::avSampleFormat(format.sampleFormat());
-
- qCDebug(qLcAudioRenderer) << "init resampler" << requiredFormat << audioStream->codecpar->channels;
- resampler.reset(new Resampler(codec, format));
-}
-
-void AudioRenderer::freeOutput()
-{
- if (audioSink) {
- audioSink->reset();
- delete audioSink;
- audioSink = nullptr;
- audioDevice = nullptr;
- }
- audioMuted = false;
- bufferedData = {};
- bufferWritten = 0;
-
- audioBaseTime = currentTime();
- processedBase = 0;
- processedUSecs = writtenUSecs = 0;
-}
-
-void AudioRenderer::init()
-{
- qCDebug(qLcAudioRenderer) << "Starting audio renderer";
- ClockedRenderer::init();
-}
-
-void AudioRenderer::cleanup()
-{
- freeOutput();
-}
-
-void AudioRenderer::loop()
-{
- if (!streamDecoder) {
- timeOut = -1; // Avoid 100% CPU load before play()
- return;
- }
-
- if (deviceChanged)
- freeOutput();
- deviceChanged = false;
- doneStep();
-
- qint64 bytesWritten = 0;
- if (bufferedData.isValid()) {
- bytesWritten = audioDevice->write(bufferedData.constData<char>() + bufferWritten, bufferedData.byteCount() - bufferWritten);
- bufferWritten += bytesWritten;
- if (bufferWritten == bufferedData.byteCount()) {
- bufferedData = {};
- bufferWritten = 0;
- }
- processedUSecs = audioSink->processedUSecs();
- } else {
- Frame frame = streamDecoder->takeFrame();
- if (!frame.isValid()) {
- if (streamDecoder->isAtEnd()) {
- if (audioSink)
- processedUSecs = audioSink->processedUSecs();
- timeOut = -1;
- eos.storeRelease(true);
- emit atEnd();
- return;
- }
- timeOut = 1;
- return;
- }
- eos.storeRelease(false);
-
- if (!audioSink)
- updateOutput(frame.codec());
-
- qint64 startTime = frame.pts();
- if (startTime < seekTime())
- return;
-
- if (!paused) {
- auto buffer = resampler->resample(frame.avFrame());
-
- if (audioMuted)
- // This is somewhat inefficient, but it'll work
- memset(buffer.data<char>(), 0, buffer.byteCount());
-
- bytesWritten = audioDevice->write(buffer.constData<char>(), buffer.byteCount());
- if (bytesWritten < buffer.byteCount()) {
- bufferedData = buffer;
- bufferWritten = bytesWritten;
- }
-
- processedUSecs = audioSink->processedUSecs();
- }
- }
-
- qint64 duration = format.durationForBytes(bytesWritten);
- writtenUSecs += duration;
-
- timeOut = (writtenUSecs - processedUSecs - latencyUSecs)/1000;
- if (timeOut < 0)
- // Don't use a zero timeout if the sink didn't want any more data, rather wait for 10ms.
- timeOut = bytesWritten > 0 ? 0 : 10;
-
-// if (!bufferedData.isEmpty())
-// qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>> could not write all data" << (bufferedData.size() - bufferWritten);
-// qDebug() << "Audio: processed" << processedUSecs << "written" << writtenUSecs
-// << "delta" << (writtenUSecs - processedUSecs) << "timeOut" << timeOut;
-// qDebug() << " updating time to" << currentTimeNoLock();
- timeUpdated(audioBaseTime + (processedUSecs - processedBase)*playbackRate());
-}
-
-void AudioRenderer::streamChanged()
-{
- // mutex is already locked
- deviceChanged = true;
-}
-
-void AudioRenderer::updateAudio()
-{
- QMutexLocker locker(&mutex);
- deviceChanged = true;
-}
-
-Decoder::Decoder()
-{
-}
-
-Decoder::~Decoder()
-{
- pause();
- if (videoRenderer)
- videoRenderer->kill();
- if (audioRenderer)
- audioRenderer->kill();
- if (demuxer)
- demuxer->kill();
-}
-
-static int read(void *opaque, uint8_t *buf, int buf_size)
-{
- auto *dev = static_cast<QIODevice *>(opaque);
- if (dev->atEnd())
- return AVERROR_EOF;
- return dev->read(reinterpret_cast<char *>(buf), buf_size);
-}
-
-static int64_t seek(void *opaque, int64_t offset, int whence)
-{
- QIODevice *dev = static_cast<QIODevice *>(opaque);
-
- if (dev->isSequential())
- return AVERROR(EINVAL);
-
- if (whence & AVSEEK_SIZE)
- return dev->size();
-
- whence &= ~AVSEEK_FORCE;
-
- if (whence == SEEK_CUR)
- offset += dev->pos();
- else if (whence == SEEK_END)
- offset += dev->size();
-
- if (!dev->seek(offset))
- return AVERROR(EINVAL);
- return offset;
-}
-
-void Decoder::setMedia(const QUrl &media, QIODevice *stream)
-{
- QByteArray url = media.toEncoded(QUrl::PreferLocalFile);
-
- AVFormatContext *context = nullptr;
-
- if (stream) {
- if (!stream->isOpen()) {
- if (!stream->open(QIODevice::ReadOnly)) {
- emitError(QMediaPlayer::ResourceError, QLatin1String("Could not open source device."));
- return;
- }
- }
- if (!stream->isSequential())
- stream->seek(0);
- context = avformat_alloc_context();
- constexpr int bufferSize = 32768;
- unsigned char *buffer = (unsigned char *)av_malloc(bufferSize);
- context->pb = avio_alloc_context(buffer, bufferSize, false, stream, ::read, nullptr, ::seek);
- }
-
- int ret = avformat_open_input(&context, url.constData(), nullptr, nullptr);
- if (ret < 0) {
- auto code = QMediaPlayer::ResourceError;
- if (ret == AVERROR(EACCES))
- code = QMediaPlayer::AccessDeniedError;
- else if (ret == AVERROR(EINVAL))
- code = QMediaPlayer::FormatError;
-
- emitError(code, QMediaPlayer::tr("Could not open file"));
- return;
- }
-
- ret = avformat_find_stream_info(context, nullptr);
- if (ret < 0) {
- emitError(QMediaPlayer::FormatError, QMediaPlayer::tr("Could not find stream information for media file"));
- return;
- }
-
-#ifndef QT_NO_DEBUG
- av_dump_format(context, 0, url.constData(), 0);
-#endif
-
- m_metaData = QFFmpegMetaData::fromAVMetaData(context->metadata);
- m_metaData.insert(QMediaMetaData::FileFormat,
- QVariant::fromValue(QFFmpegMediaFormatInfo::fileFormatForAVInputFormat(context->iformat)));
-
- checkStreams(context);
-
- m_isSeekable = !(context->ctx_flags & AVFMTCTX_UNSEEKABLE);
-
- demuxer = new Demuxer(this, context);
- demuxer->start();
-
- qCDebug(qLcDecoder) << ">>>>>> index:" << metaObject()->indexOfSlot("updateCurrentTime(qint64)");
- clockController.setNotify(this, metaObject()->method(metaObject()->indexOfSlot("updateCurrentTime(qint64)")));
-}
-
-static void insertVideoData(QMediaMetaData &metaData, AVStream *stream)
-{
- Q_ASSERT(stream);
- auto *codecPar = stream->codecpar;
- metaData.insert(QMediaMetaData::VideoBitRate, (int)codecPar->bit_rate);
- metaData.insert(QMediaMetaData::VideoCodec, QVariant::fromValue(QFFmpegMediaFormatInfo::videoCodecForAVCodecId(codecPar->codec_id)));
- metaData.insert(QMediaMetaData::Resolution, QSize(codecPar->width, codecPar->height));
- metaData.insert(QMediaMetaData::VideoFrameRate,
- qreal(stream->avg_frame_rate.num)/qreal(stream->avg_frame_rate.den));
-};
-
-static void insertAudioData(QMediaMetaData &metaData, AVStream *stream)
-{
- Q_ASSERT(stream);
- auto *codecPar = stream->codecpar;
- metaData.insert(QMediaMetaData::AudioBitRate, (int)codecPar->bit_rate);
- metaData.insert(QMediaMetaData::AudioCodec,
- QVariant::fromValue(QFFmpegMediaFormatInfo::audioCodecForAVCodecId(codecPar->codec_id)));
-};
-
-void Decoder::checkStreams(AVFormatContext *context)
-{
- qint64 duration = 0;
- AVStream *firstAudioStream = nullptr;
- AVStream *defaultAudioStream = nullptr;
- AVStream *firstVideoStream = nullptr;
- AVStream *defaultVideoStream = nullptr;
-
- for (unsigned int i = 0; i < context->nb_streams; ++i) {
- auto *stream = context->streams[i];
-
- QMediaMetaData metaData = QFFmpegMetaData::fromAVMetaData(stream->metadata);
- QPlatformMediaPlayer::TrackType type = QPlatformMediaPlayer::VideoStream;
- auto *codecPar = stream->codecpar;
-
- bool isDefault = stream->disposition & AV_DISPOSITION_DEFAULT;
- switch (codecPar->codec_type) {
- case AVMEDIA_TYPE_UNKNOWN:
- case AVMEDIA_TYPE_DATA: ///< Opaque data information usually continuous
- case AVMEDIA_TYPE_ATTACHMENT: ///< Opaque data information usually sparse
- case AVMEDIA_TYPE_NB:
- continue;
- case AVMEDIA_TYPE_VIDEO:
- type = QPlatformMediaPlayer::VideoStream;
- insertVideoData(metaData, stream);
- if (!firstVideoStream)
- firstVideoStream = stream;
- if (isDefault && !defaultVideoStream)
- defaultVideoStream = stream;
- break;
- case AVMEDIA_TYPE_AUDIO:
- type = QPlatformMediaPlayer::AudioStream;
- insertAudioData(metaData, stream);
- if (!firstAudioStream)
- firstAudioStream = stream;
- if (isDefault && !defaultAudioStream)
- defaultAudioStream = stream;
- break;
- case AVMEDIA_TYPE_SUBTITLE:
- type = QPlatformMediaPlayer::SubtitleStream;
- break;
- }
- if (isDefault && m_requestedStreams[type] < 0)
- m_requestedStreams[type] = m_streamMap[type].size();
-
- m_streamMap[type].append({ (int)i, isDefault, metaData });
- duration = qMax(duration, 1000000*stream->duration*stream->time_base.num/stream->time_base.den);
- }
-
- if (m_requestedStreams[QPlatformMediaPlayer::VideoStream] < 0 && m_streamMap[QPlatformMediaPlayer::VideoStream].size()) {
- m_requestedStreams[QPlatformMediaPlayer::VideoStream] = 0;
- defaultVideoStream = firstVideoStream;
- }
- if (m_requestedStreams[QPlatformMediaPlayer::AudioStream] < 0 && m_streamMap[QPlatformMediaPlayer::AudioStream].size()) {
- m_requestedStreams[QPlatformMediaPlayer::AudioStream] = 0;
- defaultAudioStream = firstAudioStream;
- }
- if (defaultVideoStream) {
- insertVideoData(m_metaData, defaultVideoStream);
- m_currentAVStreamIndex[QPlatformMediaPlayer::VideoStream] = defaultVideoStream->index;
- }
- if (defaultAudioStream) {
- insertAudioData(m_metaData, defaultAudioStream);
- m_currentAVStreamIndex[QPlatformMediaPlayer::AudioStream] = defaultAudioStream->index;
- }
- m_requestedStreams[QPlatformMediaPlayer::SubtitleStream] = -1;
- m_currentAVStreamIndex[QPlatformMediaPlayer::SubtitleStream] = -1;
-
- if (player)
- player->tracksChanged();
-
- if (m_duration != duration) {
- m_duration = duration;
- if (player)
- player->durationChanged(duration/1000);
- else if (audioDecoder)
- audioDecoder->durationChanged(duration/1000);
- }
-}
-
-int Decoder::activeTrack(QPlatformMediaPlayer::TrackType type)
-{
- return m_requestedStreams[type];
-}
-
-void Decoder::setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber)
-{
- if (streamNumber < 0 || streamNumber >= m_streamMap[type].size())
- streamNumber = -1;
- if (m_requestedStreams[type] == streamNumber)
- return;
- m_requestedStreams[type] = streamNumber;
- int avStreamIndex = m_streamMap[type].value(streamNumber).avStreamIndex;
- changeAVTrack(type, avStreamIndex);
-}
-
-void Decoder::error(int errorCode, const QString &errorString)
-{
- QMetaObject::invokeMethod(this, "emitError", Q_ARG(int, errorCode), Q_ARG(QString, errorString));
-}
-
-void Decoder::emitError(int error, const QString &errorString)
-{
- if (player)
- player->error(error, errorString);
- else if (audioDecoder) {
- // unfortunately the error enums for QAudioDecoder and QMediaPlayer aren't identical.
- // Map them.
- switch (QMediaPlayer::Error(error)) {
- case QMediaPlayer::NoError:
- error = QAudioDecoder::NoError;
- break;
- case QMediaPlayer::ResourceError:
- error = QAudioDecoder::ResourceError;
- break;
- case QMediaPlayer::FormatError:
- error = QAudioDecoder::FormatError;
- break;
- case QMediaPlayer::NetworkError:
- // fall through, Network error doesn't exist in QAudioDecoder
- case QMediaPlayer::AccessDeniedError:
- error = QAudioDecoder::AccessDeniedError;
- break;
- }
-
- audioDecoder->error(error, errorString);
- }
-}
-
-void Decoder::setState(QMediaPlayer::PlaybackState state)
-{
- if (m_state == state)
- return;
-
- switch (state) {
- case QMediaPlayer::StoppedState:
- qCDebug(qLcDecoder) << "Decoder::stop";
- setPaused(true);
- if (demuxer)
- demuxer->stopDecoding();
- seek(0);
- if (videoSink)
- videoSink->setVideoFrame({});
- updateCurrentTime(0);
- qCDebug(qLcDecoder) << "Decoder::stop: done";
- break;
- case QMediaPlayer::PausedState:
- qCDebug(qLcDecoder) << "Decoder::pause";
- setPaused(true);
- if (demuxer) {
- demuxer->startDecoding();
- demuxer->wake();
- if (m_state == QMediaPlayer::StoppedState)
- triggerStep();
- }
- break;
- case QMediaPlayer::PlayingState:
- qCDebug(qLcDecoder) << "Decoder::play";
- setPaused(false);
- if (demuxer)
- demuxer->startDecoding();
- break;
- }
- m_state = state;
-}
-
-void Decoder::setPaused(bool b)
-{
- clockController.setPaused(b);
-}
-
-void Decoder::triggerStep()
-{
- if (audioRenderer)
- audioRenderer->singleStep();
- if (videoRenderer)
- videoRenderer->singleStep();
-}
-
-void Decoder::setVideoSink(QVideoSink *sink)
-{
- qCDebug(qLcDecoder) << "setVideoSink" << sink;
- if (sink == videoSink)
- return;
- videoSink = sink;
- if (!videoSink || m_currentAVStreamIndex[QPlatformMediaPlayer::VideoStream] < 0) {
- if (videoRenderer) {
- videoRenderer->kill();
- videoRenderer = nullptr;
- }
- } else if (!videoRenderer) {
- videoRenderer = new VideoRenderer(this, sink);
- connect(audioRenderer, &Renderer::atEnd, this, &Decoder::streamAtEnd);
- videoRenderer->start();
- StreamDecoder *stream = demuxer->addStream(m_currentAVStreamIndex[QPlatformMediaPlayer::VideoStream]);
- videoRenderer->setStream(stream);
- stream = demuxer->addStream(m_currentAVStreamIndex[QPlatformMediaPlayer::SubtitleStream]);
- videoRenderer->setSubtitleStream(stream);
- }
-}
-
-void Decoder::setAudioSink(QPlatformAudioOutput *output)
-{
- if (audioOutput == output)
- return;
-
- qCDebug(qLcDecoder) << "setAudioSink" << audioOutput;
- audioOutput = output;
- if (!output || m_currentAVStreamIndex[QPlatformMediaPlayer::AudioStream] < 0) {
- if (audioRenderer) {
- audioRenderer->kill();
- audioRenderer = nullptr;
- }
- } else if (!audioRenderer) {
- audioRenderer = new AudioRenderer(this, output->q);
- connect(audioRenderer, &Renderer::atEnd, this, &Decoder::streamAtEnd);
- audioRenderer->start();
- auto *stream = demuxer->addStream(m_currentAVStreamIndex[QPlatformMediaPlayer::AudioStream]);
- audioRenderer->setStream(stream);
- }
-}
-
-void Decoder::changeAVTrack(QPlatformMediaPlayer::TrackType type, int streamIndex)
-{
- int oldIndex = m_currentAVStreamIndex[type];
- qCDebug(qLcDecoder) << ">>>>> change track" << type << "from" << oldIndex << "to" << streamIndex << clockController.currentTime();
- m_currentAVStreamIndex[type] = streamIndex;
- if (!demuxer)
- return;
- qCDebug(qLcDecoder) << " applying to renderer.";
- if (m_state == QMediaPlayer::PlayingState)
- setPaused(true);
- auto *streamDecoder = demuxer->addStream(streamIndex);
- switch (type) {
- case QPlatformMediaPlayer::AudioStream:
- audioRenderer->setStream(streamDecoder);
- break;
- case QPlatformMediaPlayer::VideoStream:
- videoRenderer->setStream(streamDecoder);
- break;
- case QPlatformMediaPlayer::SubtitleStream:
- videoRenderer->setSubtitleStream(streamDecoder);
- break;
- default:
- Q_UNREACHABLE();
- }
- demuxer->seek(clockController.currentTime());
- if (m_state == QMediaPlayer::PlayingState)
- setPaused(false);
- else
- triggerStep();
-}
-
-QPlatformMediaPlayer::TrackType trackType(AVMediaType mediaType)
-{
- switch (mediaType) {
- case AVMEDIA_TYPE_VIDEO:
- return QPlatformMediaPlayer::VideoStream;
- case AVMEDIA_TYPE_AUDIO:
- return QPlatformMediaPlayer::AudioStream;
- case AVMEDIA_TYPE_SUBTITLE:
- return QPlatformMediaPlayer::SubtitleStream;
- default:
- break;
- }
- return QPlatformMediaPlayer::NTrackTypes;
-}
-
-void Decoder::seek(qint64 pos)
-{
- if (!demuxer)
- return;
- pos = qBound(0, pos, m_duration);
- demuxer->seek(pos);
- clockController.syncTo(pos);
- if (player)
- player->positionChanged(pos/1000);
- demuxer->wake();
- if (m_state == QMediaPlayer::PausedState)
- triggerStep();
-}
-
-void Decoder::setPlaybackRate(float rate)
-{
- if (m_state == QMediaPlayer::PlayingState)
- setPaused(true);
- clockController.setPlaybackRate(rate);
- if (m_state == QMediaPlayer::PlayingState)
- setPaused(false);
-}
-
-void Decoder::updateCurrentTime(qint64 time)
-{
- if (player)
- player->positionChanged(time/1000);
-}
-
-void Decoder::streamAtEnd()
-{
- if (audioRenderer && !audioRenderer->isAtEnd())
- return;
- if (videoRenderer && !videoRenderer->isAtEnd())
- return;
- pause();
- // take a local copy, as the signals below could lead to this object being deleted
- auto *p = player;
- if (p) {
- p->positionChanged(m_duration/1000);
- p->stateChanged(QMediaPlayer::StoppedState);
- p->mediaStatusChanged(QMediaPlayer::EndOfMedia);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h
deleted file mode 100644
index 414524838..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegdecoder_p.h
+++ /dev/null
@@ -1,548 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QFFMPEGDECODER_P_H
-#define QFFMPEGDECODER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qffmpegthread_p.h"
-#include "qffmpeg_p.h"
-#include "qffmpegmediaplayer_p.h"
-#include "qffmpeghwaccel_p.h"
-#include "qffmpegclock_p.h"
-#include "qaudiobuffer.h"
-#include "qffmpegresampler_p.h"
-
-#include <qshareddata.h>
-#include <qtimer.h>
-#include <qqueue.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAudioSink;
-class QFFmpegAudioDecoder;
-class QFFmpegMediaPlayer;
-
-namespace QFFmpeg
-{
-
-class Resampler;
-
-// queue up max 16M of encoded data, that should always be enough
-// (it's around 2 secs of 4K HDR video, longer for almost all other formats)
-enum { MaxQueueSize = 16*1024*1024 };
-
-struct Packet
-{
- struct Data {
- Data(AVPacket *p)
- : packet(p)
- {}
- ~Data() {
- if (packet)
- av_packet_free(&packet);
- }
- QAtomicInt ref;
- AVPacket *packet = nullptr;
- };
- Packet() = default;
- Packet(AVPacket *p)
- : d(new Data(p))
- {}
-
- bool isValid() const { return !!d; }
- AVPacket *avPacket() const { return d->packet; }
-private:
- QExplicitlySharedDataPointer<Data> d;
-};
-
-struct Codec
-{
- struct Data {
- Data(AVCodecContext *context, AVStream *stream, const QFFmpeg::HWAccel &hwAccel);
- ~Data();
- QAtomicInt ref;
- AVCodecContext *context = nullptr;
- AVStream *stream = nullptr;
- QFFmpeg::HWAccel hwAccel;
- int streamIndex = -1;
- };
-
- Codec() = default;
- Codec(AVFormatContext *format, int streamIndex);
- bool isValid() const { return !!d; }
-
- AVCodecContext *context() const { return d->context; }
- AVStream *stream() const { return d->stream; }
- uint streamIndex() const { return d->stream->index; }
- HWAccel hwAccel() const { return d->hwAccel; }
- qint64 toMs(qint64 ts) const { return timeStamp(ts, d->stream->time_base); }
- qint64 toUs(qint64 ts) const { return timeStampUs(ts, d->stream->time_base); }
-
-private:
- QExplicitlySharedDataPointer<Data> d;
-};
-
-
-struct Frame
-{
- struct Data {
- Data(AVFrame *f, const Codec &codec, qint64 pts)
- : codec(codec)
- , frame(f)
- , pts(pts)
- {}
- Data(const QString &text, qint64 pts, qint64 duration)
- : text(text), pts(pts), duration(duration)
- {}
- ~Data() {
- if (frame)
- av_frame_free(&frame);
- }
- QAtomicInt ref;
- Codec codec;
- AVFrame *frame = nullptr;
- QString text;
- qint64 pts = -1;
- qint64 duration = -1;
- };
- Frame() = default;
- Frame(AVFrame *f, const Codec &codec, qint64 pts)
- : d(new Data(f, codec, pts))
- {}
- Frame(const QString &text, qint64 pts, qint64 duration)
- : d(new Data(text, pts, duration))
- {}
- bool isValid() const { return !!d; }
-
- AVFrame *avFrame() const { return d->frame; }
- AVFrame *takeAVFrame() const {
- AVFrame *f = d->frame;
- d->frame = nullptr;
- return f;
- }
- const Codec *codec() const { return &d->codec; }
- qint64 pts() const { return d->pts; }
- qint64 duration() const { return d->duration; }
- qint64 end() const { return d->pts + d->duration; }
- QString text() const { return d->text; }
-private:
- QExplicitlySharedDataPointer<Data> d;
-};
-
-class Demuxer;
-class StreamDecoder;
-class Renderer;
-class AudioRenderer;
-class VideoRenderer;
-
-class Decoder : public QObject
-{
- Q_OBJECT
-public:
- Decoder();
- Decoder(QFFmpegMediaPlayer *player)
- : player(player)
- {
- }
- Decoder(QFFmpegAudioDecoder *decoder)
- : audioDecoder(decoder)
- {
- }
- ~Decoder();
-
- void setMedia(const QUrl &media, QIODevice *stream);
-
- void init();
- void setState(QMediaPlayer::PlaybackState state);
- void play() {
- setState(QMediaPlayer::PlayingState);
- }
- void pause() {
- setState(QMediaPlayer::PausedState);
- }
- void stop() {
- setState(QMediaPlayer::StoppedState);
- }
-
- void triggerStep();
-
- void setVideoSink(QVideoSink *sink);
- void setAudioSink(QPlatformAudioOutput *output);
-
- void changeAVTrack(QPlatformMediaPlayer::TrackType type, int index);
-
- void seek(qint64 pos);
- void setPlaybackRate(float rate);
-
- void checkStreams(AVFormatContext *context);
-
- int activeTrack(QPlatformMediaPlayer::TrackType type);
- void setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber);
-
- bool isSeekable() const
- {
- return m_isSeekable;
- }
-
- // threadsafe
- void error(int errorCode, const QString &errorString);
-
-public Q_SLOTS:
- void emitError(int error, const QString &errorString);
- void updateCurrentTime(qint64 time);
- void streamAtEnd();
-
-public:
-
- // Accessed from multiple threads, but API is threadsafe
- ClockController clockController;
-
-private:
- void setPaused(bool b);
-
-protected:
- friend QFFmpegMediaPlayer;
-
- QFFmpegMediaPlayer *player = nullptr;
- QFFmpegAudioDecoder *audioDecoder = nullptr;
-
- QMediaPlayer::PlaybackState m_state = QMediaPlayer::StoppedState;
- bool m_isSeekable = false;
-
- Demuxer *demuxer = nullptr;
- int m_currentAVStreamIndex[QPlatformMediaPlayer::NTrackTypes] = { -1, -1, -1 };
-
- QVideoSink *videoSink = nullptr;
- Renderer *videoRenderer = nullptr;
-
- QPlatformAudioOutput *audioOutput = nullptr;
- Renderer *audioRenderer = nullptr;
-
- bool playing = false;
-
- struct StreamInfo {
- int avStreamIndex = -1;
- bool isDefault = false;
- QMediaMetaData metaData;
- };
-
- QList<StreamInfo> m_streamMap[QPlatformMediaPlayer::NTrackTypes];
- int m_requestedStreams[3] = { -1, -1, -1 };
- qint64 m_duration = 0;
- QMediaMetaData m_metaData;
-};
-
-class Demuxer : public Thread
-{
- Q_OBJECT
-public:
- Demuxer(Decoder *decoder, AVFormatContext *context);
- ~Demuxer();
-
- StreamDecoder *addStream(int streamIndex);
- void removeStream(int streamIndex);
-
- bool isStopped() const
- {
- return m_isStopped.loadRelaxed();
- }
- void startDecoding()
- {
- m_isStopped.storeRelaxed(false);
- updateEnabledStreams();
- wake();
- }
- void stopDecoding();
-
- int seek(qint64 pos);
-
-private:
- void updateEnabledStreams();
- void sendFinalPacketToStreams();
-
- void init() override;
- void cleanup() override;
- bool shouldWait() const override;
- void loop() override;
-
- Decoder *decoder;
- AVFormatContext *context = nullptr;
- QList<StreamDecoder *> streamDecoders;
-
- QAtomicInteger<bool> m_isStopped = true;
- qint64 last_pts = -1;
-};
-
-
-class StreamDecoder : public Thread
-{
- Q_OBJECT
-protected:
- Demuxer *demuxer = nullptr;
- Renderer *m_renderer = nullptr;
-
- struct PacketQueue {
- mutable QMutex mutex;
- QQueue<Packet> queue;
- qint64 size = 0;
- qint64 duration = 0;
- };
- PacketQueue packetQueue;
-
- struct FrameQueue {
- mutable QMutex mutex;
- QQueue<Frame> queue;
- int maxSize = 3;
- };
- FrameQueue frameQueue;
- QAtomicInteger<bool> eos = false;
- bool decoderHasNoFrames = false;
-
-public:
- StreamDecoder(Demuxer *demuxer, const Codec &codec);
-
- void addPacket(AVPacket *packet);
-
- qint64 queuedPacketSize() const {
- QMutexLocker locker(&packetQueue.mutex);
- return packetQueue.size;
- }
- qint64 queuedDuration() const {
- QMutexLocker locker(&packetQueue.mutex);
- return packetQueue.duration;
- }
-
- const Frame *lockAndPeekFrame()
- {
- frameQueue.mutex.lock();
- return frameQueue.queue.isEmpty() ? nullptr : &frameQueue.queue.first();
- }
- void removePeekedFrame()
- {
- frameQueue.queue.takeFirst();
- wake();
- }
- void unlockAndReleaseFrame()
- {
- frameQueue.mutex.unlock();
- }
- Frame takeFrame();
-
- void flush();
-
- Codec codec;
-
- void setRenderer(Renderer *r);
- Renderer *renderer() const { return m_renderer; }
-
- bool isAtEnd() const { return eos.loadAcquire(); }
-
- void killHelper() override;
-
-private:
- Packet takePacket();
- Packet peekPacket();
-
- void addFrame(const Frame &f);
-
- bool hasEnoughFrames() const
- {
- QMutexLocker locker(&frameQueue.mutex);
- return frameQueue.queue.size() >= frameQueue.maxSize;
- }
- bool hasNoPackets() const
- {
- QMutexLocker locker(&packetQueue.mutex);
- return packetQueue.queue.isEmpty();
- }
-
- void init() override;
- bool shouldWait() const override;
- void loop() override;
-
- void decode();
- void decodeSubtitle();
-
- QPlatformMediaPlayer::TrackType type() const;
-};
-
-class Renderer : public Thread
-{
- Q_OBJECT
-protected:
- QPlatformMediaPlayer::TrackType type;
-
- bool step = false;
- bool paused = true;
- StreamDecoder *streamDecoder = nullptr;
- QAtomicInteger<bool> eos = false;
-
-public:
- Renderer(QPlatformMediaPlayer::TrackType type);
-
- void setPaused(bool p) {
- QMutexLocker locker(&mutex);
- paused = p;
- if (!p)
- wake();
- }
- void singleStep() {
- QMutexLocker locker(&mutex);
- if (!paused)
- return;
- step = true;
- wake();
- }
- void doneStep() {
- step = false;
- }
- bool isAtEnd() { return !streamDecoder || eos.loadAcquire(); }
-
- void setStream(StreamDecoder *stream);
- virtual void setSubtitleStream(StreamDecoder *) {}
-
- void killHelper() override;
-
- virtual void streamChanged() {}
-
-Q_SIGNALS:
- void atEnd();
-
-protected:
- bool shouldWait() const override;
-
-public:
-};
-
-class ClockedRenderer : public Renderer, public Clock
-{
-public:
- ClockedRenderer(Decoder *decoder, QPlatformMediaPlayer::TrackType type)
- : Renderer(type)
- , Clock(&decoder->clockController)
- {
- }
- ~ClockedRenderer()
- {
- }
- void setPaused(bool paused) override;
-};
-
-class VideoRenderer : public ClockedRenderer
-{
- Q_OBJECT
-
- StreamDecoder *subtitleStreamDecoder = nullptr;
-public:
- VideoRenderer(Decoder *decoder, QVideoSink *sink);
-
- void killHelper() override;
-
- void setSubtitleStream(StreamDecoder *stream) override;
-private:
-
- void init() override;
- void loop() override;
-
- QVideoSink *sink;
-};
-
-class AudioRenderer : public ClockedRenderer
-{
- Q_OBJECT
-public:
- AudioRenderer(Decoder *decoder, QAudioOutput *output);
- ~AudioRenderer() = default;
-
- // Clock interface
- void syncTo(qint64 usecs) override;
- void setPlaybackRate(float rate, qint64 currentTime) override;
-
-private slots:
- void updateAudio();
-
-private:
- void updateOutput(const Codec *codec);
- void freeOutput();
-
- void init() override;
- void cleanup() override;
- void loop() override;
- void streamChanged() override;
- Type type() const override { return AudioClock; }
-
- int outputSamples(int inputSamples) {
- return qRound(inputSamples/playbackRate());
- }
-
- // Used for timing update calculations based on processed data
- qint64 audioBaseTime = 0;
- qint64 processedBase = 0;
- qint64 processedUSecs = 0;
-
- bool deviceChanged = false;
- QAudioOutput *output = nullptr;
- bool audioMuted = false;
- qint64 writtenUSecs = 0;
- qint64 latencyUSecs = 0;
-
- QAudioFormat format;
- QAudioSink *audioSink = nullptr;
- QIODevice *audioDevice = nullptr;
- std::unique_ptr<Resampler> resampler;
- QAudioBuffer bufferedData;
- qsizetype bufferWritten = 0;
-};
-
-}
-
-QT_END_NAMESPACE
-
-#endif
-
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h b/src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h
new file mode 100644
index 000000000..239d8ff0c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGDEFS_P_H
+#define QFFMPEGDEFS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+extern "C" {
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+#include <libswresample/swresample.h>
+#include <libavutil/avutil.h>
+#include <libswscale/swscale.h>
+}
+
+#define QT_FFMPEG_OLD_CHANNEL_LAYOUT (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100))
+#define QT_FFMPEG_HAS_VULKAN \
+ (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 91, 100)) // since ffmpeg n4.3
+#define QT_FFMPEG_HAS_FRAME_TIME_BASE \
+ (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 18, 100)) // since ffmpeg n5.0
+#define QT_FFMPEG_HAS_FRAME_DURATION \
+ (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 3, 100)) // since ffmpeg n6.0
+#define QT_FFMPEG_STREAM_SIDE_DATA_DEPRECATED \
+ (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 15, 100)) // since ffmpeg n6.1
+#define QT_FFMPEG_HAS_D3D12VA \
+ (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(59, 8, 100)) // since ffmpeg n7.0
+#define QT_FFMPEG_SWR_CONST_CH_LAYOUT (LIBSWRESAMPLE_VERSION_INT >= AV_VERSION_INT(4, 9, 100))
+#define QT_FFMPEG_AVIO_WRITE_CONST \
+ (LIBAVFORMAT_VERSION_MAJOR >= 61)
+
+#endif // QFFMPEGDEFS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp
deleted file mode 100644
index f8fa064bc..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegencoder.cpp
+++ /dev/null
@@ -1,567 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "qffmpegencoder_p.h"
-#include "qffmpegmediaformatinfo_p.h"
-#include "qffmpegvideoframeencoder_p.h"
-#include "private/qmultimediautils_p.h"
-
-#include <qdebug.h>
-#include <qiodevice.h>
-#include <qaudiosource.h>
-#include <qaudiobuffer.h>
-#include "qffmpegaudioinput_p.h"
-#include <private/qplatformcamera_p.h>
-#include "qffmpegvideobuffer_p.h"
-#include "qffmpegmediametadata_p.h"
-#include "qffmpegencoderoptions_p.h"
-
-#include <qloggingcategory.h>
-
-extern "C" {
-#include <libavutil/pixdesc.h>
-#include <libavutil/common.h>
-}
-
-QT_BEGIN_NAMESPACE
-
-Q_LOGGING_CATEGORY(qLcFFmpegEncoder, "qt.multimedia.ffmpeg.encoder")
-
-namespace QFFmpeg
-{
-
-Encoder::Encoder(const QMediaEncoderSettings &settings, const QUrl &url)
- : settings(settings)
-{
- const AVOutputFormat *avFormat = QFFmpegMediaFormatInfo::outputFormatForFileFormat(settings.fileFormat());
-
- formatContext = avformat_alloc_context();
- formatContext->oformat = const_cast<AVOutputFormat *>(avFormat); // constness varies
-
- QByteArray encoded = url.toEncoded();
- formatContext->url = (char *)av_malloc(encoded.size() + 1);
- memcpy(formatContext->url, encoded.constData(), encoded.size() + 1);
- formatContext->pb = nullptr;
- avio_open2(&formatContext->pb, formatContext->url, AVIO_FLAG_WRITE, nullptr, nullptr);
- qCDebug(qLcFFmpegEncoder) << "opened" << formatContext->url;
-
- muxer = new Muxer(this);
-}
-
-Encoder::~Encoder()
-{
-}
-
-void Encoder::addAudioInput(QFFmpegAudioInput *input)
-{
- audioEncode = new AudioEncoder(this, input, settings);
- connect(input, &QFFmpegAudioInput::newAudioBuffer, this, &Encoder::newAudioBuffer);
- input->setRunning(true);
-}
-
-void Encoder::addVideoSource(QPlatformCamera *source)
-{
- videoEncode = new VideoEncoder(this, source, settings);
- connect(source, &QPlatformCamera::newVideoFrame, this, &Encoder::newVideoFrame);
-}
-
-void Encoder::start()
-{
- qCDebug(qLcFFmpegEncoder) << "Encoder::start!";
-
- formatContext->metadata = QFFmpegMetaData::toAVMetaData(metaData);
-
- int res = avformat_write_header(formatContext, nullptr);
- if (res < 0)
- qWarning() << "could not write header" << res;
-
- muxer->start();
- if (audioEncode)
- audioEncode->start();
- if (videoEncode)
- videoEncode->start();
- isRecording = true;
-}
-
-void EncodingFinalizer::run()
-{
- if (encoder->audioEncode)
- encoder->audioEncode->kill();
- if (encoder->videoEncode)
- encoder->videoEncode->kill();
- encoder->muxer->kill();
-
- int res = av_write_trailer(encoder->formatContext);
- if (res < 0)
- qWarning() << "could not write trailer" << res;
-
- avformat_free_context(encoder->formatContext);
- qDebug() << " done finalizing.";
- emit encoder->finalizationDone();
- delete encoder;
- deleteLater();
-}
-
-void Encoder::finalize()
-{
- qDebug() << ">>>>>>>>>>>>>>> finalize";
-
- isRecording = false;
- auto *finalizer = new EncodingFinalizer(this);
- finalizer->start();
-}
-
-void Encoder::setPaused(bool p)
-{
- if (audioEncode)
- audioEncode->setPaused(p);
- if (videoEncode)
- videoEncode->setPaused(p);
-}
-
-void Encoder::setMetaData(const QMediaMetaData &metaData)
-{
- this->metaData = metaData;
-}
-
-void Encoder::newAudioBuffer(const QAudioBuffer &buffer)
-{
- if (audioEncode && isRecording)
- audioEncode->addBuffer(buffer);
-}
-
-void Encoder::newVideoFrame(const QVideoFrame &frame)
-{
- if (videoEncode && isRecording)
- videoEncode->addFrame(frame);
-}
-
-void Encoder::newTimeStamp(qint64 time)
-{
- QMutexLocker locker(&timeMutex);
- if (time > timeRecorded) {
- timeRecorded = time;
- emit durationChanged(time);
- }
-}
-
-Muxer::Muxer(Encoder *encoder)
- : encoder(encoder)
-{
- setObjectName(QLatin1String("Muxer"));
-}
-
-void Muxer::addPacket(AVPacket *packet)
-{
-// qCDebug(qLcFFmpegEncoder) << "Muxer::addPacket" << packet->pts << packet->stream_index;
- QMutexLocker locker(&queueMutex);
- packetQueue.enqueue(packet);
- wake();
-}
-
-AVPacket *Muxer::takePacket()
-{
- QMutexLocker locker(&queueMutex);
- if (packetQueue.isEmpty())
- return nullptr;
-// qCDebug(qLcFFmpegEncoder) << "Muxer::takePacket" << packetQueue.first()->pts;
- return packetQueue.dequeue();
-}
-
-void Muxer::init()
-{
-}
-
-void Muxer::cleanup()
-{
-}
-
-bool QFFmpeg::Muxer::shouldWait() const
-{
- QMutexLocker locker(&queueMutex);
- return packetQueue.isEmpty();
-}
-
-void Muxer::loop()
-{
- auto *packet = takePacket();
-// qCDebug(qLcFFmpegEncoder) << "writing packet to file" << packet->pts << packet->duration << packet->stream_index;
- av_interleaved_write_frame(encoder->formatContext, packet);
-}
-
-
-static AVSampleFormat bestMatchingSampleFormat(AVSampleFormat requested, const AVSampleFormat *available)
-{
- if (!available)
- return requested;
-
- const AVSampleFormat *f = available;
- AVSampleFormat best = *f;
-/*
- enum {
- First,
- Planar,
- Exact,
- } score = First;
-*/
- for (; *f != AV_SAMPLE_FMT_NONE; ++f) {
- qCDebug(qLcFFmpegEncoder) << "format:" << *f;
- if (*f == requested) {
- best = *f;
-// score = Exact;
- break;
- }
-
- if (av_get_planar_sample_fmt(requested) == *f) {
-// score = Planar;
- best = *f;
- }
- }
- return best;
-}
-
-AudioEncoder::AudioEncoder(Encoder *encoder, QFFmpegAudioInput *input, const QMediaEncoderSettings &settings)
- : input(input)
-{
- this->encoder = encoder;
-
- setObjectName(QLatin1String("AudioEncoder"));
- qCDebug(qLcFFmpegEncoder) << "AudioEncoder" << settings.audioCodec();
-
- format = input->device.preferredFormat();
- auto codecID = QFFmpegMediaFormatInfo::codecIdForAudioCodec(settings.audioCodec());
- Q_ASSERT(avformat_query_codec(encoder->formatContext->oformat, codecID, FF_COMPLIANCE_NORMAL));
-
- auto *avCodec = avcodec_find_encoder(codecID);
-
- AVSampleFormat requested = QFFmpegMediaFormatInfo::avSampleFormat(format.sampleFormat());
- AVSampleFormat bestSampleFormat = bestMatchingSampleFormat(requested, avCodec->sample_fmts);
-
- stream = avformat_new_stream(encoder->formatContext, nullptr);
- stream->id = encoder->formatContext->nb_streams - 1;
- stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
- stream->codecpar->codec_id = codecID;
- stream->codecpar->channel_layout = av_get_default_channel_layout(format.channelCount());
- stream->codecpar->channels = format.channelCount();
- stream->codecpar->sample_rate = format.sampleRate();
- stream->codecpar->frame_size = 1024;
- stream->codecpar->format = bestSampleFormat;
- stream->time_base = AVRational{ 1, format.sampleRate() };
-
- Q_ASSERT(avCodec);
- codec = avcodec_alloc_context3(avCodec);
- avcodec_parameters_to_context(codec, stream->codecpar);
-
- AVDictionary *opts = nullptr;
- applyAudioEncoderOptions(settings, avCodec->name, codec, &opts);
-
- int res = avcodec_open2(codec, avCodec, &opts);
- qCDebug(qLcFFmpegEncoder) << "audio codec opened" << res;
- qCDebug(qLcFFmpegEncoder) << "audio codec params: fmt=" << codec->sample_fmt << "rate=" << codec->sample_rate;
-
- if (codec->sample_fmt != requested) {
- resampler = swr_alloc_set_opts(nullptr, // we're allocating a new context
- codec->channel_layout, // out_ch_layout
- codec->sample_fmt, // out_sample_fmt
- codec->sample_rate, // out_sample_rate
- av_get_default_channel_layout(format.channelCount()), // in_ch_layout
- requested, // in_sample_fmt
- format.sampleRate(), // in_sample_rate
- 0, // log_offset
- nullptr);
- swr_init(resampler);
- }
-}
-
-void AudioEncoder::addBuffer(const QAudioBuffer &buffer)
-{
- QMutexLocker locker(&queueMutex);
- audioBufferQueue.enqueue(buffer);
- wake();
-}
-
-QAudioBuffer AudioEncoder::takeBuffer()
-{
- QMutexLocker locker(&queueMutex);
- if (audioBufferQueue.isEmpty())
- return QAudioBuffer();
- return audioBufferQueue.dequeue();
-}
-
-void AudioEncoder::init()
-{
- if (input) {
- input->setFrameSize(codec->frame_size);
- }
- qCDebug(qLcFFmpegEncoder) << "AudioEncoder::init started audio device thread.";
-}
-
-void AudioEncoder::cleanup()
-{
- while (!audioBufferQueue.isEmpty())
- loop();
- while (avcodec_send_frame(codec, nullptr) == AVERROR(EAGAIN))
- retrievePackets();
- retrievePackets();
-}
-
-bool AudioEncoder::shouldWait() const
-{
- QMutexLocker locker(&queueMutex);
- return audioBufferQueue.isEmpty();
-}
-
-void AudioEncoder::retrievePackets()
-{
- while (1) {
- AVPacket *packet = av_packet_alloc();
- int ret = avcodec_receive_packet(codec, packet);
- if (ret < 0) {
- av_packet_unref(packet);
- if (ret != AVERROR(EOF))
- break;
- if (ret != AVERROR(EAGAIN)) {
- char errStr[1024];
- av_strerror(ret, errStr, 1024);
- qCDebug(qLcFFmpegEncoder) << "receive packet" << ret << errStr;
- }
- break;
- }
-
- // qCDebug(qLcFFmpegEncoder) << "writing video packet" << packet->size << packet->pts << timeStamp(packet->pts, stream->time_base) << packet->stream_index;
- packet->stream_index = stream->id;
- encoder->muxer->addPacket(packet);
- }
-}
-
-void AudioEncoder::loop()
-{
- QAudioBuffer buffer = takeBuffer();
- if (!buffer.isValid() || paused.loadAcquire())
- return;
-
-// qCDebug(qLcFFmpegEncoder) << "new audio buffer" << buffer.byteCount() << buffer.format() << buffer.frameCount() << codec->frame_size;
- retrievePackets();
-
- AVFrame *frame = av_frame_alloc();
- frame->format = codec->sample_fmt;
- frame->channel_layout = codec->channel_layout;
- frame->channels = codec->channels;
- frame->sample_rate = codec->sample_rate;
- frame->nb_samples = buffer.frameCount();
- if (frame->nb_samples)
- av_frame_get_buffer(frame, 0);
-
- if (resampler) {
- const uint8_t *data = buffer.constData<uint8_t>();
- swr_convert(resampler, frame->extended_data, frame->nb_samples, &data, frame->nb_samples);
- } else {
- memcpy(frame->buf[0]->data, buffer.constData<uint8_t>(), buffer.byteCount());
- }
-
- frame->pts = samplesWritten;
- samplesWritten += buffer.frameCount();
-
- qint64 time = format.durationForFrames(samplesWritten);
- encoder->newTimeStamp(time/1000);
-
-// qCDebug(qLcFFmpegEncoder) << "sending audio frame" << buffer.byteCount() << frame->pts << ((double)buffer.frameCount()/frame->sample_rate);
- int ret = avcodec_send_frame(codec, frame);
- if (ret < 0) {
- char errStr[1024];
- av_strerror(ret, errStr, 1024);
-// qCDebug(qLcFFmpegEncoder) << "error sending frame" << ret << errStr;
- }
-}
-
-VideoEncoder::VideoEncoder(Encoder *encoder, QPlatformCamera *camera, const QMediaEncoderSettings &settings)
- : m_encoderSettings(settings)
- , m_camera(camera)
-{
- this->encoder = encoder;
-
- setObjectName(QLatin1String("VideoEncoder"));
- qCDebug(qLcFFmpegEncoder) << "VideoEncoder" << settings.videoCodec();
-
- auto format = m_camera->cameraFormat();
- auto *hwAccel = static_cast<const QFFmpeg::HWAccel *>(camera->ffmpegHWAccel());
- AVPixelFormat swFormat = QFFmpegVideoBuffer::toAVPixelFormat(format.pixelFormat());
- AVPixelFormat pixelFormat = hwAccel ? hwAccel->hwFormat() : swFormat;
- frameEncoder = new VideoFrameEncoder(settings, format.resolution(), format.maxFrameRate(), pixelFormat, swFormat);
- frameEncoder->initWithFormatContext(encoder->formatContext);
-}
-
-VideoEncoder::~VideoEncoder()
-{
- delete frameEncoder;
-}
-
-void VideoEncoder::addFrame(const QVideoFrame &frame)
-{
- QMutexLocker locker(&queueMutex);
- videoFrameQueue.enqueue(frame);
- wake();
-}
-
-QVideoFrame VideoEncoder::takeFrame()
-{
- QMutexLocker locker(&queueMutex);
- if (videoFrameQueue.isEmpty())
- return QVideoFrame();
- return videoFrameQueue.dequeue();
-}
-
-void VideoEncoder::retrievePackets()
-{
- if (!frameEncoder)
- return;
- while (AVPacket *packet = frameEncoder->retrievePacket())
- encoder->muxer->addPacket(packet);
-}
-
-void VideoEncoder::init()
-{
- qCDebug(qLcFFmpegEncoder) << "VideoEncoder::init started video device thread.";
- bool ok = frameEncoder->open();
- if (!ok)
- encoder->error(QMediaRecorder::ResourceError, "Could not initialize encoder");
-}
-
-void VideoEncoder::cleanup()
-{
- while (!videoFrameQueue.isEmpty())
- loop();
- if (frameEncoder) {
- while (frameEncoder->sendFrame(nullptr) == AVERROR(EAGAIN))
- retrievePackets();
- retrievePackets();
- }
-}
-
-bool VideoEncoder::shouldWait() const
-{
- QMutexLocker locker(&queueMutex);
- return videoFrameQueue.isEmpty();
-}
-
-struct QVideoFrameHolder
-{
- QVideoFrame f;
- QImage i;
-};
-
-static void freeQVideoFrame(void *opaque, uint8_t *)
-{
- delete reinterpret_cast<QVideoFrameHolder *>(opaque);
-}
-
-void VideoEncoder::loop()
-{
- if (paused.loadAcquire())
- return;
-
- retrievePackets();
-
- auto frame = takeFrame();
- if (!frame.isValid())
- return;
-
- if (frameEncoder->isNull())
- return;
-
-// qCDebug(qLcFFmpegEncoder) << "new video buffer" << frame.startTime();
-
- AVFrame *avFrame = nullptr;
-
- auto *videoBuffer = dynamic_cast<QFFmpegVideoBuffer *>(frame.videoBuffer());
- if (videoBuffer) {
- // ffmpeg video buffer, let's use the native AVFrame stored in there
- auto *hwFrame = videoBuffer->getHWFrame();
- if (hwFrame && hwFrame->format == frameEncoder->sourceFormat())
- avFrame = av_frame_clone(hwFrame);
- }
-
- if (!avFrame) {
- frame.map(QVideoFrame::ReadOnly);
- auto size = frame.size();
- avFrame = av_frame_alloc();
- avFrame->format = frameEncoder->sourceFormat();
- avFrame->width = size.width();
- avFrame->height = size.height();
- av_frame_get_buffer(avFrame, 0);
-
- for (int i = 0; i < 4; ++i) {
- avFrame->data[i] = const_cast<uint8_t *>(frame.bits(i));
- avFrame->linesize[i] = frame.bytesPerLine(i);
- }
-
- QImage img;
- if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
- // the QImage is cached inside the video frame, so we can take the pointer to the image data here
- img = frame.toImage();
- avFrame->data[0] = (uint8_t *)img.bits();
- avFrame->linesize[0] = img.bytesPerLine();
- }
-
- Q_ASSERT(avFrame->data[0]);
- // ensure the video frame and it's data is alive as long as it's being used in the encoder
- avFrame->opaque_ref = av_buffer_create(nullptr, 0, freeQVideoFrame, new QVideoFrameHolder{frame, img}, 0);
- }
-
- if (baseTime.loadAcquire() < 0) {
- baseTime.storeRelease(frame.startTime() - lastFrameTime);
-// qCDebug(qLcFFmpegEncoder) << ">>>> adjusting base time to" << baseTime.loadAcquire() << frame.startTime() << lastFrameTime;
- }
-
- qint64 time = frame.startTime() - baseTime.loadAcquire();
- lastFrameTime = frame.endTime() - baseTime.loadAcquire();
- avFrame->pts = frameEncoder->getPts(time);
-
- encoder->newTimeStamp(time/1000);
-
-// qCDebug(qLcFFmpegEncoder) << ">>> sending frame" << avFrame->pts << time;
- int ret = frameEncoder->sendFrame(avFrame);
- if (ret < 0) {
- qCDebug(qLcFFmpegEncoder) << "error sending frame" << ret << err2str(ret);
- encoder->error(QMediaRecorder::ResourceError, err2str(ret));
- }
-}
-
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h
deleted file mode 100644
index cc4503c74..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegencoder_p.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QFFMPEGENCODER_P_H
-#define QFFMPEGENCODER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qffmpegthread_p.h"
-#include "qffmpeg_p.h"
-#include "qffmpeghwaccel_p.h"
-
-#include <private/qplatformmediarecorder_p.h>
-#include <qaudioformat.h>
-#include <qaudiobuffer.h>
-
-#include <qqueue.h>
-
-QT_BEGIN_NAMESPACE
-
-class QFFmpegAudioInput;
-class QVideoFrame;
-class QPlatformCamera;
-
-namespace QFFmpeg
-{
-
-class Encoder;
-class Muxer;
-class AudioEncoder;
-class VideoEncoder;
-class VideoFrameEncoder;
-
-class EncodingFinalizer : public QThread
-{
-public:
- EncodingFinalizer(Encoder *e)
- : encoder(e)
- {}
- void run() override;
-
- Encoder *encoder = nullptr;
-};
-
-class Encoder : public QObject
-{
- Q_OBJECT
-public:
- Encoder(const QMediaEncoderSettings &settings, const QUrl &url);
- ~Encoder();
-
- void addAudioInput(QFFmpegAudioInput *input);
- void addVideoSource(QPlatformCamera *source);
-
- void start();
- void finalize();
-
- void setPaused(bool p);
-
- void setMetaData(const QMediaMetaData &metaData);
-
-public Q_SLOTS:
- void newAudioBuffer(const QAudioBuffer &buffer);
- void newVideoFrame(const QVideoFrame &frame);
- void newTimeStamp(qint64 time);
-
-Q_SIGNALS:
- void durationChanged(qint64 duration);
- void error(QMediaRecorder::Error code, const QString &description);
- void finalizationDone();
-
-public:
-
- QMediaEncoderSettings settings;
- QMediaMetaData metaData;
- AVFormatContext *formatContext = nullptr;
- Muxer *muxer = nullptr;
- bool isRecording = false;
-
- AudioEncoder *audioEncode = nullptr;
- VideoEncoder *videoEncode = nullptr;
-
- QMutex timeMutex;
- qint64 timeRecorded = 0;
-};
-
-
-class Muxer : public Thread
-{
- mutable QMutex queueMutex;
- QQueue<AVPacket *> packetQueue;
-public:
- Muxer(Encoder *encoder);
-
- void addPacket(AVPacket *);
-
-private:
- AVPacket *takePacket();
-
- void init() override;
- void cleanup() override;
- bool shouldWait() const override;
- void loop() override;
-
- Encoder *encoder;
-};
-
-class EncoderThread : public Thread
-{
-public:
- virtual void setPaused(bool b)
- {
- paused.storeRelease(b);
- }
-
-protected:
- QAtomicInteger<bool> paused = false;
- Encoder *encoder = nullptr;
-};
-
-class AudioEncoder : public EncoderThread
-{
- mutable QMutex queueMutex;
- QQueue<QAudioBuffer> audioBufferQueue;
-public:
- AudioEncoder(Encoder *encoder, QFFmpegAudioInput *input, const QMediaEncoderSettings &settings);
-
- void addBuffer(const QAudioBuffer &buffer);
-
- QFFmpegAudioInput *audioInput() const { return input; }
-
-private:
- QAudioBuffer takeBuffer();
- void retrievePackets();
-
- void init() override;
- void cleanup() override;
- bool shouldWait() const override;
- void loop() override;
-
- AVStream *stream = nullptr;
- AVCodecContext *codec = nullptr;
- QFFmpegAudioInput *input;
- QAudioFormat format;
-
- SwrContext *resampler = nullptr;
- qint64 samplesWritten = 0;
-};
-
-
-class VideoEncoder : public EncoderThread
-{
- mutable QMutex queueMutex;
- QQueue<QVideoFrame> videoFrameQueue;
-public:
- VideoEncoder(Encoder *encoder, QPlatformCamera *camera, const QMediaEncoderSettings &settings);
- ~VideoEncoder();
-
- void addFrame(const QVideoFrame &frame);
-
- void setPaused(bool b) override
- {
- EncoderThread::setPaused(b);
- if (b)
- baseTime.storeRelease(-1);
- }
-
-private:
- QVideoFrame takeFrame();
- void retrievePackets();
-
- void init() override;
- void cleanup() override;
- bool shouldWait() const override;
- void loop() override;
-
- QMediaEncoderSettings m_encoderSettings;
- QPlatformCamera *m_camera = nullptr;
- VideoFrameEncoder *frameEncoder = nullptr;
-
- QAtomicInteger<qint64> baseTime = -1;
- qint64 lastFrameTime = 0;
-};
-
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions_p.h b/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions_p.h
deleted file mode 100644
index f6e0907c6..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions_p.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QFFMPEGENCODEROPTIONS_P_H
-#define QFFMPEGENCODEROPTIONS_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qffmpeghwaccel_p.h"
-#include "qvideoframeformat.h"
-#include "private/qplatformmediarecorder_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QFFmpeg {
-
-void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts);
-void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts);
-
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencodingformatcontext.cpp b/src/plugins/multimedia/ffmpeg/qffmpegencodingformatcontext.cpp
new file mode 100644
index 000000000..8b367a822
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegencodingformatcontext.cpp
@@ -0,0 +1,116 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegencodingformatcontext_p.h"
+#include "qffmpegmediaformatinfo_p.h"
+#include "qffmpegioutils_p.h"
+#include "qfile.h"
+#include "QtCore/qloggingcategory.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+static Q_LOGGING_CATEGORY(qLcEncodingFormatContext, "qt.multimedia.ffmpeg.encodingformatcontext");
+
+namespace {
+// In the example https://ffmpeg.org/doxygen/trunk/avio_read_callback_8c-example.html,
+// BufferSize = 4096 is suggested, however, it might be not optimal. To be investigated.
+constexpr size_t DefaultBufferSize = 4096;
+} // namespace
+
+EncodingFormatContext::EncodingFormatContext(QMediaFormat::FileFormat fileFormat)
+ : m_avFormatContext(avformat_alloc_context())
+{
+ const AVOutputFormat *avFormat = QFFmpegMediaFormatInfo::outputFormatForFileFormat(fileFormat);
+ m_avFormatContext->oformat = const_cast<AVOutputFormat *>(avFormat); // constness varies
+}
+
+EncodingFormatContext::~EncodingFormatContext()
+{
+ closeAVIO();
+
+ avformat_free_context(m_avFormatContext);
+}
+
+void EncodingFormatContext::openAVIO(const QString &filePath)
+{
+ Q_ASSERT(!isAVIOOpen());
+ Q_ASSERT(!filePath.isEmpty());
+
+ const QByteArray filePathUtf8 = filePath.toUtf8();
+
+ std::unique_ptr<char, decltype(&av_free)> url(
+ reinterpret_cast<char *>(av_malloc(filePathUtf8.size() + 1)), &av_free);
+ memcpy(url.get(), filePathUtf8.constData(), filePathUtf8.size() + 1);
+
+ // Initialize the AVIOContext for accessing the resource indicated by the url
+ auto result = avio_open2(&m_avFormatContext->pb, url.get(), AVIO_FLAG_WRITE, nullptr, nullptr);
+
+ qCDebug(qLcEncodingFormatContext)
+ << "opened by file path:" << url.get() << ", result:" << result;
+
+ Q_ASSERT(m_avFormatContext->url == nullptr);
+ if (isAVIOOpen())
+ m_avFormatContext->url = url.release();
+ else
+ openAVIOWithQFile(filePath);
+}
+
+void EncodingFormatContext::openAVIOWithQFile(const QString &filePath)
+{
+ // QTBUG-123082, To be investigated:
+ // - should we use the logic with QFile for all file paths?
+ // - does avio_open2 handle network protocols that QFile doesn't?
+ // - which buffer size should we set to opening with QFile to ensure the best performance?
+
+ auto file = std::make_unique<QFile>(filePath);
+
+ if (!file->open(QFile::WriteOnly)) {
+ qCDebug(qLcEncodingFormatContext) << "Cannot open QFile" << filePath;
+ return;
+ }
+
+ openAVIO(file.get());
+
+ if (isAVIOOpen())
+ m_outputFile = std::move(file);
+}
+
+void EncodingFormatContext::openAVIO(QIODevice *device)
+{
+ Q_ASSERT(!isAVIOOpen());
+ Q_ASSERT(device);
+
+ if (!device->isWritable())
+ return;
+
+ auto buffer = static_cast<uint8_t *>(av_malloc(DefaultBufferSize));
+ m_avFormatContext->pb = avio_alloc_context(buffer, DefaultBufferSize, 1, device, nullptr,
+ &writeQIODevice, &seekQIODevice);
+}
+
+void EncodingFormatContext::closeAVIO()
+{
+ // Close the AVIOContext and release any file handles
+ if (isAVIOOpen()) {
+ if (m_avFormatContext->url && *m_avFormatContext->url != '\0') {
+ auto closeResult = avio_closep(&m_avFormatContext->pb);
+ Q_ASSERT(closeResult == 0);
+ } else {
+ av_free(std::exchange(m_avFormatContext->pb->buffer, nullptr));
+ avio_context_free(&m_avFormatContext->pb);
+ }
+
+ // delete url even though it might be delete by avformat_free_context to
+ // ensure consistency in openAVIO/closeAVIO.
+ av_freep(&m_avFormatContext->url);
+ m_outputFile.reset();
+ } else {
+ Q_ASSERT(!m_outputFile);
+ }
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencodingformatcontext_p.h b/src/plugins/multimedia/ffmpeg/qffmpegencodingformatcontext_p.h
new file mode 100644
index 000000000..69d0cd873
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegencodingformatcontext_p.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGENCODINGFORMATCONTEXT_P_H
+#define QFFMPEGENCODINGFORMATCONTEXT_P_H
+
+#include "qffmpegdefs_p.h"
+#include "qmediaformat.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+class QFile;
+
+namespace QFFmpeg {
+
+class EncodingFormatContext
+{
+public:
+ explicit EncodingFormatContext(QMediaFormat::FileFormat fileFormat);
+ ~EncodingFormatContext();
+
+ void openAVIO(const QString &filePath);
+
+ void openAVIO(QIODevice *device);
+
+ bool isAVIOOpen() const { return m_avFormatContext->pb != nullptr; }
+
+ void closeAVIO();
+
+ AVFormatContext *avFormatContext() { return m_avFormatContext; }
+
+ const AVFormatContext *avFormatContext() const { return m_avFormatContext; }
+
+private:
+ Q_DISABLE_COPY_MOVE(EncodingFormatContext)
+
+ void openAVIOWithQFile(const QString &filePath);
+
+private:
+ AVFormatContext *m_avFormatContext;
+ std::unique_ptr<QFile> m_outputFile;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGENCODINGFORMATCONTEXT_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp
index e28a89ad8..06bd4f4d3 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp
@@ -1,41 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "libavutil/version.h"
#include "qffmpeghwaccel_p.h"
#if QT_CONFIG(vaapi)
@@ -46,161 +12,347 @@
#endif
#if QT_CONFIG(wmf)
#include "qffmpeghwaccel_d3d11_p.h"
+#include <QtCore/private/qsystemlibrary_p.h>
+
+#endif
+#ifdef Q_OS_ANDROID
+# include "qffmpeghwaccel_mediacodec_p.h"
#endif
#include "qffmpeg_p.h"
#include "qffmpegvideobuffer_p.h"
-
-#include <private/qrhi_p.h>
-#include <qdebug.h>
+#include "qscopedvaluerollback.h"
+#include "QtCore/qfile.h"
+
+#include <rhi/qrhi.h>
+#include <qloggingcategory.h>
+#include <unordered_set>
+#ifdef Q_OS_LINUX
+#include <QLibrary>
+#endif
/* Infrastructure for HW acceleration goes into this file. */
QT_BEGIN_NAMESPACE
-namespace QFFmpeg {
+static Q_LOGGING_CATEGORY(qLHWAccel, "qt.multimedia.ffmpeg.hwaccel");
+extern bool thread_local FFmpegLogsEnabledInThread;
-// HW context initialization
+namespace QFFmpeg {
-// preferred order of HW accelerators to use
-static const AVHWDeviceType preferredHardwareAccelerators[] = {
-// Linux/Unix
-#if defined(Q_OS_LINUX)
+static const std::initializer_list<AVHWDeviceType> preferredHardwareAccelerators = {
+#if defined(Q_OS_ANDROID)
+ AV_HWDEVICE_TYPE_MEDIACODEC,
+#elif defined(Q_OS_LINUX)
+ AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_VAAPI,
-// AV_HWDEVICE_TYPE_DRM,
+
+ // TODO: investigate VDPAU advantages.
+ // nvenc/nvdec codecs use AV_HWDEVICE_TYPE_CUDA by default, but they can also use VDPAU
+ // if it's included into the ffmpeg build and vdpau drivers are installed.
+ // AV_HWDEVICE_TYPE_VDPAU
#elif defined (Q_OS_WIN)
AV_HWDEVICE_TYPE_D3D11VA,
#elif defined (Q_OS_DARWIN)
AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
-#elif defined (Q_OS_ANDROID)
- AV_HWDEVICE_TYPE_MEDIACODEC,
#endif
- AV_HWDEVICE_TYPE_NONE
};
-static AVBufferRef *loadHWContext(const AVHWDeviceType type)
+static AVBufferUPtr loadHWContext(AVHWDeviceType type)
{
AVBufferRef *hwContext = nullptr;
+ qCDebug(qLHWAccel) << " Checking HW context:" << av_hwdevice_get_type_name(type);
int ret = av_hwdevice_ctx_create(&hwContext, type, nullptr, nullptr, 0);
- qDebug() << " Checking HW context:" << av_hwdevice_get_type_name(type);
+
if (ret == 0) {
- qDebug() << " Using above hw context.";
- return hwContext;
+ qCDebug(qLHWAccel) << " Using above hw context.";
+ return AVBufferUPtr(hwContext);
}
- qDebug() << " Could not create hw context:" << ret << strerror(-ret);
+ qCDebug(qLHWAccel) << " Could not create hw context:" << ret << strerror(-ret);
return nullptr;
}
-static AVBufferRef *hardwareContextForCodec(const AVCodec *codec)
+// FFmpeg might crash on loading non-existing hw devices.
+// Let's roughly precheck drivers/libraries.
+static bool precheckDriver(AVHWDeviceType type)
{
- qDebug() << "Checking HW acceleration for decoder" << codec->name;
-
- // First try our preferred accelerators. Those are the ones where we can
- // set up a zero copy pipeline
- auto *preferred = preferredHardwareAccelerators;
- while (*preferred != AV_HWDEVICE_TYPE_NONE) {
- for (int i = 0;; ++i) {
- const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i);
- if (!config)
- break;
- if (config->device_type == *preferred) {
- auto *hwContext = loadHWContext(config->device_type);
- if (hwContext)
- return hwContext;
- break;
- }
- }
- ++preferred;
+ // precheckings might need some improvements
+#if defined(Q_OS_LINUX)
+ if (type == AV_HWDEVICE_TYPE_CUDA) {
+ if (!QFile::exists(QLatin1String("/proc/driver/nvidia/version")))
+ return false;
+
+ // QTBUG-122199
+ // CUDA backend requires libnvcuvid in libavcodec
+ QLibrary lib("libnvcuvid.so");
+ if (!lib.load())
+ return false;
+ lib.unload();
+ return true;
}
+#elif defined(Q_OS_WINDOWS)
+ if (type == AV_HWDEVICE_TYPE_D3D11VA)
+ return QSystemLibrary(QLatin1String("d3d11.dll")).load();
- // Ok, let's see if we can get any HW acceleration at all. It'll still involve one buffer copy,
- // as we can't move the data into RHI textures without a CPU copy
- for (int i = 0;; ++i) {
- const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i);
- if (!config)
- break;
+#if QT_FFMPEG_HAS_D3D12VA
+ if (type == AV_HWDEVICE_TYPE_D3D12VA)
+ return QSystemLibrary(QLatin1String("d3d12.dll")).load();
+#endif
- auto *hwContext = loadHWContext(config->device_type);
- if (hwContext)
- return hwContext;
- }
- qDebug() << " No HW accelerators found, using SW decoding.";
- return nullptr;
+ if (type == AV_HWDEVICE_TYPE_DXVA2)
+ return QSystemLibrary(QLatin1String("d3d9.dll")).load();
+ // TODO: check nvenc/nvdec and revisit the checking
+ if (type == AV_HWDEVICE_TYPE_CUDA)
+ return QSystemLibrary(QLatin1String("nvml.dll")).load();
+#else
+ Q_UNUSED(type);
+#endif
+
+ return true;
}
-// Used for the AVCodecContext::get_format callback
-AVPixelFormat getFormat(AVCodecContext *s, const AVPixelFormat *fmt)
+static bool checkHwType(AVHWDeviceType type)
{
- // First check HW accelerated codecs, the HW device context must be set
- if (s->hw_device_ctx && s->codec->hw_configs) {
- auto *device_ctx = (AVHWDeviceContext*)s->hw_device_ctx->data;
- for (int i = 0; const AVCodecHWConfig *config = avcodec_get_hw_config(s->codec, i); i++) {
- if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
- continue;
- if (device_ctx->type != config->device_type)
- continue;
- for (int n = 0; fmt[n] != AV_PIX_FMT_NONE; n++) {
- if (config->pix_fmt == fmt[n]) {
-#if QT_CONFIG(wmf)
- if (fmt[n] == AV_PIX_FMT_D3D11)
- QFFmpeg::D3D11TextureConverter::SetupDecoderTextures(s);
-#endif
- return fmt[n];
- }
- }
- }
+ const auto deviceName = av_hwdevice_get_type_name(type);
+ if (!deviceName) {
+ qWarning() << "Internal FFmpeg error, unknow hw type:" << type;
+ return false;
}
- // prefer video formats we can handle directly
- for (int n = 0; fmt[n] != AV_PIX_FMT_NONE; n++) {
- bool needsConversion = true;
- QFFmpegVideoBuffer::toQtPixelFormat(fmt[n], &needsConversion);
- if (!needsConversion)
- return fmt[n];
+ if (!precheckDriver(type)) {
+ qCDebug(qLHWAccel) << "Drivers for hw device" << deviceName << "is not installed";
+ return false;
}
- // take the native format, this will involve one additional format conversion on the CPU side
- return *fmt;
+ if (type == AV_HWDEVICE_TYPE_MEDIACODEC ||
+ type == AV_HWDEVICE_TYPE_VIDEOTOOLBOX ||
+ type == AV_HWDEVICE_TYPE_D3D11VA ||
+#if QT_FFMPEG_HAS_D3D12VA
+ type == AV_HWDEVICE_TYPE_D3D12VA ||
+#endif
+ type == AV_HWDEVICE_TYPE_DXVA2)
+ return true; // Don't waste time; it's expected to work fine of the precheck is OK
+
+
+ QScopedValueRollback rollback(FFmpegLogsEnabledInThread);
+ FFmpegLogsEnabledInThread = false;
+
+ return loadHWContext(type) != nullptr;
}
-TextureConverter::Data::~Data()
+static const std::vector<AVHWDeviceType> &deviceTypes()
{
- delete backend;
+ static const auto types = []() {
+ qCDebug(qLHWAccel) << "Check device types";
+ QElapsedTimer timer;
+ timer.start();
+
+ // gather hw pix formats
+ std::unordered_set<AVPixelFormat> hwPixFormats;
+ void *opaque = nullptr;
+ while (auto codec = av_codec_iterate(&opaque)) {
+ if (auto pixFmt = codec->pix_fmts)
+ for (; *pixFmt != AV_PIX_FMT_NONE; ++pixFmt)
+ if (isHwPixelFormat(*pixFmt))
+ hwPixFormats.insert(*pixFmt);
+ }
+
+ // create a device types list
+ std::vector<AVHWDeviceType> result;
+ AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
+ while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)
+ if (hwPixFormats.count(pixelFormatForHwDevice(type)) && checkHwType(type))
+ result.push_back(type);
+ result.shrink_to_fit();
+
+ // reorder the list accordingly preferredHardwareAccelerators
+ auto it = result.begin();
+ for (const auto preffered : preferredHardwareAccelerators) {
+ auto found = std::find(it, result.end(), preffered);
+ if (found != result.end())
+ std::rotate(it++, found, std::next(found));
+ }
+
+ using namespace std::chrono;
+ qCDebug(qLHWAccel) << "Device types checked. Spent time:" << duration_cast<microseconds>(timer.durationElapsed());
+
+ return result;
+ }();
+
+ return types;
}
+static std::vector<AVHWDeviceType> deviceTypes(const char *envVarName)
+{
+ const auto definedDeviceTypes = qgetenv(envVarName);
+
+ if (definedDeviceTypes.isNull())
+ return deviceTypes();
+
+ std::vector<AVHWDeviceType> result;
+ const auto definedDeviceTypesString = QString::fromUtf8(definedDeviceTypes).toLower();
+ for (const auto &deviceType : definedDeviceTypesString.split(',')) {
+ if (!deviceType.isEmpty()) {
+ const auto foundType = av_hwdevice_find_type_by_name(deviceType.toUtf8().data());
+ if (foundType == AV_HWDEVICE_TYPE_NONE)
+ qWarning() << "Unknown hw device type" << deviceType;
+ else
+ result.emplace_back(foundType);
+ }
+ }
+ result.shrink_to_fit();
+ return result;
+}
-HWAccel::Data::~Data()
+template<typename CodecFinder>
+std::pair<const AVCodec *, std::unique_ptr<HWAccel>>
+findCodecWithHwAccel(AVCodecID id, const std::vector<AVHWDeviceType> &deviceTypes,
+ CodecFinder codecFinder,
+ const std::function<bool(const HWAccel &)> &hwAccelPredicate)
{
- if (hwDeviceContext)
- av_buffer_unref(&hwDeviceContext);
- if (hwFramesContext)
- av_buffer_unref(&hwFramesContext);
+ for (auto type : deviceTypes) {
+ const auto codec = codecFinder(id, type, {});
+
+ if (!codec)
+ continue;
+
+ qCDebug(qLHWAccel) << "Found potential codec" << codec->name << "for hw accel" << type
+ << "; Checking the hw device...";
+
+ auto hwAccel = QFFmpeg::HWAccel::create(type);
+
+ if (!hwAccel)
+ continue;
+
+ if (hwAccelPredicate && !hwAccelPredicate(*hwAccel)) {
+ qCDebug(qLHWAccel) << "HW device is available but doesn't suit due to restrictions";
+ continue;
+ }
+
+ qCDebug(qLHWAccel) << "HW device is OK";
+
+ return { codec, std::move(hwAccel) };
+ }
+
+ qCDebug(qLHWAccel) << "No hw acceleration found for codec id" << id;
+
+ return { nullptr, nullptr };
}
+static bool isNoConversionFormat(AVPixelFormat f)
+{
+ bool needsConversion = true;
+ QFFmpegVideoBuffer::toQtPixelFormat(f, &needsConversion);
+ return !needsConversion;
+};
-HWAccel::HWAccel(const AVCodec *codec)
+namespace {
+
+bool hwTextureConversionEnabled()
{
- if (codec->type != AVMEDIA_TYPE_VIDEO)
- return;
- auto *ctx = hardwareContextForCodec(codec);
- if (!ctx)
- return;
- d = new Data;
- d->hwDeviceContext = ctx;
+
+ // HW textures conversions are not stable in specific cases, dependent on the hardware and OS.
+ // We need the env var for testing with no textures conversion on the user's side.
+ static const int disableHwConversion =
+ qEnvironmentVariableIntValue("QT_DISABLE_HW_TEXTURES_CONVERSION");
+
+ return !disableHwConversion;
}
-HWAccel::HWAccel(AVHWDeviceType deviceType)
+void setupDecoder(const AVPixelFormat format, AVCodecContext *const codecContext)
{
- auto *ctx = loadHWContext(deviceType);
- if (!ctx)
+ if (!hwTextureConversionEnabled())
return;
- d = new Data;
- d->hwDeviceContext = ctx;
+
+#if QT_CONFIG(wmf)
+ if (format == AV_PIX_FMT_D3D11)
+ QFFmpeg::D3D11TextureConverter::SetupDecoderTextures(codecContext);
+#elif defined Q_OS_ANDROID
+ if (format == AV_PIX_FMT_MEDIACODEC)
+ QFFmpeg::MediaCodecTextureConverter::setupDecoderSurface(codecContext);
+#else
+ Q_UNUSED(codecContext);
+ Q_UNUSED(format);
+#endif
+}
+
+} // namespace
+
+// Used for the AVCodecContext::get_format callback
+AVPixelFormat getFormat(AVCodecContext *codecContext, const AVPixelFormat *suggestedFormats)
+{
+ // First check HW accelerated codecs, the HW device context must be set
+ if (codecContext->hw_device_ctx) {
+ auto *device_ctx = (AVHWDeviceContext *)codecContext->hw_device_ctx->data;
+ std::pair formatAndScore(AV_PIX_FMT_NONE, NotSuitableAVScore);
+
+ // to be rewritten via findBestAVFormat
+ for (int i = 0;
+ const AVCodecHWConfig *config = avcodec_get_hw_config(codecContext->codec, i); i++) {
+ if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
+ continue;
+
+ if (device_ctx->type != config->device_type)
+ continue;
+
+ const bool isDeprecated = (config->methods & AV_CODEC_HW_CONFIG_METHOD_AD_HOC) != 0;
+ const bool shouldCheckCodecFormats = config->pix_fmt == AV_PIX_FMT_NONE;
+
+ auto scoresGettor = [&](AVPixelFormat format) {
+ if (shouldCheckCodecFormats && !isAVFormatSupported(codecContext->codec, format))
+ return NotSuitableAVScore;
+
+ if (!shouldCheckCodecFormats && config->pix_fmt != format)
+ return NotSuitableAVScore;
+
+ auto result = DefaultAVScore;
+
+ if (isDeprecated)
+ result -= 10000;
+ if (isHwPixelFormat(format))
+ result += 10;
+
+ return result;
+ };
+
+ const auto found = findBestAVFormat(suggestedFormats, scoresGettor);
+
+ if (found.second > formatAndScore.second)
+ formatAndScore = found;
+ }
+
+ const auto &format = formatAndScore.first;
+ if (format != AV_PIX_FMT_NONE) {
+ setupDecoder(format, codecContext);
+ qCDebug(qLHWAccel) << "Selected format" << format << "for hw" << device_ctx->type;
+ return format;
+ }
+ }
+
+ // prefer video formats we can handle directly
+ const auto noConversionFormat = findAVFormat(suggestedFormats, &isNoConversionFormat);
+ if (noConversionFormat != AV_PIX_FMT_NONE) {
+ qCDebug(qLHWAccel) << "Selected format with no conversion" << noConversionFormat;
+ return noConversionFormat;
+ }
+
+ qCDebug(qLHWAccel) << "Selected format with conversion" << *suggestedFormats;
+
+ // take the native format, this will involve one additional format conversion on the CPU side
+ return *suggestedFormats;
}
HWAccel::~HWAccel() = default;
+std::unique_ptr<HWAccel> HWAccel::create(AVHWDeviceType deviceType)
+{
+ if (auto ctx = loadHWContext(deviceType))
+ return std::unique_ptr<HWAccel>(new HWAccel(std::move(ctx)));
+ else
+ return {};
+}
+
AVPixelFormat HWAccel::format(AVFrame *frame)
{
if (!frame->hw_frames_ctx)
@@ -211,130 +363,85 @@ AVPixelFormat HWAccel::format(AVFrame *frame)
return AVPixelFormat(hwFramesContext->sw_format);
}
-const AVHWDeviceType *HWAccel::preferredDeviceTypes()
+const std::vector<AVHWDeviceType> &HWAccel::encodingDeviceTypes()
+{
+ static const auto &result = deviceTypes("QT_FFMPEG_ENCODING_HW_DEVICE_TYPES");
+ return result;
+}
+
+const std::vector<AVHWDeviceType> &HWAccel::decodingDeviceTypes()
{
- return preferredHardwareAccelerators;
+ static const auto &result = deviceTypes("QT_FFMPEG_DECODING_HW_DEVICE_TYPES");
+ return result;
}
AVHWDeviceContext *HWAccel::hwDeviceContext() const
{
- if (!d || !d->hwDeviceContext)
- return nullptr;
- return (AVHWDeviceContext *)d->hwDeviceContext->data;
+ return m_hwDeviceContext ? (AVHWDeviceContext *)m_hwDeviceContext->data : nullptr;
}
AVPixelFormat HWAccel::hwFormat() const
{
- switch (deviceType()) {
- case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
- return AV_PIX_FMT_VIDEOTOOLBOX;
- case AV_HWDEVICE_TYPE_VAAPI:
- return AV_PIX_FMT_VAAPI;
- default:
- return AV_PIX_FMT_NONE;
- }
+ return pixelFormatForHwDevice(deviceType());
}
-const AVCodec *HWAccel::hardwareEncoderForCodecId(AVCodecID id) const
+const AVHWFramesConstraints *HWAccel::constraints() const
{
- const char *codec = nullptr;
- switch (deviceType()) {
-#ifdef Q_OS_DARWIN
- case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
- switch (id) {
- case AV_CODEC_ID_H264:
- codec = "h264_videotoolbox";
- break;
- case AV_CODEC_ID_HEVC:
- codec = "hevc_videotoolbox";
- break;
- case AV_CODEC_ID_PRORES:
- codec = "prores_videotoolbox";
- break;
- case AV_CODEC_ID_VP9:
- codec = "vp9_videotoolbox";
- break;
- default:
- break;
- }
- break;
-#endif
- case AV_HWDEVICE_TYPE_VAAPI:
- switch (id) {
- case AV_CODEC_ID_H264:
- codec = "h264_vaapi";
- break;
- case AV_CODEC_ID_HEVC:
- codec = "hevc_vaapi";
- break;
- case AV_CODEC_ID_MJPEG:
- codec = "mjpeg_vaapi";
- break;
- case AV_CODEC_ID_MPEG2VIDEO:
- codec = "mpeg2_vaapi";
- break;
- case AV_CODEC_ID_VP8:
- codec = "vp8_vaapi";
- break;
- case AV_CODEC_ID_VP9:
- codec = "vp9_vaapi";
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- if (!codec)
- return nullptr;
- const AVCodec *c = avcodec_find_encoder_by_name(codec);
- qDebug() << "searching for HW codec" << codec << "got" << c;
- return c;
+ std::call_once(m_constraintsOnceFlag, [this]() {
+ if (auto context = hwDeviceContextAsBuffer())
+ m_constraints.reset(av_hwdevice_get_hwframe_constraints(context, nullptr));
+ });
+
+ return m_constraints.get();
}
-HWAccel HWAccel::findHardwareAccelForCodecID(AVCodecID id)
+std::pair<const AVCodec *, std::unique_ptr<HWAccel>>
+HWAccel::findEncoderWithHwAccel(AVCodecID id, const std::function<bool(const HWAccel &)>& hwAccelPredicate)
{
- auto *accels = preferredHardwareAccelerators;
- while (*accels != AV_HWDEVICE_TYPE_NONE) {
- auto accel = HWAccel(*accels);
- if (accel.hardwareEncoderForCodecId(id) != nullptr)
- return accel;
- ++accels;
- }
- return {};
+ auto finder = qOverload<AVCodecID, const std::optional<AVHWDeviceType> &,
+ const std::optional<PixelOrSampleFormat> &>(&QFFmpeg::findAVEncoder);
+ return findCodecWithHwAccel(id, encodingDeviceTypes(), finder, hwAccelPredicate);
+}
+
+std::pair<const AVCodec *, std::unique_ptr<HWAccel>>
+HWAccel::findDecoderWithHwAccel(AVCodecID id, const std::function<bool(const HWAccel &)>& hwAccelPredicate)
+{
+ return findCodecWithHwAccel(id, decodingDeviceTypes(), &QFFmpeg::findAVDecoder,
+ hwAccelPredicate);
}
AVHWDeviceType HWAccel::deviceType() const
{
- if (!d || !d->hwDeviceContext)
- return AV_HWDEVICE_TYPE_NONE;
- return hwDeviceContext()->type;
+ return m_hwDeviceContext ? hwDeviceContext()->type : AV_HWDEVICE_TYPE_NONE;
}
void HWAccel::createFramesContext(AVPixelFormat swFormat, const QSize &size)
{
- if (!d || !d->hwDeviceContext)
+ if (m_hwFramesContext) {
+ qWarning() << "Frames context has been already created!";
+ return;
+ }
+
+ if (!m_hwDeviceContext)
return;
- d->hwFramesContext = av_hwframe_ctx_alloc(d->hwDeviceContext);
- auto *c = (AVHWFramesContext *)d->hwFramesContext->data;
+
+ m_hwFramesContext.reset(av_hwframe_ctx_alloc(m_hwDeviceContext.get()));
+ auto *c = (AVHWFramesContext *)m_hwFramesContext->data;
c->format = hwFormat();
c->sw_format = swFormat;
c->width = size.width();
c->height = size.height();
- qDebug() << "init frames context";
- int err = av_hwframe_ctx_init(d->hwFramesContext);
+ qCDebug(qLHWAccel) << "init frames context";
+ int err = av_hwframe_ctx_init(m_hwFramesContext.get());
if (err < 0)
qWarning() << "failed to init HW frame context" << err << err2str(err);
else
- qDebug() << "Initialized frames context" << size << c->format << c->sw_format;
+ qCDebug(qLHWAccel) << "Initialized frames context" << size << c->format << c->sw_format;
}
AVHWFramesContext *HWAccel::hwFramesContext() const
{
- if (!d || !d->hwFramesContext)
- return nullptr;
- return (AVHWFramesContext *)d->hwFramesContext->data;
+ return m_hwFramesContext ? (AVHWFramesContext *)m_hwFramesContext->data : nullptr;
}
@@ -358,20 +465,29 @@ void TextureConverter::updateBackend(AVPixelFormat fmt)
d->backend = nullptr;
if (!d->rhi)
return;
+
+ if (!hwTextureConversionEnabled())
+ return;
+
switch (fmt) {
#if QT_CONFIG(vaapi)
case AV_PIX_FMT_VAAPI:
- d->backend = new VAAPITextureConverter(d->rhi);
+ d->backend = std::make_unique<VAAPITextureConverter>(d->rhi);
break;
#endif
#ifdef Q_OS_DARWIN
case AV_PIX_FMT_VIDEOTOOLBOX:
- d->backend = new VideoToolBoxTextureConverter(d->rhi);
+ d->backend = std::make_unique<VideoToolBoxTextureConverter>(d->rhi);
break;
#endif
#if QT_CONFIG(wmf)
case AV_PIX_FMT_D3D11:
- d->backend = new D3D11TextureConverter(d->rhi);
+ d->backend = std::make_unique<D3D11TextureConverter>(d->rhi);
+ break;
+#endif
+#ifdef Q_OS_ANDROID
+ case AV_PIX_FMT_MEDIACODEC:
+ d->backend = std::make_unique<MediaCodecTextureConverter>(d->rhi);
break;
#endif
default:
@@ -380,11 +496,6 @@ void TextureConverter::updateBackend(AVPixelFormat fmt)
d->format = fmt;
}
-std::unique_ptr<QRhiTexture> TextureSet::texture(int /*plane*/)
-{
- return {};
-}
-
} // namespace QFFmpeg
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp
index 350545f8a..a2533a132 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11.cpp
@@ -1,161 +1,271 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpeghwaccel_d3d11_p.h"
+#include "playbackengine/qffmpegstreamdecoder_p.h"
#include <qvideoframeformat.h>
#include "qffmpegvideobuffer_p.h"
-
#include <private/qvideotexturehelper_p.h>
-#include <private/qrhi_p.h>
-#include <private/qrhid3d11_p.h>
+#include <private/qcomptr_p.h>
+#include <private/quniquehandle_p.h>
+
+#include <rhi/qrhi.h>
#include <qopenglfunctions.h>
#include <qdebug.h>
#include <qloggingcategory.h>
#include <libavutil/hwcontext_d3d11va.h>
+#include <d3d11_1.h>
+#include <dxgi1_2.h>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcMediaFFmpegHWAccel, "qt.multimedia.hwaccel")
+namespace {
+
+Q_LOGGING_CATEGORY(qLcMediaFFmpegHWAccel, "qt.multimedia.hwaccel");
+
+ComPtr<ID3D11Device1> GetD3DDevice(QRhi *rhi)
+{
+ const auto native = static_cast<const QRhiD3D11NativeHandles *>(rhi->nativeHandles());
+ if (!native)
+ return {};
+
+ const ComPtr<ID3D11Device> rhiDevice = static_cast<ID3D11Device *>(native->dev);
+ ComPtr<ID3D11Device1> dev1;
+ if (rhiDevice.As(&dev1) != S_OK)
+ return nullptr;
+
+ return dev1;
+}
+
+} // namespace
namespace QFFmpeg {
-class D3D11TextureSet : public TextureSet
+bool TextureBridge::copyToSharedTex(ID3D11Device *dev, ID3D11DeviceContext *ctx,
+ const ComPtr<ID3D11Texture2D> &tex, UINT index,
+ const QSize &frameSize)
{
-public:
- D3D11TextureSet(QRhi *rhi, QVideoFrameFormat::PixelFormat format, ID3D11Texture2D *tex, int index)
- : m_rhi(rhi)
- , m_format(format)
- , m_tex(tex)
- , m_index(index)
- {}
-
- ~D3D11TextureSet() override {
- if (m_tex)
- m_tex->Release();
+ if (!ensureSrcTex(dev, tex, frameSize))
+ return false;
+
+ // Flush to ensure that texture is fully updated before we share it.
+ ctx->Flush();
+
+ if (m_srcMutex->AcquireSync(m_srcKey, INFINITE) != S_OK)
+ return false;
+
+ const UINT width = static_cast<UINT>(frameSize.width());
+ const UINT height = static_cast<UINT>(frameSize.height());
+
+ // A crop box is needed because FFmpeg may have created textures
+ // that are bigger than the frame size to account for the decoder's
+ // surface alignment requirements.
+ const D3D11_BOX crop{ 0, 0, 0, width, height, 1 };
+ ctx->CopySubresourceRegion(m_srcTex.Get(), 0, 0, 0, 0, tex.Get(), index, &crop);
+
+ m_srcMutex->ReleaseSync(m_destKey);
+ return true;
+}
+
+ComPtr<ID3D11Texture2D> TextureBridge::copyFromSharedTex(const ComPtr<ID3D11Device1> &dev,
+ const ComPtr<ID3D11DeviceContext> &ctx)
+{
+ if (!ensureDestTex(dev))
+ return {};
+
+ if (m_destMutex->AcquireSync(m_destKey, INFINITE) != S_OK)
+ return {};
+
+ ctx->CopySubresourceRegion(m_outputTex.Get(), 0, 0, 0, 0, m_destTex.Get(), 0, nullptr);
+
+ m_destMutex->ReleaseSync(m_srcKey);
+
+ return m_outputTex;
+}
+
+bool TextureBridge::ensureDestTex(const ComPtr<ID3D11Device1> &dev)
+{
+ if (m_destDevice != dev) {
+ // Destination device changed. Recreate texture.
+ m_destTex = nullptr;
+ m_destDevice = dev;
}
- std::unique_ptr<QRhiTexture> texture(int plane) override {
- auto desc = QVideoTextureHelper::textureDescription(m_format);
- if (!m_tex || !m_rhi || !desc || plane >= desc->nplanes)
- return {};
+ if (m_destTex)
+ return true;
- D3D11_TEXTURE2D_DESC d3d11desc = {};
- m_tex->GetDesc(&d3d11desc);
+ if (m_destDevice->OpenSharedResource1(m_sharedHandle.get(), IID_PPV_ARGS(&m_destTex)) != S_OK)
+ return false;
- QSize planeSize(desc->widthForPlane(int(d3d11desc.Width), plane),
- desc->heightForPlane(int(d3d11desc.Height), plane));
+ CD3D11_TEXTURE2D_DESC desc{};
+ m_destTex->GetDesc(&desc);
- std::unique_ptr<QRhiTexture> tex(m_rhi->newTextureArray(desc->textureFormat[plane],
- int(d3d11desc.ArraySize),
- planeSize, 1, {}));
- if (tex) {
- tex->setArrayRange(m_index, 1);
- if (!tex->createFrom({quint64(m_tex), 0}))
- tex.reset();
- }
- return tex;
+ desc.MiscFlags = 0;
+ desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+
+ if (m_destDevice->CreateTexture2D(&desc, nullptr, m_outputTex.ReleaseAndGetAddressOf()) != S_OK)
+ return false;
+
+ if (m_destTex.As(&m_destMutex) != S_OK)
+ return false;
+
+ return true;
+}
+
+bool TextureBridge::ensureSrcTex(ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex, const QSize &frameSize)
+{
+ if (!isSrcInitialized(dev, tex, frameSize))
+ return recreateSrc(dev, tex, frameSize);
+
+ return true;
+}
+
+bool TextureBridge::isSrcInitialized(const ID3D11Device *dev,
+ const ComPtr<ID3D11Texture2D> &tex,
+ const QSize &frameSize) const
+{
+ if (!m_srcTex)
+ return false;
+
+ // Check if device has changed
+ ComPtr<ID3D11Device> texDevice;
+ m_srcTex->GetDevice(texDevice.GetAddressOf());
+ if (dev != texDevice.Get())
+ return false;
+
+ // Check if shared texture has correct size and format
+ CD3D11_TEXTURE2D_DESC inputDesc{};
+ tex->GetDesc(&inputDesc);
+
+ CD3D11_TEXTURE2D_DESC currentDesc{};
+ m_srcTex->GetDesc(&currentDesc);
+
+ if (inputDesc.Format != currentDesc.Format)
+ return false;
+
+ const UINT width = static_cast<UINT>(frameSize.width());
+ const UINT height = static_cast<UINT>(frameSize.height());
+
+ if (currentDesc.Width != width || currentDesc.Height != height)
+ return false;
+
+ return true;
+}
+
+bool TextureBridge::recreateSrc(ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex, const QSize &frameSize)
+{
+ m_sharedHandle.close();
+
+ CD3D11_TEXTURE2D_DESC desc{};
+ tex->GetDesc(&desc);
+
+ const UINT width = static_cast<UINT>(frameSize.width());
+ const UINT height = static_cast<UINT>(frameSize.height());
+
+ CD3D11_TEXTURE2D_DESC texDesc{ desc.Format, width, height };
+ texDesc.MipLevels = 1;
+ texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
+
+ if (dev->CreateTexture2D(&texDesc, nullptr, m_srcTex.ReleaseAndGetAddressOf()) != S_OK)
+ return false;
+
+ ComPtr<IDXGIResource1> res;
+ if (m_srcTex.As(&res) != S_OK)
+ return false;
+
+ const HRESULT hr =
+ res->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &m_sharedHandle);
+
+ if (hr != S_OK || !m_sharedHandle)
+ return false;
+
+ if (m_srcTex.As(&m_srcMutex) != S_OK || !m_srcMutex)
+ return false;
+
+ m_destTex = nullptr;
+ m_destMutex = nullptr;
+ return true;
+}
+
+class D3D11TextureSet : public TextureSet
+{
+public:
+ D3D11TextureSet(QRhi *rhi, ComPtr<ID3D11Texture2D> &&tex)
+ : m_owner{ rhi }, m_tex(std::move(tex))
+ {
+ }
+
+ qint64 textureHandle(QRhi *rhi, int /*plane*/) override
+ {
+ if (rhi != m_owner)
+ return 0u;
+ return reinterpret_cast<qint64>(m_tex.Get());
}
private:
- QRhi *m_rhi = nullptr;
- QVideoFrameFormat::PixelFormat m_format;
- ID3D11Texture2D *m_tex = nullptr;
- int m_index = 0;
+ QRhi *m_owner = nullptr;
+ ComPtr<ID3D11Texture2D> m_tex;
};
-
D3D11TextureConverter::D3D11TextureConverter(QRhi *rhi)
- : TextureConverterBackend(rhi)
+ : TextureConverterBackend(rhi), m_rhiDevice{ GetD3DDevice(rhi) }
{
+ if (!m_rhiDevice)
+ return;
+
+ m_rhiDevice->GetImmediateContext(m_rhiCtx.GetAddressOf());
}
TextureSet *D3D11TextureConverter::getTextures(AVFrame *frame)
{
- if (!frame || !frame->hw_frames_ctx || frame->format != AV_PIX_FMT_D3D11)
+ if (!m_rhiDevice)
return nullptr;
- auto *fCtx = (AVHWFramesContext *)frame->hw_frames_ctx->data;
- auto *ctx = fCtx->device_ctx;
- if (!ctx || ctx->type != AV_HWDEVICE_TYPE_D3D11VA)
+ if (!frame || !frame->hw_frames_ctx || frame->format != AV_PIX_FMT_D3D11)
return nullptr;
- auto nh = static_cast<const QRhiD3D11NativeHandles *>(rhi->nativeHandles());
- if (!nh)
- return nullptr;
+ const auto *fCtx = reinterpret_cast<AVHWFramesContext *>(frame->hw_frames_ctx->data);
+ const auto *ctx = fCtx->device_ctx;
- auto dev = reinterpret_cast<ID3D11Device *>(nh->dev);
- if (!dev)
+ if (!ctx || ctx->type != AV_HWDEVICE_TYPE_D3D11VA)
return nullptr;
- auto ffmpegTex = (ID3D11Texture2D *)frame->data[0];
- int index = (intptr_t)frame->data[1];
-
- IDXGIResource *dxgiResource = nullptr;
- HRESULT hr = ffmpegTex->QueryInterface(__uuidof(IDXGIResource), (void **)&dxgiResource);
- if (FAILED(hr)) {
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to obtain resource handle from FFMpeg texture" << hr;
- return nullptr;
- }
- HANDLE shared = nullptr;
- hr = dxgiResource->GetSharedHandle(&shared);
- dxgiResource->Release();
- if (FAILED(hr)) {
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to obtain shared handle for FFmpeg texture" << hr;
- return nullptr;
- }
+ const ComPtr<ID3D11Texture2D> ffmpegTex = reinterpret_cast<ID3D11Texture2D *>(frame->data[0]);
+ const int index = static_cast<int>(reinterpret_cast<intptr_t>(frame->data[1]));
if (rhi->backend() == QRhi::D3D11) {
- ID3D11Texture2D *sharedTex = nullptr;
- hr = dev->OpenSharedResource(shared, __uuidof(ID3D11Texture2D), (void **)(&sharedTex));
- if (FAILED(hr)) {
- qCDebug(qLcMediaFFmpegHWAccel) << "Failed to share FFmpeg texture" << hr;
- return nullptr;
+ {
+ const auto *avDeviceCtx = static_cast<AVD3D11VADeviceContext *>(ctx->hwctx);
+
+ if (!avDeviceCtx)
+ return nullptr;
+
+ // Lock the FFmpeg device context while we copy from FFmpeg's
+ // frame pool into a shared texture because the underlying ID3D11DeviceContext
+ // is not thread safe.
+ avDeviceCtx->lock(avDeviceCtx->lock_ctx);
+ QScopeGuard autoUnlock([&] { avDeviceCtx->unlock(avDeviceCtx->lock_ctx); });
+
+ // Populate the shared texture with one slice from the frame pool, cropping away
+ // extra surface alignment areas that FFmpeg adds to the textures
+ QSize frameSize{ frame->width, frame->height };
+ if (!m_bridge.copyToSharedTex(avDeviceCtx->device, avDeviceCtx->device_context,
+ ffmpegTex, index, frameSize)) {
+ return nullptr;
+ }
}
- QVideoFrameFormat::PixelFormat format = QFFmpegVideoBuffer::toQtPixelFormat(AVPixelFormat(fCtx->sw_format));
- return new D3D11TextureSet(rhi, format, sharedTex, index);
- } else if (rhi->backend() == QRhi::OpenGLES2) {
+ // Get a copy of the texture on the RHI device
+ ComPtr<ID3D11Texture2D> output = m_bridge.copyFromSharedTex(m_rhiDevice, m_rhiCtx);
+ if (!output)
+ return nullptr;
+
+ return new D3D11TextureSet(rhi, std::move(output));
}
return nullptr;
@@ -163,17 +273,28 @@ TextureSet *D3D11TextureConverter::getTextures(AVFrame *frame)
void D3D11TextureConverter::SetupDecoderTextures(AVCodecContext *s)
{
- int ret = avcodec_get_hw_frames_parameters(s,
- s->hw_device_ctx,
- AV_PIX_FMT_D3D11,
+ // We are holding pool frames alive for quite long, which may cause
+ // codecs to run out of frames because FFmpeg has a fixed size
+ // decoder frame pool. We must therefore add extra frames to the pool
+ // to account for the frames we keep alive. First, we need to account
+ // for the maximum number of queued frames during rendering. In
+ // addition, we add one frame for the RHI rendering pipeline, and one
+ // additional frame because we may hold one in the Qt event loop.
+
+ const qint32 maxRenderQueueSize = StreamDecoder::maxQueueSize(QPlatformMediaPlayer::VideoStream);
+ constexpr qint32 framesHeldByRhi = 1;
+ constexpr qint32 framesHeldByQtEventLoop = 1;
+ s->extra_hw_frames = maxRenderQueueSize + framesHeldByRhi + framesHeldByQtEventLoop;
+
+ int ret = avcodec_get_hw_frames_parameters(s, s->hw_device_ctx, AV_PIX_FMT_D3D11,
&s->hw_frames_ctx);
if (ret < 0) {
qCDebug(qLcMediaFFmpegHWAccel) << "Failed to allocate HW frames context" << ret;
return;
}
- auto *frames_ctx = (AVHWFramesContext *)s->hw_frames_ctx->data;
- auto *hwctx = (AVD3D11VAFramesContext *)frames_ctx->hwctx;
+ const auto *frames_ctx = reinterpret_cast<const AVHWFramesContext *>(s->hw_frames_ctx->data);
+ auto *hwctx = static_cast<AVD3D11VAFramesContext *>(frames_ctx->hwctx);
hwctx->MiscFlags = D3D11_RESOURCE_MISC_SHARED;
hwctx->BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE;
ret = av_hwframe_ctx_init(s->hw_frames_ctx);
@@ -183,6 +304,6 @@ void D3D11TextureConverter::SetupDecoderTextures(AVCodecContext *s)
}
}
-}
+} // namespace QFFmpeg
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h
index 383b7168c..bfcc1f10c 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_d3d11_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGHWACCEL_D3D11_P_H
#define QFFMPEGHWACCEL_D3D11_P_H
@@ -51,6 +15,12 @@
//
#include "qffmpeghwaccel_p.h"
+#include <private/quniquehandle_p.h>
+#include <private/qcomptr_p.h>
+#include <qt_windows.h>
+
+#include <d3d11.h>
+#include <d3d11_1.h>
#if QT_CONFIG(wmf)
@@ -60,6 +30,56 @@ class QRhi;
namespace QFFmpeg {
+struct SharedTextureHandleTraits
+{
+ using Type = HANDLE;
+ static Type invalidValue() { return nullptr; }
+ static bool close(Type handle) { return CloseHandle(handle) != 0; }
+};
+
+using SharedTextureHandle = QUniqueHandle<SharedTextureHandleTraits>;
+
+/*! \internal Utility class for synchronized transfer of a texture between two D3D devices
+ *
+ * This class is used to copy a texture from one device to another device. This
+ * is implemented using a shared texture, along with keyed mutexes to synchronize
+ * access to the texture.
+ *
+ * This is needed because we need to copy data from FFmpeg to RHI. FFmpeg and RHI
+ * uses different D3D devices.
+ */
+class TextureBridge final
+{
+public:
+ /** Copy a texture slice at position 'index' belonging to device 'dev'
+ * into a shared texture, limiting the texture size to the frame size */
+ bool copyToSharedTex(ID3D11Device *dev, ID3D11DeviceContext *ctx,
+ const ComPtr<ID3D11Texture2D> &tex, UINT index, const QSize &frameSize);
+
+ /** Obtain a copy of the texture on a second device 'dev' */
+ ComPtr<ID3D11Texture2D> copyFromSharedTex(const ComPtr<ID3D11Device1> &dev,
+ const ComPtr<ID3D11DeviceContext> &ctx);
+
+private:
+ bool ensureDestTex(const ComPtr<ID3D11Device1> &dev);
+ bool ensureSrcTex(ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex, const QSize &frameSize);
+ bool isSrcInitialized(const ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex, const QSize &frameSize) const;
+ bool recreateSrc(ID3D11Device *dev, const ComPtr<ID3D11Texture2D> &tex, const QSize &frameSize);
+
+ SharedTextureHandle m_sharedHandle{};
+
+ const UINT m_srcKey = 0;
+ ComPtr<ID3D11Texture2D> m_srcTex;
+ ComPtr<IDXGIKeyedMutex> m_srcMutex;
+
+ const UINT m_destKey = 1;
+ ComPtr<ID3D11Device1> m_destDevice;
+ ComPtr<ID3D11Texture2D> m_destTex;
+ ComPtr<IDXGIKeyedMutex> m_destMutex;
+
+ ComPtr<ID3D11Texture2D> m_outputTex;
+};
+
class D3D11TextureConverter : public TextureConverterBackend
{
public:
@@ -68,9 +88,14 @@ public:
TextureSet *getTextures(AVFrame *frame) override;
static void SetupDecoderTextures(AVCodecContext *s);
+
+private:
+ ComPtr<ID3D11Device1> m_rhiDevice;
+ ComPtr<ID3D11DeviceContext> m_rhiCtx;
+ TextureBridge m_bridge;
};
-}
+} // namespace QFFmpeg
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp
new file mode 100644
index 000000000..e9dd8705a
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec.cpp
@@ -0,0 +1,120 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpeghwaccel_mediacodec_p.h"
+
+#include "androidsurfacetexture_p.h"
+#include <rhi/qrhi.h>
+
+extern "C" {
+#include <libavcodec/mediacodec.h>
+#include <libavutil/hwcontext_mediacodec.h>
+}
+
+#if !defined(Q_OS_ANDROID)
+# error "Configuration error"
+#endif
+
+namespace QFFmpeg {
+
+class MediaCodecTextureSet : public TextureSet
+{
+public:
+ MediaCodecTextureSet(qint64 textureHandle) : handle(textureHandle) { }
+
+ qint64 textureHandle(QRhi *, int plane) override { return (plane == 0) ? handle : 0; }
+
+private:
+ qint64 handle;
+};
+
+namespace {
+
+void deleteSurface(AVHWDeviceContext *ctx)
+{
+ AndroidSurfaceTexture* s = reinterpret_cast<AndroidSurfaceTexture *>(ctx->user_opaque);
+ delete s;
+}
+
+AndroidSurfaceTexture* getTextureSurface(AVFrame *frame)
+{
+ if (!frame || !frame->hw_frames_ctx)
+ return nullptr;
+
+ auto *frameContext = reinterpret_cast<AVHWFramesContext *>(frame->hw_frames_ctx->data);
+
+ if (!frameContext || !frameContext->device_ctx)
+ return nullptr;
+
+ AVHWDeviceContext *deviceContext = frameContext->device_ctx;
+
+ return reinterpret_cast<AndroidSurfaceTexture *>(deviceContext->user_opaque);
+}
+} // namespace
+
+void MediaCodecTextureConverter::setupDecoderSurface(AVCodecContext *avCodecContext)
+{
+ std::unique_ptr<AndroidSurfaceTexture> androidSurfaceTexture(new AndroidSurfaceTexture(0));
+ AVMediaCodecContext *mediacodecContext = av_mediacodec_alloc_context();
+ av_mediacodec_default_init(avCodecContext, mediacodecContext, androidSurfaceTexture->surface());
+
+ if (!avCodecContext->hw_device_ctx || !avCodecContext->hw_device_ctx->data)
+ return;
+
+ AVHWDeviceContext *deviceContext =
+ reinterpret_cast<AVHWDeviceContext *>(avCodecContext->hw_device_ctx->data);
+
+ if (!deviceContext->hwctx)
+ return;
+
+ AVMediaCodecDeviceContext *mediaDeviceContext =
+ reinterpret_cast<AVMediaCodecDeviceContext *>(deviceContext->hwctx);
+
+ if (!mediaDeviceContext)
+ return;
+
+ mediaDeviceContext->surface = androidSurfaceTexture->surface();
+
+ Q_ASSERT(deviceContext->user_opaque == nullptr);
+ deviceContext->user_opaque = androidSurfaceTexture.release();
+ deviceContext->free = deleteSurface;
+}
+
+TextureSet *MediaCodecTextureConverter::getTextures(AVFrame *frame)
+{
+ AndroidSurfaceTexture * androidSurfaceTexture = getTextureSurface(frame);
+
+ if (!androidSurfaceTexture || !androidSurfaceTexture->isValid())
+ return {};
+
+ if (!externalTexture || m_currentSurfaceIndex != androidSurfaceTexture->index()) {
+ m_currentSurfaceIndex = androidSurfaceTexture->index();
+ androidSurfaceTexture->detachFromGLContext();
+ externalTexture = std::unique_ptr<QRhiTexture>(
+ rhi->newTexture(QRhiTexture::Format::RGBA8, { frame->width, frame->height }, 1,
+ QRhiTexture::ExternalOES));
+
+ if (!externalTexture->create()) {
+ qWarning() << "Failed to create the external texture!";
+ return {};
+ }
+
+ quint64 textureHandle = externalTexture->nativeTexture().object;
+ androidSurfaceTexture->attachToGLContext(textureHandle);
+ }
+
+ // release a MediaCodec buffer and render it to the surface
+ AVMediaCodecBuffer *buffer = (AVMediaCodecBuffer *)frame->data[3];
+
+ if (!buffer) {
+ qWarning() << "Received a frame without AVMediaCodecBuffer.";
+ } else if (av_mediacodec_release_buffer(buffer, 1) < 0) {
+ qWarning() << "Failed to render buffer to surface.";
+ return {};
+ }
+
+ androidSurfaceTexture->updateTexImage();
+
+ return new MediaCodecTextureSet(externalTexture->nativeTexture().object);
+}
+}
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec_p.h
new file mode 100644
index 000000000..79d33d03e
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_mediacodec_p.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGHWACCEL_MEDIACODEC_P_H
+#define QFFMPEGHWACCEL_MEDIACODEC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpeghwaccel_p.h"
+#include <memory>
+
+namespace QFFmpeg {
+struct Frame;
+
+class MediaCodecTextureConverter : public TextureConverterBackend
+{
+public:
+ MediaCodecTextureConverter(QRhi *rhi) : TextureConverterBackend(rhi){};
+ TextureSet *getTextures(AVFrame *frame) override;
+
+ static void setupDecoderSurface(AVCodecContext *s);
+private:
+ std::unique_ptr<QRhiTexture> externalTexture;
+ quint64 m_currentSurfaceIndex = 0;
+};
+}
+#endif // QFFMPEGHWACCEL_MEDIACODEC_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h
index c25faecfd..eee535343 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGHWACCEL_P_H
#define QFFMPEGHWACCEL_P_H
@@ -52,8 +16,11 @@
#include "qffmpeg_p.h"
#include "qvideoframeformat.h"
+#include <private/qabstractvideobuffer_p.h>
#include <qshareddata.h>
#include <memory>
+#include <functional>
+#include <mutex>
QT_BEGIN_NAMESPACE
@@ -72,8 +39,7 @@ class TextureSet {
public:
// ### Should add QVideoFrameFormat::PixelFormat here
virtual ~TextureSet() {}
- virtual qint64 textureHandle(int /*plane*/) { return 0; }
- virtual std::unique_ptr<QRhiTexture> texture(int plane);
+ virtual qint64 textureHandle(QRhi *, int /*plane*/) { return 0; }
};
class TextureConverterBackend
@@ -93,11 +59,10 @@ class TextureConverter
class Data final
{
public:
- ~Data();
QAtomicInt ref = 0;
QRhi *rhi = nullptr;
AVPixelFormat format = AV_PIX_FMT_NONE;
- TextureConverterBackend *backend = nullptr;
+ std::unique_ptr<TextureConverterBackend> backend;
};
public:
TextureConverter(QRhi *rhi = nullptr);
@@ -118,38 +83,43 @@ private:
class HWAccel
{
- struct Data {
- ~Data();
- QAtomicInt ref = 0;
- AVBufferRef *hwDeviceContext = nullptr;
- AVBufferRef *hwFramesContext = nullptr;
- };
+ AVBufferUPtr m_hwDeviceContext;
+ AVBufferUPtr m_hwFramesContext;
+
+ mutable std::once_flag m_constraintsOnceFlag;
+ mutable AVHWFramesConstraintsUPtr m_constraints;
public:
- HWAccel() = default;
- explicit HWAccel(AVHWDeviceType deviceType);
- explicit HWAccel(const AVCodec *codec);
~HWAccel();
- bool isNull() const { return !d || !d->hwDeviceContext; }
+ static std::unique_ptr<HWAccel> create(AVHWDeviceType deviceType);
+
+ static std::pair<const AVCodec *, std::unique_ptr<HWAccel>>
+ findEncoderWithHwAccel(AVCodecID id,
+ const std::function<bool(const HWAccel &)>& hwAccelPredicate = nullptr);
+
+ static std::pair<const AVCodec *, std::unique_ptr<HWAccel>>
+ findDecoderWithHwAccel(AVCodecID id,
+ const std::function<bool(const HWAccel &)>& hwAccelPredicate = nullptr);
AVHWDeviceType deviceType() const;
- AVBufferRef *hwDeviceContextAsBuffer() const { return d ? d->hwDeviceContext : nullptr; }
+ AVBufferRef *hwDeviceContextAsBuffer() const { return m_hwDeviceContext.get(); }
AVHWDeviceContext *hwDeviceContext() const;
AVPixelFormat hwFormat() const;
-
- const AVCodec *hardwareEncoderForCodecId(AVCodecID id) const;
- static HWAccel findHardwareAccelForCodecID(AVCodecID id);
+ const AVHWFramesConstraints *constraints() const;
void createFramesContext(AVPixelFormat swFormat, const QSize &size);
- AVBufferRef *hwFramesContextAsBuffer() const { return d ? d->hwFramesContext : nullptr; }
+ AVBufferRef *hwFramesContextAsBuffer() const { return m_hwFramesContext.get(); }
AVHWFramesContext *hwFramesContext() const;
static AVPixelFormat format(AVFrame *frame);
- static const AVHWDeviceType *preferredDeviceTypes();
+ static const std::vector<AVHWDeviceType> &encodingDeviceTypes();
+
+ static const std::vector<AVHWDeviceType> &decodingDeviceTypes();
+
private:
- QExplicitlySharedDataPointer<Data> d;
+ HWAccel(AVBufferUPtr hwDeviceContext) : m_hwDeviceContext(std::move(hwDeviceContext)) { }
};
}
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp
index 09e06c64a..09ffaaf71 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpeghwaccel_vaapi_p.h"
@@ -49,8 +13,7 @@
#include "qffmpegvideobuffer_p.h"
#include "private/qvideotexturehelper_p.h"
-#include <private/qrhi_p.h>
-#include <private/qrhigles2_p.h>
+#include <rhi/qrhi.h>
#include <qguiapplication.h>
#include <qpa/qplatformnativeinterface.h>
@@ -94,7 +57,11 @@ extern "C" {
#include <unistd.h>
-#include <qdebug.h>
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLHWAccelVAAPI, "qt.multimedia.ffmpeg.hwaccelvaapi");
namespace QFFmpeg {
@@ -110,7 +77,7 @@ static const quint32 *fourccFromPixelFormat(const QVideoFrameFormat::PixelFormat
const quint32 rg16_fourcc = DRM_FORMAT_RG1616;
#endif
-// qDebug() << "Getting DRM fourcc for pixel format" << format;
+// qCDebug(qLHWAccelVAAPI) << "Getting DRM fourcc for pixel format" << format;
switch (format) {
case QVideoFrameFormat::Format_Invalid:
@@ -187,7 +154,7 @@ class VAAPITextureSet : public TextureSet
{
public:
~VAAPITextureSet();
- qint64 textureHandle(int plane) override {
+ qint64 textureHandle(QRhi *, int plane) override {
return textures[plane];
}
@@ -201,7 +168,7 @@ public:
VAAPITextureConverter::VAAPITextureConverter(QRhi *rhi)
: TextureConverterBackend(nullptr)
{
- qDebug() << ">>>> Creating VAAPI HW accelerator";
+ qCDebug(qLHWAccelVAAPI) << ">>>> Creating VAAPI HW accelerator";
if (!rhi || rhi->backend() != QRhi::OpenGLES2) {
qWarning() << "VAAPITextureConverter: No rhi or non openGL based RHI";
@@ -212,21 +179,21 @@ VAAPITextureConverter::VAAPITextureConverter(QRhi *rhi)
auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
glContext = nativeHandles->context;
if (!glContext) {
- qDebug() << " no GL context, disabling";
+ qCDebug(qLHWAccelVAAPI) << " no GL context, disabling";
return;
}
const QString platform = QGuiApplication::platformName();
QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface();
eglDisplay = pni->nativeResourceForIntegration("egldisplay");
- qDebug() << " platform is" << platform << eglDisplay;
+ qCDebug(qLHWAccelVAAPI) << " platform is" << platform << eglDisplay;
if (!eglDisplay) {
- qDebug() << " no egl display, disabling";
+ qCDebug(qLHWAccelVAAPI) << " no egl display, disabling";
return;
}
eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES");
if (!eglDisplay) {
- qDebug() << " no eglImageTargetTexture2D, disabling";
+ qCDebug(qLHWAccelVAAPI) << " no eglImageTargetTexture2D, disabling";
return;
}
@@ -241,9 +208,9 @@ VAAPITextureConverter::~VAAPITextureConverter()
//#define VA_EXPORT_USE_LAYERS
TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
{
-// qDebug() << "VAAPIAccel::getTextures";
+// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel::getTextures";
if (frame->format != AV_PIX_FMT_VAAPI || !eglDisplay) {
- qDebug() << "format/egl error" << frame->format << eglDisplay;
+ qCDebug(qLHWAccelVAAPI) << "format/egl error" << frame->format << eglDisplay;
return nullptr;
}
@@ -258,13 +225,13 @@ TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
auto *vaCtx = (AVVAAPIDeviceContext *)ctx->hwctx;
auto vaDisplay = vaCtx->display;
if (!vaDisplay) {
- qDebug() << " no VADisplay, disabling";
+ qCDebug(qLHWAccelVAAPI) << " no VADisplay, disabling";
return nullptr;
}
VASurfaceID vaSurface = (uintptr_t)frame->data[3];
- VADRMPRIMESurfaceDescriptor prime;
+ VADRMPRIMESurfaceDescriptor prime = {};
if (vaExportSurfaceHandle(vaDisplay, vaSurface,
VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
VA_EXPORT_SURFACE_READ_ONLY |
@@ -278,10 +245,17 @@ TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
qWarning() << "vaExportSurfaceHandle failed";
return nullptr;
}
+
+ // Make sure all fd's in 'prime' are closed when we return from this function
+ QScopeGuard closeObjectsGuard([&prime]() {
+ for (uint32_t i = 0; i < prime.num_objects; ++i)
+ close(prime.objects[i].fd);
+ });
+
// ### Check that prime.fourcc is what we expect
vaSyncSurface(vaDisplay, vaSurface);
-// qDebug() << "VAAPIAccel: vaSufraceDesc: width/height" << prime.width << prime.height << "num objects"
+// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: vaSufraceDesc: width/height" << prime.width << prime.height << "num objects"
// << prime.num_objects << "num layers" << prime.num_layers;
QOpenGLFunctions functions(glContext);
@@ -303,7 +277,7 @@ TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
}
Q_ASSERT(nPlanes == desc->nplanes);
nPlanes = desc->nplanes;
-// qDebug() << "VAAPIAccel: nPlanes" << nPlanes;
+// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: nPlanes" << nPlanes;
rhi->makeThreadLocalNativeContextCurrent();
@@ -334,22 +308,30 @@ TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
};
images[i] = eglCreateImage(eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, img_attr);
if (!images[i]) {
- qWarning() << "eglCreateImage failed for plane" << i << Qt::hex << eglGetError();
- return nullptr;
+ const GLenum error = eglGetError();
+ if (error == EGL_BAD_MATCH) {
+ qWarning() << "eglCreateImage failed for plane" << i << "with error code EGL_BAD_MATCH, "
+ "disabling hardware acceleration. This could indicate an EGL implementation issue."
+ "\nVAAPI driver: " << vaQueryVendorString(vaDisplay)
+ << "\nEGL vendor:" << eglQueryString(eglDisplay, EGL_VENDOR);
+ this->rhi = nullptr; // Disabling texture conversion here to fix QTBUG-112312
+ return nullptr;
+ }
+ if (error) {
+ qWarning() << "eglCreateImage failed for plane" << i << "with error code" << error;
+ return nullptr;
+ }
}
functions.glActiveTexture(GL_TEXTURE0 + i);
functions.glBindTexture(GL_TEXTURE_2D, glTextures[i]);
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC eglImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)this->eglImageTargetTexture2D;
eglImageTargetTexture2D(GL_TEXTURE_2D, images[i]);
- if (glGetError()) {
- qWarning() << "eglImageTargetTexture2D failed";
- }
+ GLenum error = glGetError();
+ if (error)
+ qWarning() << "eglImageTargetTexture2D failed with error code" << error;
}
- for (int i = 0; i < (int)prime.num_objects; ++i)
- close(prime.objects[i].fd);
-
for (int i = 0; i < nPlanes; ++i) {
functions.glActiveTexture(GL_TEXTURE0 + i);
functions.glBindTexture(GL_TEXTURE_2D, 0);
@@ -363,7 +345,7 @@ TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
for (int i = 0; i < 4; ++i)
textureSet->textures[i] = glTextures[i];
-// qDebug() << "VAAPIAccel: got textures" << textures[0] << textures[1] << textures[2] << textures[3];
+// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: got textures" << textures[0] << textures[1] << textures[2] << textures[3];
return textureSet;
}
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h
index f769ca2cf..03084cc72 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGHWACCEL_VAAPI_P_H
#define QFFMPEGHWACCEL_VAAPI_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm
index d77eab142..948f7fc23 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpeghwaccel_videotoolbox_p.h"
@@ -45,22 +9,27 @@
#include <qvideoframeformat.h>
#include <qffmpegvideobuffer_p.h>
+#include <qloggingcategory.h>
#include "private/qvideotexturehelper_p.h"
-#include <private/qrhi_p.h>
-#include <private/qrhimetal_p.h>
-#include <private/qrhigles2_p.h>
+#include <rhi/qrhi.h>
#include <CoreVideo/CVMetalTexture.h>
#include <CoreVideo/CVMetalTextureCache.h>
#include <qopenglcontext.h>
-
+#ifdef Q_OS_MACOS
#import <AppKit/AppKit.h>
+#endif
+#ifdef Q_OS_IOS
+#import <OpenGLES/EAGL.h>
+#endif
#import <Metal/Metal.h>
QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(qLcVideotoolbox, "qt.multimedia.ffmpeg.videotoolbox")
+
namespace QFFmpeg
{
@@ -70,7 +39,7 @@ class VideoToolBoxTextureSet : public TextureSet
{
public:
~VideoToolBoxTextureSet();
- qint64 textureHandle(int plane) override;
+ qint64 textureHandle(QRhi *, int plane) override;
QRhi *rhi = nullptr;
CVMetalTextureRef cvMetalTexture[3] = {};
@@ -91,7 +60,6 @@ VideoToolBoxTextureConverter::VideoToolBoxTextureConverter(QRhi *rhi)
return;
if (rhi->backend() == QRhi::Metal) {
- qDebug() << " using metal backend";
const auto *metal = static_cast<const QRhiMetalNativeHandles *>(rhi->nativeHandles());
// Create a Metal Core Video texture cache from the pixel buffer.
@@ -202,13 +170,13 @@ TextureSet *VideoToolBoxTextureConverter::getTextures(AVFrame *frame)
bool needsConversion = false;
QVideoFrameFormat::PixelFormat pixelFormat = QFFmpegVideoBuffer::toQtPixelFormat(HWAccel::format(frame), &needsConversion);
if (needsConversion) {
- qDebug() << "XXXXXXXXXXXX pixel format needs conversion" << pixelFormat << HWAccel::format(frame);
+ // qDebug() << "XXXXXXXXXXXX pixel format needs conversion" << pixelFormat << HWAccel::format(frame);
return nullptr;
}
CVPixelBufferRef buffer = (CVPixelBufferRef)frame->data[3];
- VideoToolBoxTextureSet *textureSet = new VideoToolBoxTextureSet;
+ auto textureSet = std::make_unique<VideoToolBoxTextureSet>();
textureSet->m_buffer = buffer;
textureSet->rhi = rhi;
CVPixelBufferRetain(buffer);
@@ -251,8 +219,10 @@ TextureSet *VideoToolBoxTextureConverter::getTextures(AVFrame *frame)
buffer,
nil,
&textureSet->cvOpenGLTexture);
- if (cvret != kCVReturnSuccess)
- qWarning() << "OpenGL texture creation failed" << cvret;
+ if (cvret != kCVReturnSuccess) {
+ qCWarning(qLcVideotoolbox) << "OpenGL texture creation failed" << cvret;
+ return nullptr;
+ }
Q_ASSERT(CVOpenGLTextureGetTarget(textureSet->cvOpenGLTexture) == GL_TEXTURE_RECTANGLE);
#endif
@@ -272,13 +242,15 @@ TextureSet *VideoToolBoxTextureConverter::getTextures(AVFrame *frame)
GL_UNSIGNED_BYTE,
0,
&textureSet->cvOpenGLESTexture);
- if (cvret != kCVReturnSuccess)
- qWarning() << "OpenGL ES texture creation failed" << cvret;
+ if (cvret != kCVReturnSuccess) {
+ qCWarning(qLcVideotoolbox) << "OpenGL ES texture creation failed" << cvret;
+ return nullptr;
+ }
#endif
#endif
}
- return textureSet;
+ return textureSet.release();
}
VideoToolBoxTextureSet::~VideoToolBoxTextureSet()
@@ -296,7 +268,7 @@ VideoToolBoxTextureSet::~VideoToolBoxTextureSet()
CVPixelBufferRelease(m_buffer);
}
-qint64 VideoToolBoxTextureSet::textureHandle(int plane)
+qint64 VideoToolBoxTextureSet::textureHandle(QRhi *, int plane)
{
if (rhi->backend() == QRhi::Metal)
return cvMetalTexture[plane] ? qint64(CVMetalTextureGetTexture(cvMetalTexture[plane])) : 0;
diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h
index 46ef9f6d0..44fa32dd2 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_videotoolbox_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGHWACCEL_VIDEOTOOLBOX_P_H
#define QFFMPEGHWACCEL_VIDEOTOOLBOX_P_H
@@ -59,7 +23,11 @@
#include <CoreVideo/CVImageBuffer.h>
#include <CoreVideo/CVMetalTexture.h>
+#if defined(Q_OS_MACOS)
#include <CoreVideo/CVOpenGLTextureCache.h>
+#elif defined(Q_OS_IOS)
+#include <CoreVideo/CVOpenGLESTextureCache.h>
+#endif
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
index fe8df22b7..2fb878784 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegimagecapture_p.h"
#include <private/qplatformmediaformatinfo_p.h>
@@ -53,11 +17,15 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcImageCapture, "qt.multimedia.imageCapture")
+// Probably, might be increased. To be investigated and tested on Android implementation
+static constexpr int MaxPendingImagesCount = 1;
+
+static Q_LOGGING_CATEGORY(qLcImageCapture, "qt.multimedia.imageCapture")
QFFmpegImageCapture::QFFmpegImageCapture(QImageCapture *parent)
: QPlatformImageCapture(parent)
{
+ qRegisterMetaType<QVideoFrame>();
}
QFFmpegImageCapture::~QFFmpegImageCapture()
@@ -115,7 +83,7 @@ int QFFmpegImageCapture::doCapture(const QString &fileName)
qCDebug(qLcImageCapture) << "error 1";
return -1;
}
- if (!m_camera) {
+ if (!m_videoSource) {
//emit error in the next event loop,
//so application can associate it with returned request id.
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
@@ -126,7 +94,7 @@ int QFFmpegImageCapture::doCapture(const QString &fileName)
qCDebug(qLcImageCapture) << "error 2";
return -1;
}
- if (passImage) {
+ if (m_pendingImages.size() >= MaxPendingImagesCount) {
//emit error in the next event loop,
//so application can associate it with returned request id.
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
@@ -137,13 +105,12 @@ int QFFmpegImageCapture::doCapture(const QString &fileName)
qCDebug(qLcImageCapture) << "error 3";
return -1;
}
- m_lastId++;
- pendingImages.enqueue({m_lastId, fileName, QMediaMetaData{}});
- // let one image pass the pipeline
- passImage = true;
+ m_lastId++;
+ m_pendingImages.enqueue({ m_lastId, fileName, QMediaMetaData{} });
updateReadyForCapture();
+
return m_lastId;
}
@@ -154,48 +121,39 @@ void QFFmpegImageCapture::setCaptureSession(QPlatformMediaCaptureSession *sessio
return;
if (m_session) {
- disconnect(m_session, nullptr, this, nullptr);
+ m_session->disconnect(this);
m_lastId = 0;
- pendingImages.clear();
- passImage = false;
- cameraActive = false;
+ m_pendingImages.clear();
}
m_session = captureSession;
+
if (m_session)
- connect(m_session, &QPlatformMediaCaptureSession::cameraChanged, this, &QFFmpegImageCapture::onCameraChanged);
+ connect(m_session, &QFFmpegMediaCaptureSession::primaryActiveVideoSourceChanged, this,
+ &QFFmpegImageCapture::onVideoSourceChanged);
- onCameraChanged();
- updateReadyForCapture();
+ onVideoSourceChanged();
}
void QFFmpegImageCapture::updateReadyForCapture()
{
- bool ready = m_session && !passImage && cameraActive;
- if (ready == m_isReadyForCapture)
- return;
- m_isReadyForCapture = ready;
- emit readyForCaptureChanged(m_isReadyForCapture);
-}
+ const bool ready = m_session && m_pendingImages.size() < MaxPendingImagesCount && m_videoSource
+ && m_videoSource->isActive();
-void QFFmpegImageCapture::cameraActiveChanged(bool active)
-{
- qCDebug(qLcImageCapture) << "cameraActiveChanged" << cameraActive << active;
- if (cameraActive == active)
- return;
- cameraActive = active;
- qCDebug(qLcImageCapture) << "isReady" << isReadyForCapture();
- updateReadyForCapture();
+ qCDebug(qLcImageCapture) << "updateReadyForCapture" << ready;
+
+ if (std::exchange(m_isReadyForCapture, ready) != ready)
+ emit readyForCaptureChanged(ready);
}
void QFFmpegImageCapture::newVideoFrame(const QVideoFrame &frame)
{
- if (!passImage)
+ if (m_pendingImages.empty())
return;
- passImage = false;
- Q_ASSERT(!pendingImages.isEmpty());
- auto pending = pendingImages.dequeue();
+ auto pending = m_pendingImages.dequeue();
+
+ qCDebug(qLcImageCapture) << "Taking image" << pending.id;
emit imageExposed(pending.id);
// ### Add metadata from the AVFrame
@@ -253,27 +211,33 @@ void QFFmpegImageCapture::newVideoFrame(const QVideoFrame &frame)
emit error(pending.id, err, writer.errorString());
}
}
+
updateReadyForCapture();
}
-void QFFmpegImageCapture::onCameraChanged()
+void QFFmpegImageCapture::setupVideoSourceConnections()
{
- auto *camera = m_session ? m_session->camera() : nullptr;
- if (m_camera == camera)
- return;
+ connect(m_videoSource, &QPlatformCamera::newVideoFrame, this,
+ &QFFmpegImageCapture::newVideoFrame);
+}
+
+QPlatformVideoSource *QFFmpegImageCapture::videoSource() const
+{
+ return m_videoSource;
+}
- if (m_camera)
- disconnect(m_camera);
+void QFFmpegImageCapture::onVideoSourceChanged()
+{
+ if (m_videoSource)
+ m_videoSource->disconnect(this);
- m_camera = camera;
+ m_videoSource = m_session ? m_session->primaryActiveVideoSource() : nullptr;
- if (camera) {
- cameraActiveChanged(camera->isActive());
- connect(camera, &QPlatformCamera::activeChanged, this, &QFFmpegImageCapture::cameraActiveChanged);
- connect(camera, &QPlatformCamera::newVideoFrame, this, &QFFmpegImageCapture::newVideoFrame);
- } else {
- cameraActiveChanged(false);
- }
+ // TODO: optimize, setup the connection only when the capture is ready
+ if (m_videoSource)
+ setupVideoSourceConnections();
+
+ updateReadyForCapture();
}
QImageEncoderSettings QFFmpegImageCapture::imageSettings() const
@@ -303,3 +267,5 @@ void QFFmpegImageCapture::setImageSettings(const QImageEncoderSettings &settings
}
QT_END_NAMESPACE
+
+#include "moc_qffmpegimagecapture_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h
index 91dacb9d2..d8174ae05 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGIMAGECAPTURE_H
@@ -55,12 +19,12 @@
#include <private/qplatformimagecapture_p.h>
#include "qffmpegmediacapturesession_p.h"
+#include <QtCore/qpointer.h>
#include <qqueue.h>
QT_BEGIN_NAMESPACE
class QFFmpegImageCapture : public QPlatformImageCapture
-
{
Q_OBJECT
public:
@@ -76,20 +40,21 @@ public:
void setCaptureSession(QPlatformMediaCaptureSession *session);
+protected:
+ virtual int doCapture(const QString &fileName);
+ virtual void setupVideoSourceConnections();
+ QPlatformVideoSource *videoSource() const;
void updateReadyForCapture();
-public Q_SLOTS:
- void cameraActiveChanged(bool active);
+protected Q_SLOTS:
void newVideoFrame(const QVideoFrame &frame);
- void onCameraChanged();
+ void onVideoSourceChanged();
private:
- int doCapture(const QString &fileName);
-
QFFmpegMediaCaptureSession *m_session = nullptr;
+ QPointer<QPlatformVideoSource> m_videoSource;
int m_lastId = 0;
QImageEncoderSettings m_settings;
- QPlatformCamera *m_camera = nullptr;
struct PendingImage {
int id;
@@ -97,9 +62,7 @@ private:
QMediaMetaData metaData;
};
- QQueue<PendingImage> pendingImages;
- bool passImage = false;
- bool cameraActive = false;
+ QQueue<PendingImage> m_pendingImages;
bool m_isReadyForCapture = false;
};
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegioutils.cpp b/src/plugins/multimedia/ffmpeg/qffmpegioutils.cpp
new file mode 100644
index 000000000..cbef88f2b
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegioutils.cpp
@@ -0,0 +1,55 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegioutils_p.h"
+#include "qiodevice.h"
+#include "qffmpegdefs_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+int readQIODevice(void *opaque, uint8_t *buf, int buf_size)
+{
+ auto *dev = static_cast<QIODevice *>(opaque);
+ Q_ASSERT(dev);
+
+ if (dev->atEnd())
+ return AVERROR_EOF;
+ return dev->read(reinterpret_cast<char *>(buf), buf_size);
+}
+
+int writeQIODevice(void *opaque, AvioWriteBufferType buf, int buf_size)
+{
+ auto dev = static_cast<QIODevice *>(opaque);
+ Q_ASSERT(dev);
+
+ return dev->write(reinterpret_cast<const char *>(buf), buf_size);
+}
+
+int64_t seekQIODevice(void *opaque, int64_t offset, int whence)
+{
+ QIODevice *dev = static_cast<QIODevice *>(opaque);
+ Q_ASSERT(dev);
+
+ if (dev->isSequential())
+ return AVERROR(EINVAL);
+
+ if (whence & AVSEEK_SIZE)
+ return dev->size();
+
+ whence &= ~AVSEEK_FORCE;
+
+ if (whence == SEEK_CUR)
+ offset += dev->pos();
+ else if (whence == SEEK_END)
+ offset += dev->size();
+
+ if (!dev->seek(offset))
+ return AVERROR(EINVAL);
+ return offset;
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegioutils_p.h b/src/plugins/multimedia/ffmpeg/qffmpegioutils_p.h
new file mode 100644
index 000000000..7f00990f6
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegioutils_p.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGIOUTILS_P_H
+#define QFFMPEGIOUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtmultimediaglobal.h"
+#include "qffmpegdefs_p.h"
+
+#include <type_traits>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+int readQIODevice(void *opaque, uint8_t *buf, int buf_size);
+
+using AvioWriteBufferType =
+ std::conditional_t<QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t *>;
+
+int writeQIODevice(void *opaque, AvioWriteBufferType buf, int buf_size);
+
+int64_t seekQIODevice(void *opaque, int64_t offset, int whence);
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGIOUTILS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp
index f9001d30b..f02593d16 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession.cpp
@@ -1,66 +1,43 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegmediacapturesession_p.h"
#include "private/qplatformaudioinput_p.h"
#include "private/qplatformaudiooutput_p.h"
+#include "private/qplatformsurfacecapture_p.h"
#include "qffmpegimagecapture_p.h"
#include "qffmpegmediarecorder_p.h"
#include "private/qplatformcamera_p.h"
#include "qvideosink.h"
+#include "qffmpegaudioinput_p.h"
+#include "qaudiosink.h"
+#include "qaudiobuffer.h"
+#include "qaudiooutput.h"
#include <qloggingcategory.h>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcMediaCapture, "qt.multimedia.capture")
+static Q_LOGGING_CATEGORY(qLcFFmpegMediaCaptureSession, "qt.multimedia.ffmpeg.mediacapturesession")
+static int preferredAudioSinkBufferSize(const QFFmpegAudioInput &input)
+{
+ // Heuristic params to avoid jittering
+ // TODO: investigate the reason of jittering and probably reduce the factor
+ constexpr int BufferSizeFactor = 2;
+ constexpr int BufferSizeExceeding = 4096;
+ return input.bufferSize() * BufferSizeFactor + BufferSizeExceeding;
+}
QFFmpegMediaCaptureSession::QFFmpegMediaCaptureSession()
{
+ connect(this, &QFFmpegMediaCaptureSession::primaryActiveVideoSourceChanged, this,
+ &QFFmpegMediaCaptureSession::updateVideoFrameConnection);
}
-QFFmpegMediaCaptureSession::~QFFmpegMediaCaptureSession()
-{
-}
+QFFmpegMediaCaptureSession::~QFFmpegMediaCaptureSession() = default;
QPlatformCamera *QFFmpegMediaCaptureSession::camera()
{
@@ -69,21 +46,30 @@ QPlatformCamera *QFFmpegMediaCaptureSession::camera()
void QFFmpegMediaCaptureSession::setCamera(QPlatformCamera *camera)
{
- if (m_camera == camera)
- return;
- if (m_camera) {
- m_camera->disconnect(this);
- m_camera->setCaptureSession(nullptr);
- }
+ if (setVideoSource(m_camera, camera))
+ emit cameraChanged();
+}
+
+QPlatformSurfaceCapture *QFFmpegMediaCaptureSession::screenCapture()
+{
+ return m_screenCapture;
+}
- m_camera = camera;
+void QFFmpegMediaCaptureSession::setScreenCapture(QPlatformSurfaceCapture *screenCapture)
+{
+ if (setVideoSource(m_screenCapture, screenCapture))
+ emit screenCaptureChanged();
+}
- if (m_camera) {
- connect(m_camera, &QPlatformCamera::newVideoFrame, this, &QFFmpegMediaCaptureSession::newVideoFrame);
- m_camera->setCaptureSession(this);
- }
+QPlatformSurfaceCapture *QFFmpegMediaCaptureSession::windowCapture()
+{
+ return m_windowCapture;
+}
- emit cameraChanged();
+void QFFmpegMediaCaptureSession::setWindowCapture(QPlatformSurfaceCapture *windowCapture)
+{
+ if (setVideoSource(m_windowCapture, windowCapture))
+ emit windowCaptureChanged();
}
QPlatformImageCapture *QFFmpegMediaCaptureSession::imageCapture()
@@ -129,33 +115,172 @@ QPlatformMediaRecorder *QFFmpegMediaCaptureSession::mediaRecorder()
void QFFmpegMediaCaptureSession::setAudioInput(QPlatformAudioInput *input)
{
- if (m_audioInput == input)
+ qCDebug(qLcFFmpegMediaCaptureSession)
+ << "set audio input:" << (input ? input->device.description() : "null");
+
+ auto ffmpegAudioInput = dynamic_cast<QFFmpegAudioInput *>(input);
+ Q_ASSERT(!!input == !!ffmpegAudioInput);
+
+ if (m_audioInput == ffmpegAudioInput)
return;
- m_audioInput = input;
+ if (m_audioInput)
+ m_audioInput->q->disconnect(this);
+
+ m_audioInput = ffmpegAudioInput;
+ if (m_audioInput)
+ // TODO: implement the signal in QPlatformAudioInput and connect to it, QTBUG-112294
+ connect(m_audioInput->q, &QAudioInput::deviceChanged, this,
+ &QFFmpegMediaCaptureSession::updateAudioSink);
+
+ updateAudioSink();
+}
+
+void QFFmpegMediaCaptureSession::updateAudioSink()
+{
+ if (m_audioSink) {
+ m_audioSink->reset();
+ m_audioSink.reset();
+ }
+
+ if (!m_audioInput || !m_audioOutput)
+ return;
+
+ auto format = m_audioInput->device.preferredFormat();
+
+ if (!m_audioOutput->device.isFormatSupported(format))
+ qWarning() << "Audio source format" << format << "is not compatible with the audio output";
+
+ m_audioSink = std::make_unique<QAudioSink>(m_audioOutput->device, format);
+
+ m_audioBufferSize = preferredAudioSinkBufferSize(*m_audioInput);
+ m_audioSink->setBufferSize(m_audioBufferSize);
+
+ qCDebug(qLcFFmpegMediaCaptureSession)
+ << "Create audiosink, format:" << format << "bufferSize:" << m_audioSink->bufferSize()
+ << "output device:" << m_audioOutput->device.description();
+
+ m_audioIODevice = m_audioSink->start();
+ if (m_audioIODevice) {
+ auto writeToDevice = [this](const QAudioBuffer &buffer) {
+ if (m_audioBufferSize < preferredAudioSinkBufferSize(*m_audioInput)) {
+ qCDebug(qLcFFmpegMediaCaptureSession)
+ << "Recreate audiosink due to small buffer size:" << m_audioBufferSize;
+
+ updateAudioSink();
+ }
+
+ const auto written =
+ m_audioIODevice->write(buffer.data<const char>(), buffer.byteCount());
+
+ if (written < buffer.byteCount())
+ qCWarning(qLcFFmpegMediaCaptureSession)
+ << "Not all bytes written:" << written << "vs" << buffer.byteCount();
+ };
+ connect(m_audioInput, &QFFmpegAudioInput::newAudioBuffer, m_audioSink.get(), writeToDevice);
+ } else {
+ qWarning() << "Failed to start audiosink push mode";
+ }
+
+ updateVolume();
+}
+
+void QFFmpegMediaCaptureSession::updateVolume()
+{
+ if (m_audioSink)
+ m_audioSink->setVolume(m_audioOutput->muted ? 0.f : m_audioOutput->volume);
+}
+
+QPlatformAudioInput *QFFmpegMediaCaptureSession::audioInput()
+{
+ return m_audioInput;
}
void QFFmpegMediaCaptureSession::setVideoPreview(QVideoSink *sink)
{
- if (m_videoSink == sink)
+ if (std::exchange(m_videoSink, sink) == sink)
return;
- m_videoSink = sink;
+ updateVideoFrameConnection();
}
void QFFmpegMediaCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
{
+ qCDebug(qLcFFmpegMediaCaptureSession)
+ << "set audio output:" << (output ? output->device.description() : "null");
+
if (m_audioOutput == output)
return;
+ if (m_audioOutput)
+ m_audioOutput->q->disconnect(this);
+
m_audioOutput = output;
+ if (m_audioOutput) {
+ // TODO: implement the signals in QPlatformAudioOutput and connect to them, QTBUG-112294
+ connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this,
+ &QFFmpegMediaCaptureSession::updateAudioSink);
+ connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this,
+ &QFFmpegMediaCaptureSession::updateVolume);
+ connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this,
+ &QFFmpegMediaCaptureSession::updateVolume);
+ }
+
+ updateAudioSink();
}
-void QFFmpegMediaCaptureSession::newVideoFrame(const QVideoFrame &frame)
+void QFFmpegMediaCaptureSession::updateVideoFrameConnection()
{
- if (m_videoSink)
- m_videoSink->setVideoFrame(frame);
+ disconnect(m_videoFrameConnection);
+
+ if (m_primaryActiveVideoSource && m_videoSink) {
+ // deliver frames directly to video sink;
+ // AutoConnection type might be a pessimization due to an extra queuing
+ // TODO: investigate and integrate direct connection
+ m_videoFrameConnection =
+ connect(m_primaryActiveVideoSource, &QPlatformVideoSource::newVideoFrame,
+ m_videoSink, &QVideoSink::setVideoFrame);
+ }
}
+void QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource()
+{
+ auto sources = activeVideoSources();
+ auto source = sources.empty() ? nullptr : sources.front();
+ if (std::exchange(m_primaryActiveVideoSource, source) != source)
+ emit primaryActiveVideoSourceChanged();
+}
+
+template<typename VideoSource>
+bool QFFmpegMediaCaptureSession::setVideoSource(QPointer<VideoSource> &source,
+ VideoSource *newSource)
+{
+ if (source == newSource)
+ return false;
+
+ if (auto prevSource = std::exchange(source, newSource)) {
+ prevSource->setCaptureSession(nullptr);
+ prevSource->disconnect(this);
+ }
+
+ if (source) {
+ source->setCaptureSession(this);
+ connect(source, &QPlatformVideoSource::activeChanged, this,
+ &QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource);
+ connect(source, &QObject::destroyed, this,
+ &QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource, Qt::QueuedConnection);
+ }
+
+ updatePrimaryActiveVideoSource();
+
+ return true;
+}
+
+QPlatformVideoSource *QFFmpegMediaCaptureSession::primaryActiveVideoSource()
+{
+ return m_primaryActiveVideoSource;
+}
QT_END_NAMESPACE
+
+#include "moc_qffmpegmediacapturesession_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h
index 353cab3e7..6c80d0b09 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediacapturesession_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGMEDIACAPTURESESSION_H
#define QFFMPEGMEDIACAPTURESESSION_H
@@ -53,24 +17,38 @@
#include <private/qplatformmediacapture_p.h>
#include <private/qplatformmediaintegration_p.h>
+#include "qpointer.h"
+#include "qiodevice.h"
QT_BEGIN_NAMESPACE
class QFFmpegMediaRecorder;
class QFFmpegImageCapture;
class QVideoFrame;
+class QAudioSink;
+class QFFmpegAudioInput;
+class QAudioBuffer;
+class QPlatformVideoSource;
class QFFmpegMediaCaptureSession : public QPlatformMediaCaptureSession
{
Q_OBJECT
public:
+ using VideoSources = std::vector<QPointer<QPlatformVideoSource>>;
+
QFFmpegMediaCaptureSession();
- virtual ~QFFmpegMediaCaptureSession();
+ ~QFFmpegMediaCaptureSession() override;
QPlatformCamera *camera() override;
void setCamera(QPlatformCamera *camera) override;
+ QPlatformSurfaceCapture *screenCapture() override;
+ void setScreenCapture(QPlatformSurfaceCapture *) override;
+
+ QPlatformSurfaceCapture *windowCapture() override;
+ void setWindowCapture(QPlatformSurfaceCapture *) override;
+
QPlatformImageCapture *imageCapture() override;
void setImageCapture(QPlatformImageCapture *imageCapture) override;
@@ -78,21 +56,41 @@ public:
void setMediaRecorder(QPlatformMediaRecorder *recorder) override;
void setAudioInput(QPlatformAudioInput *input) override;
- QPlatformAudioInput *audioInput() { return m_audioInput; }
+ QPlatformAudioInput *audioInput();
void setVideoPreview(QVideoSink *sink) override;
void setAudioOutput(QPlatformAudioOutput *output) override;
-public Q_SLOTS:
- void newVideoFrame(const QVideoFrame &frame);
+ QPlatformVideoSource *primaryActiveVideoSource();
+
+private Q_SLOTS:
+ void updateAudioSink();
+ void updateVolume();
+ void updateVideoFrameConnection();
+ void updatePrimaryActiveVideoSource();
+
+Q_SIGNALS:
+ void primaryActiveVideoSourceChanged();
private:
- QPlatformCamera *m_camera = nullptr;
- QPlatformAudioInput *m_audioInput = nullptr;
+ template<typename VideoSource>
+ bool setVideoSource(QPointer<VideoSource> &source, VideoSource *newSource);
+
+ QPointer<QPlatformCamera> m_camera;
+ QPointer<QPlatformSurfaceCapture> m_screenCapture;
+ QPointer<QPlatformSurfaceCapture> m_windowCapture;
+ QPointer<QPlatformVideoSource> m_primaryActiveVideoSource;
+
+ QFFmpegAudioInput *m_audioInput = nullptr;
QFFmpegImageCapture *m_imageCapture = nullptr;
QFFmpegMediaRecorder *m_mediaRecorder = nullptr;
QPlatformAudioOutput *m_audioOutput = nullptr;
QVideoSink *m_videoSink = nullptr;
+ std::unique_ptr<QAudioSink> m_audioSink;
+ QPointer<QIODevice> m_audioIODevice;
+ qsizetype m_audioBufferSize = 0;
+
+ QMetaObject::Connection m_videoFrameConnection;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp
index 0ff4cc68b..6389b4eed 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp
@@ -1,49 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegmediaformatinfo_p.h"
-#include "qffmpeg_p.h"
#include "qaudioformat.h"
#include "qimagewriter.h"
+#include <qloggingcategory.h>
+
QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(qLcMediaFormatInfo, "qt.multimedia.ffmpeg.mediaformatinfo")
+
static struct {
AVCodecID id;
QMediaFormat::VideoCodec codec;
@@ -162,7 +129,7 @@ static const AVOutputFormat *avFormatForFormat(QMediaFormat::FileFormat format)
QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
{
- qDebug() << ">>>> listing codecs";
+ qCDebug(qLcMediaFormatInfo) << ">>>> listing codecs";
QList<QMediaFormat::AudioCodec> audioEncoders;
QList<QMediaFormat::AudioCodec> extraAudioDecoders;
@@ -171,8 +138,8 @@ QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
const AVCodecDescriptor *descriptor = nullptr;
while ((descriptor = avcodec_descriptor_next(descriptor))) {
- bool canEncode = (avcodec_find_encoder(descriptor->id) != nullptr);
- bool canDecode = (avcodec_find_decoder(descriptor->id) != nullptr);
+ const bool canEncode = QFFmpeg::findAVEncoder(descriptor->id) != nullptr;
+ const bool canDecode = QFFmpeg::findAVDecoder(descriptor->id) != nullptr;
auto videoCodec = videoCodecForAVCodecId(descriptor->id);
auto audioCodec = audioCodecForAVCodecId(descriptor->id);
if (descriptor->type == AVMEDIA_TYPE_VIDEO && videoCodec != QMediaFormat::VideoCodec::Unspecified) {
@@ -183,9 +150,8 @@ QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
if (!extraVideoDecoders.contains(videoCodec))
extraVideoDecoders.append(videoCodec);
}
- }
-
- else if (descriptor->type == AVMEDIA_TYPE_AUDIO && audioCodec != QMediaFormat::AudioCodec::Unspecified) {
+ } else if (descriptor->type == AVMEDIA_TYPE_AUDIO
+ && audioCodec != QMediaFormat::AudioCodec::Unspecified) {
if (canEncode) {
if (!audioEncoders.contains(audioCodec))
audioEncoders.append(audioCodec);
@@ -197,14 +163,14 @@ QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
}
// get demuxers
-// qDebug() << ">>>> Muxers";
+// qCDebug(qLcMediaFormatInfo) << ">>>> Muxers";
void *opaque = nullptr;
const AVOutputFormat *outputFormat = nullptr;
while ((outputFormat = av_muxer_iterate(&opaque))) {
auto mediaFormat = formatForAVFormat(outputFormat);
if (mediaFormat == QMediaFormat::UnspecifiedFormat)
continue;
-// qDebug() << " mux:" << outputFormat->name << outputFormat->long_name << outputFormat->mime_type << outputFormat->extensions << mediaFormat;
+// qCDebug(qLcMediaFormatInfo) << " mux:" << outputFormat->name << outputFormat->long_name << outputFormat->mime_type << outputFormat->extensions << mediaFormat;
CodecMap encoder;
encoder.format = mediaFormat;
@@ -214,7 +180,7 @@ QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
// only add the codec if it can be used with this container
if (avformat_query_codec(outputFormat, id, FF_COMPLIANCE_NORMAL) == 1) {
// add codec for container
-// qDebug() << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
+// qCDebug(qLcMediaFormatInfo) << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
encoder.audio.append(codec);
}
}
@@ -223,7 +189,7 @@ QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
// only add the codec if it can be used with this container
if (avformat_query_codec(outputFormat, id, FF_COMPLIANCE_NORMAL) == 1) {
// add codec for container
-// qDebug() << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
+// qCDebug(qLcMediaFormatInfo) << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
encoder.video.append(codec);
}
}
@@ -258,7 +224,16 @@ QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
// can encode. That's a safe subset.
decoders = encoders;
-// qDebug() << "extraDecoders:" << extraAudioDecoders << extraVideoDecoders;
+#ifdef Q_OS_WINDOWS
+ // MediaFoundation HVEC encoder fails when processing frames
+ for (auto &encoder : encoders) {
+ auto h265index = encoder.video.indexOf(QMediaFormat::VideoCodec::H265);
+ if (h265index >= 0)
+ encoder.video.removeAt(h265index);
+ }
+#endif
+
+// qCDebug(qLcMediaFormatInfo) << "extraDecoders:" << extraAudioDecoders << extraVideoDecoders;
// FFmpeg can currently only decode WMA and WMV, not encode
if (extraAudioDecoders.contains(QMediaFormat::AudioCodec::WMA)) {
decoders[QMediaFormat::WMA].audio.append(QMediaFormat::AudioCodec::WMA);
@@ -524,11 +499,17 @@ QAudioFormat QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(AVCodecParam
QAudioFormat format;
format.setSampleFormat(sampleFormat(AVSampleFormat(codecpar->format)));
format.setSampleRate(codecpar->sample_rate);
-
- auto channelLayout = codecpar->channel_layout;
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ uint64_t channelLayout = codecpar->channel_layout;
if (!channelLayout)
channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->channels));
-
+#else
+ uint64_t channelLayout = 0;
+ if (codecpar->ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
+ channelLayout = codecpar->ch_layout.u.mask;
+ else
+ channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->ch_layout.nb_channels));
+#endif
format.setChannelConfig(channelConfigForAVLayout(channelLayout));
return format;
}
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h
index ed941d0bd..52fcf6f72 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFmpegMediaFormatInfo_H
#define QFFmpegMediaFormatInfo_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp
index 791b59d47..aefe6102e 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtMultimedia/private/qplatformmediaplugin_p.h>
#include <qcameradevice.h>
@@ -48,27 +12,57 @@
#include "qffmpegimagecapture_p.h"
#include "qffmpegaudioinput_p.h"
#include "qffmpegaudiodecoder_p.h"
+#include "qffmpegresampler_p.h"
+#include "qffmpegsymbolsresolve_p.h"
+#include "qgrabwindowsurfacecapture_p.h"
+#include "qffmpegconverter_p.h"
#ifdef Q_OS_MACOS
#include <VideoToolbox/VideoToolbox.h>
+
+#include "qcgcapturablewindows_p.h"
+#include "qcgwindowcapture_p.h"
+#include "qavfscreencapture_p.h"
#endif
#ifdef Q_OS_DARWIN
#include "qavfcamera_p.h"
+
#elif defined(Q_OS_WINDOWS)
#include "qwindowscamera_p.h"
#include "qwindowsvideodevices_p.h"
+#include "qffmpegscreencapture_dxgi_p.h"
+#include "qwincapturablewindows_p.h"
+#include "qgdiwindowcapture_p.h"
#endif
#ifdef Q_OS_ANDROID
# include "jni.h"
+# include "qandroidvideodevices_p.h"
+# include "qandroidcamera_p.h"
+# include "qandroidimagecapture_p.h"
extern "C" {
-# include <libavcodec/jni.h>
+# include <libavutil/log.h>
+# include <libavcodec/jni.h>
}
#endif
#if QT_CONFIG(linux_v4l)
#include "qv4l2camera_p.h"
+#include "qv4l2cameradevices_p.h"
+#endif
+
+#if QT_CONFIG(cpp_winrt)
+#include "qffmpegwindowcapture_uwp_p.h"
+#endif
+
+#if QT_CONFIG(xlib)
+#include "qx11surfacecapture_p.h"
+#include "qx11capturablewindows_p.h"
+#endif
+
+#if QT_CONFIG(eglfs)
+#include "qeglfsscreencapture_p.h"
#endif
QT_BEGIN_NAMESPACE
@@ -85,62 +79,142 @@ public:
QPlatformMediaIntegration* create(const QString &name) override
{
- if (name == QLatin1String("ffmpeg"))
+ if (name == u"ffmpeg")
return new QFFmpegMediaIntegration;
return nullptr;
}
};
-QFFmpegMediaIntegration::QFFmpegMediaIntegration()
+bool thread_local FFmpegLogsEnabledInThread = true;
+static bool UseCustomFFmpegLogger = false;
+
+static void qffmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl)
{
- m_formatsInfo = new QFFmpegMediaFormatInfo();
+ if (!FFmpegLogsEnabledInThread)
+ return;
-#if QT_CONFIG(linux_v4l)
- m_videoDevices = new QV4L2CameraDevices(this);
+ if (!UseCustomFFmpegLogger)
+ return av_log_default_callback(ptr, level, fmt, vl);
+
+ // filter logs above the chosen level and AV_LOG_QUIET (negative level)
+ if (level < 0 || level > av_log_get_level())
+ return;
+
+ QString message = QStringLiteral("FFmpeg log: %1").arg(QString::vasprintf(fmt, vl));
+ if (message.endsWith("\n"))
+ message.removeLast();
+
+ if (level == AV_LOG_DEBUG || level == AV_LOG_TRACE)
+ qDebug() << message;
+ else if (level == AV_LOG_VERBOSE || level == AV_LOG_INFO)
+ qInfo() << message;
+ else if (level == AV_LOG_WARNING)
+ qWarning() << message;
+ else if (level == AV_LOG_ERROR || level == AV_LOG_FATAL || level == AV_LOG_PANIC)
+ qCritical() << message;
+}
+
+static void setupFFmpegLogger()
+{
+ if (qEnvironmentVariableIsSet("QT_FFMPEG_DEBUG")) {
+ av_log_set_level(AV_LOG_DEBUG);
+ UseCustomFFmpegLogger = true;
+ }
+
+ av_log_set_callback(&qffmpegLogCallback);
+}
+
+static QPlatformSurfaceCapture *createScreenCaptureByBackend(QString backend)
+{
+ if (backend == u"grabwindow")
+ return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
+
+#if QT_CONFIG(eglfs)
+ if (backend == u"eglfs")
+ return new QEglfsScreenCapture;
#endif
-#ifdef Q_OS_DARWIN
- m_videoDevices = new QAVFVideoDevices(this);
+
+#if QT_CONFIG(xlib)
+ if (backend == u"x11")
+ return new QX11SurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
#elif defined(Q_OS_WINDOWS)
- m_videoDevices = new QWindowsVideoDevices(this);
+ if (backend == u"dxgi")
+ return new QFFmpegScreenCaptureDxgi;
+#elif defined(Q_OS_MACOS)
+ if (backend == u"avf")
+ return new QAVFScreenCapture;
#endif
+ return nullptr;
+}
-#ifndef QT_NO_DEBUG
- qDebug() << "Available HW decoding frameworks:";
- AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
- while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)
- qDebug() << " " << av_hwdevice_get_type_name(type);
+static QPlatformSurfaceCapture *createWindowCaptureByBackend(QString backend)
+{
+ if (backend == u"grabwindow")
+ return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
+
+#if QT_CONFIG(xlib)
+ if (backend == u"x11")
+ return new QX11SurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
+#elif defined(Q_OS_WINDOWS)
+ if (backend == u"gdi")
+ return new QGdiWindowCapture;
+#if QT_CONFIG(cpp_winrt)
+ if (backend == u"uwp")
+ return new QFFmpegWindowCaptureUwp;
#endif
+#elif defined(Q_OS_MACOS)
+ if (backend == u"cg")
+ return new QCGWindowCapture;
+#endif
+ return nullptr;
}
-QFFmpegMediaIntegration::~QFFmpegMediaIntegration()
+QFFmpegMediaIntegration::QFFmpegMediaIntegration()
+ : QPlatformMediaIntegration(QLatin1String("ffmpeg"))
{
- delete m_formatsInfo;
+ resolveSymbols();
+
+ setupFFmpegLogger();
+
+#ifndef QT_NO_DEBUG
+ qDebug() << "Available HW decoding frameworks:";
+ for (auto type : QFFmpeg::HWAccel::decodingDeviceTypes())
+ qDebug() << " " << av_hwdevice_get_type_name(type);
+
+ qDebug() << "Available HW encoding frameworks:";
+ for (auto type : QFFmpeg::HWAccel::encodingDeviceTypes())
+ qDebug() << " " << av_hwdevice_get_type_name(type);
+#endif
}
-QPlatformMediaFormatInfo *QFFmpegMediaIntegration::formatInfo()
+QMaybe<QPlatformAudioDecoder *> QFFmpegMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
{
- return m_formatsInfo;
+ return new QFFmpegAudioDecoder(decoder);
}
-QPlatformAudioDecoder *QFFmpegMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
+QMaybe<std::unique_ptr<QPlatformAudioResampler>>
+QFFmpegMediaIntegration::createAudioResampler(const QAudioFormat &inputFormat,
+ const QAudioFormat &outputFormat)
{
- return new QFFmpegAudioDecoder(decoder);
+ return { std::make_unique<QFFmpegResampler>(inputFormat, outputFormat) };
}
-QPlatformMediaCaptureSession *QFFmpegMediaIntegration::createCaptureSession()
+QMaybe<QPlatformMediaCaptureSession *> QFFmpegMediaIntegration::createCaptureSession()
{
return new QFFmpegMediaCaptureSession();
}
-QPlatformMediaPlayer *QFFmpegMediaIntegration::createPlayer(QMediaPlayer *player)
+QMaybe<QPlatformMediaPlayer *> QFFmpegMediaIntegration::createPlayer(QMediaPlayer *player)
{
return new QFFmpegMediaPlayer(player);
}
-QPlatformCamera *QFFmpegMediaIntegration::createCamera(QCamera *camera)
+QMaybe<QPlatformCamera *> QFFmpegMediaIntegration::createCamera(QCamera *camera)
{
#ifdef Q_OS_DARWIN
return new QAVFCamera(camera);
+#elif defined(Q_OS_ANDROID)
+ return new QAndroidCamera(camera);
#elif QT_CONFIG(linux_v4l)
return new QV4L2Camera(camera);
#elif defined(Q_OS_WINDOWS)
@@ -151,27 +225,131 @@ QPlatformCamera *QFFmpegMediaIntegration::createCamera(QCamera *camera)
#endif
}
-QPlatformMediaRecorder *QFFmpegMediaIntegration::createRecorder(QMediaRecorder *recorder)
+QPlatformSurfaceCapture *QFFmpegMediaIntegration::createScreenCapture(QScreenCapture *)
+{
+ static const QString screenCaptureBackend = qgetenv("QT_SCREEN_CAPTURE_BACKEND").toLower();
+
+ if (!screenCaptureBackend.isEmpty()) {
+ if (auto screenCapture = createScreenCaptureByBackend(screenCaptureBackend))
+ return screenCapture;
+
+ qWarning() << "Not supported QT_SCREEN_CAPTURE_BACKEND:" << screenCaptureBackend;
+ }
+
+#if QT_CONFIG(xlib)
+ if (QX11SurfaceCapture::isSupported())
+ return new QX11SurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
+#endif
+
+#if QT_CONFIG(eglfs)
+ if (QEglfsScreenCapture::isSupported())
+ return new QEglfsScreenCapture;
+#endif
+
+#if defined(Q_OS_WINDOWS)
+ return new QFFmpegScreenCaptureDxgi;
+#elif defined(Q_OS_MACOS) // TODO: probably use it for iOS as well
+ return new QAVFScreenCapture;
+#else
+ return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
+#endif
+}
+
+QPlatformSurfaceCapture *QFFmpegMediaIntegration::createWindowCapture(QWindowCapture *)
+{
+ static const QString windowCaptureBackend = qgetenv("QT_WINDOW_CAPTURE_BACKEND").toLower();
+
+ if (!windowCaptureBackend.isEmpty()) {
+ if (auto windowCapture = createWindowCaptureByBackend(windowCaptureBackend))
+ return windowCapture;
+
+ qWarning() << "Not supported QT_WINDOW_CAPTURE_BACKEND:" << windowCaptureBackend;
+ }
+
+#if QT_CONFIG(xlib)
+ if (QX11SurfaceCapture::isSupported())
+ return new QX11SurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
+#endif
+
+#if defined(Q_OS_WINDOWS)
+# if QT_CONFIG(cpp_winrt)
+ if (QFFmpegWindowCaptureUwp::isSupported())
+ return new QFFmpegWindowCaptureUwp;
+# endif
+
+ return new QGdiWindowCapture;
+#elif defined(Q_OS_MACOS) // TODO: probably use it for iOS as well
+ return new QCGWindowCapture;
+#else
+ return new QGrabWindowSurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
+#endif
+}
+
+QMaybe<QPlatformMediaRecorder *> QFFmpegMediaIntegration::createRecorder(QMediaRecorder *recorder)
{
return new QFFmpegMediaRecorder(recorder);
}
-QPlatformImageCapture *QFFmpegMediaIntegration::createImageCapture(QImageCapture *imageCapture)
+QMaybe<QPlatformImageCapture *> QFFmpegMediaIntegration::createImageCapture(QImageCapture *imageCapture)
{
+#if defined(Q_OS_ANDROID)
+ return new QAndroidImageCapture(imageCapture);
+#else
return new QFFmpegImageCapture(imageCapture);
+#endif
}
-QPlatformVideoSink *QFFmpegMediaIntegration::createVideoSink(QVideoSink *sink)
+QMaybe<QPlatformVideoSink *> QFFmpegMediaIntegration::createVideoSink(QVideoSink *sink)
{
return new QFFmpegVideoSink(sink);
}
-QPlatformAudioInput *QFFmpegMediaIntegration::createAudioInput(QAudioInput *input)
+QMaybe<QPlatformAudioInput *> QFFmpegMediaIntegration::createAudioInput(QAudioInput *input)
{
return new QFFmpegAudioInput(input);
}
+QVideoFrame QFFmpegMediaIntegration::convertVideoFrame(QVideoFrame &srcFrame,
+ const QVideoFrameFormat &destFormat)
+{
+ return convertFrame(srcFrame, destFormat);
+}
+
+QPlatformMediaFormatInfo *QFFmpegMediaIntegration::createFormatInfo()
+{
+ return new QFFmpegMediaFormatInfo;
+}
+
+QPlatformVideoDevices *QFFmpegMediaIntegration::createVideoDevices()
+{
+#if defined(Q_OS_ANDROID)
+ return new QAndroidVideoDevices(this);
+#elif QT_CONFIG(linux_v4l)
+ return new QV4L2CameraDevices(this);
+#elif defined Q_OS_DARWIN
+ return new QAVFVideoDevices(this);
+#elif defined(Q_OS_WINDOWS)
+ return new QWindowsVideoDevices(this);
+#else
+ return nullptr;
+#endif
+}
+
+QPlatformCapturableWindows *QFFmpegMediaIntegration::createCapturableWindows()
+{
+#if QT_CONFIG(xlib)
+ if (QX11SurfaceCapture::isSupported())
+ return new QX11CapturableWindows;
+#elif defined Q_OS_MACOS
+ return new QCGCapturableWindows;
+#elif defined(Q_OS_WINDOWS)
+ return new QWinCapturableWindows;
+#endif
+ return nullptr;
+}
+
#ifdef Q_OS_ANDROID
+
Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
{
static bool initialized = false;
@@ -188,6 +366,9 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
if (av_jni_set_java_vm(vm, nullptr))
return JNI_ERR;
+ if (!QAndroidCamera::registerNativeMethods())
+ return JNI_ERR;
+
return JNI_VERSION_1_6;
}
#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h
index 17840c4d1..473a5f044 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaintegration_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERINTEGRATION_H
#define QGSTREAMERINTEGRATION_H
@@ -61,24 +25,31 @@ class QFFmpegMediaIntegration : public QPlatformMediaIntegration
{
public:
QFFmpegMediaIntegration();
- ~QFFmpegMediaIntegration();
- static QFFmpegMediaIntegration *instance() { return static_cast<QFFmpegMediaIntegration *>(QPlatformMediaIntegration::instance()); }
- QPlatformMediaFormatInfo *formatInfo() override;
+ QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *decoder) override;
+ QMaybe<std::unique_ptr<QPlatformAudioResampler>> createAudioResampler(const QAudioFormat &inputFormat, const QAudioFormat &outputFormat) override;
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *player) override;
+ QMaybe<QPlatformCamera *> createCamera(QCamera *) override;
+ QPlatformSurfaceCapture *createScreenCapture(QScreenCapture *) override;
+ QPlatformSurfaceCapture *createWindowCapture(QWindowCapture *) override;
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *) override;
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *) override;
- QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *decoder) override;
- QPlatformMediaCaptureSession *createCaptureSession() override;
- QPlatformMediaPlayer *createPlayer(QMediaPlayer *player) override;
- QPlatformCamera *createCamera(QCamera *) override;
- QPlatformMediaRecorder *createRecorder(QMediaRecorder *) override;
- QPlatformImageCapture *createImageCapture(QImageCapture *) override;
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
- QPlatformVideoSink *createVideoSink(QVideoSink *sink) override;
-
- QPlatformAudioInput *createAudioInput(QAudioInput *input) override;
+ QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *input) override;
// QPlatformAudioOutput *createAudioOutput(QAudioOutput *) override;
- QFFmpegMediaFormatInfo *m_formatsInfo = nullptr;
+ QVideoFrame convertVideoFrame(QVideoFrame &srcFrame,
+ const QVideoFrameFormat &destFormat) override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+
+ QPlatformVideoDevices *createVideoDevices() override;
+
+ QPlatformCapturableWindows *createCapturableWindows() override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp
index 0d84440f0..465e380db 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegmediametadata_p.h"
#include <QDebug>
@@ -44,14 +8,21 @@
#include <qurl.h>
#include <qlocale.h>
+#include <qloggingcategory.h>
+
QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(qLcMetaData, "qt.multimedia.ffmpeg.metadata")
+
namespace {
-struct {
+struct ffmpegTagToMetaDataKey
+{
const char *tag;
QMediaMetaData::Key key;
-} ffmpegTagToMetaDataKey[] = {
+};
+
+constexpr ffmpegTagToMetaDataKey ffmpegTagToMetaDataKey[] = {
{ "title", QMediaMetaData::Title },
{ "comment", QMediaMetaData::Comment },
{ "description", QMediaMetaData::Description },
@@ -80,7 +51,7 @@ struct {
static QMediaMetaData::Key tagToKey(const char *tag)
{
- auto *map = ffmpegTagToMetaDataKey;
+ const auto *map = ffmpegTagToMetaDataKey;
while (map->tag) {
if (!strcmp(map->tag, tag))
return map->key;
@@ -91,7 +62,7 @@ static QMediaMetaData::Key tagToKey(const char *tag)
static const char *keyToTag(QMediaMetaData::Key key)
{
- auto *map = ffmpegTagToMetaDataKey;
+ const auto *map = ffmpegTagToMetaDataKey;
while (map->tag) {
if (map->key == key)
return map->tag;
@@ -103,12 +74,12 @@ static const char *keyToTag(QMediaMetaData::Key key)
//internal
void QFFmpegMetaData::addEntry(QMediaMetaData &metaData, AVDictionaryEntry *entry)
{
-// qDebug() << " checking:" << entry->key << entry->value;
+ qCDebug(qLcMetaData) << " checking:" << entry->key << entry->value;
QByteArray tag(entry->key);
QMediaMetaData::Key key = tagToKey(tag.toLower());
if (key == QMediaMetaData::Key(-1))
return;
-// qDebug() << " adding" << key;
+ qCDebug(qLcMetaData) << " adding" << key;
auto *map = &metaData;
@@ -165,8 +136,6 @@ QMediaMetaData QFFmpegMetaData::fromAVMetaData(const AVDictionary *tags)
QByteArray QFFmpegMetaData::value(const QMediaMetaData &metaData, QMediaMetaData::Key key)
{
-// qDebug() << " checking:" << entry->key << entry->value;
-
const int metaTypeId = keyType(key).id();
const QVariant val = metaData.value(key);
switch (metaTypeId) {
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h
index 33e14a89b..201287495 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediametadata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGMEDIAMETADATA_H
#define QFFMPEGMEDIAMETADATA_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp
index a1614d08f..6a950a6ad 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer.cpp
@@ -1,94 +1,127 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegmediaplayer_p.h"
-#include "qffmpegdecoder_p.h"
-#include "qffmpegmediaformatinfo_p.h"
-#include "qlocale.h"
-#include "qffmpeg_p.h"
-#include "qffmpegmediametadata_p.h"
-#include "qffmpegvideobuffer_p.h"
#include "private/qplatformaudiooutput_p.h"
#include "qvideosink.h"
-#include "qaudiosink.h"
#include "qaudiooutput.h"
-#include <qlocale.h>
-#include <qthread.h>
-#include <qatomic.h>
-#include <qwaitcondition.h>
-#include <qmutex.h>
+#include "qffmpegplaybackengine_p.h"
+#include <qiodevice.h>
+#include <qvideosink.h>
#include <qtimer.h>
-#include <qqueue.h>
+#include <QtConcurrent/QtConcurrent>
#include <qloggingcategory.h>
QT_BEGIN_NAMESPACE
+namespace QFFmpeg {
+
+class CancelToken : public ICancelToken
+{
+public:
+
+ bool isCancelled() const override { return m_cancelled.load(std::memory_order_acquire); }
+
+ void cancel() { m_cancelled.store(true, std::memory_order_release); }
+
+private:
+ std::atomic_bool m_cancelled = false;
+};
+
+} // namespace QFFmpeg
+
using namespace QFFmpeg;
QFFmpegMediaPlayer::QFFmpegMediaPlayer(QMediaPlayer *player)
: QPlatformMediaPlayer(player)
{
+ m_positionUpdateTimer.setInterval(50);
+ m_positionUpdateTimer.setTimerType(Qt::PreciseTimer);
+ connect(&m_positionUpdateTimer, &QTimer::timeout, this, &QFFmpegMediaPlayer::updatePosition);
}
QFFmpegMediaPlayer::~QFFmpegMediaPlayer()
{
- delete decoder;
-}
+ if (m_cancelToken)
+ m_cancelToken->cancel();
+
+ m_loadMedia.waitForFinished();
+};
qint64 QFFmpegMediaPlayer::duration() const
{
- return decoder ? decoder->m_duration/1000 : 0;
+ return m_playbackEngine ? m_playbackEngine->duration() / 1000 : 0;
}
void QFFmpegMediaPlayer::setPosition(qint64 position)
{
- if (decoder)
- decoder->seek(position*1000);
- if (state() == QMediaPlayer::StoppedState)
- mediaStatusChanged(QMediaPlayer::LoadedMedia);
+ if (mediaStatus() == QMediaPlayer::LoadingMedia)
+ return;
+
+ if (m_playbackEngine) {
+ m_playbackEngine->seek(position * 1000);
+ updatePosition();
+ }
+
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+}
+
+void QFFmpegMediaPlayer::updatePosition()
+{
+ positionChanged(m_playbackEngine ? m_playbackEngine->currentPosition() / 1000 : 0);
+}
+
+void QFFmpegMediaPlayer::endOfStream()
+{
+ // start update timer and report end position anyway
+ m_positionUpdateTimer.stop();
+ positionChanged(duration());
+
+ stateChanged(QMediaPlayer::StoppedState);
+ mediaStatusChanged(QMediaPlayer::EndOfMedia);
+}
+
+void QFFmpegMediaPlayer::onLoopChanged()
+{
+ // report about finish and start
+ // reporting both signals is a bit contraversial
+ // but it eshures the idea of notifications about
+ // imporatant position points.
+ // Also, it ensures more predictable flow for testing.
+ positionChanged(duration());
+ positionChanged(0);
+ m_positionUpdateTimer.stop();
+ m_positionUpdateTimer.start();
+}
+
+void QFFmpegMediaPlayer::onBuffered()
+{
+ if (mediaStatus() == QMediaPlayer::BufferingMedia)
+ mediaStatusChanged(QMediaPlayer::BufferedMedia);
}
float QFFmpegMediaPlayer::bufferProgress() const
{
- return 1.;
+ return m_bufferProgress;
+}
+
+void QFFmpegMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status)
+{
+ if (mediaStatus() == status)
+ return;
+
+ const auto newBufferProgress = status == QMediaPlayer::BufferingMedia ? 0.25f // to be improved
+ : status == QMediaPlayer::BufferedMedia ? 1.f
+ : 0.f;
+
+ if (!qFuzzyCompare(newBufferProgress, m_bufferProgress)) {
+ m_bufferProgress = newBufferProgress;
+ bufferProgressChanged(newBufferProgress);
+ }
+
+ QPlatformMediaPlayer::mediaStatusChanged(status);
}
QMediaTimeRange QFFmpegMediaPlayer::availablePlaybackRanges() const
@@ -103,11 +136,17 @@ qreal QFFmpegMediaPlayer::playbackRate() const
void QFFmpegMediaPlayer::setPlaybackRate(qreal rate)
{
- if (m_playbackRate == rate)
+ const float effectiveRate = std::max(static_cast<float>(rate), 0.0f);
+
+ if (qFuzzyCompare(m_playbackRate, effectiveRate))
return;
- m_playbackRate = rate;
- if (decoder)
- decoder->setPlaybackRate(rate);
+
+ m_playbackRate = effectiveRate;
+
+ if (m_playbackEngine)
+ m_playbackEngine->setPlaybackRate(effectiveRate);
+
+ emit playbackRateChanged(effectiveRate);
}
QUrl QFFmpegMediaPlayer::media() const
@@ -120,68 +159,177 @@ const QIODevice *QFFmpegMediaPlayer::mediaStream() const
return m_device;
}
+void QFFmpegMediaPlayer::handleIncorrectMedia(QMediaPlayer::MediaStatus status)
+{
+ seekableChanged(false);
+ audioAvailableChanged(false);
+ videoAvailableChanged(false);
+ metaDataChanged();
+ mediaStatusChanged(status);
+ m_playbackEngine = nullptr;
+};
+
void QFFmpegMediaPlayer::setMedia(const QUrl &media, QIODevice *stream)
{
+ // Wait for previous unfinished load attempts.
+ if (m_cancelToken)
+ m_cancelToken->cancel();
+
+ m_loadMedia.waitForFinished();
+
m_url = media;
m_device = stream;
- if (decoder)
- delete decoder;
- decoder = nullptr;
-
- positionChanged(0);
+ m_playbackEngine = nullptr;
if (media.isEmpty() && !stream) {
- seekableChanged(false);
- audioAvailableChanged(false);
- videoAvailableChanged(false);
- metaDataChanged();
- mediaStatusChanged(QMediaPlayer::NoMedia);
+ handleIncorrectMedia(QMediaPlayer::NoMedia);
return;
}
mediaStatusChanged(QMediaPlayer::LoadingMedia);
- decoder = new Decoder(this);
- decoder->setMedia(media, stream);
- decoder->setAudioSink(m_audioOutput);
- decoder->setVideoSink(m_videoSink);
+ m_requestedStatus = QMediaPlayer::StoppedState;
+
+ m_cancelToken = std::make_shared<CancelToken>();
+
+ // Load media asynchronously to keep GUI thread responsive while loading media
+ m_loadMedia = QtConcurrent::run([this, media, stream, cancelToken = m_cancelToken] {
+ // On worker thread
+ const MediaDataHolder::Maybe mediaHolder =
+ MediaDataHolder::create(media, stream, cancelToken);
+
+ // Transition back to calling thread using invokeMethod because
+ // QFuture continuations back on calling thread may deadlock (QTBUG-117918)
+ QMetaObject::invokeMethod(this, [this, mediaHolder, cancelToken] {
+ setMediaAsync(mediaHolder, cancelToken);
+ });
+ });
+}
+
+void QFFmpegMediaPlayer::setMediaAsync(QFFmpeg::MediaDataHolder::Maybe mediaDataHolder,
+ const std::shared_ptr<QFFmpeg::CancelToken> &cancelToken)
+{
+ Q_ASSERT(mediaStatus() == QMediaPlayer::LoadingMedia);
+
+ // If loading was cancelled, we do not emit any signals about failing
+ // to load media (or any other events). The rationale is that cancellation
+ // either happens during destruction, where the signals are no longer
+ // of interest, or it happens as a response to user requesting to load
+ // another media file. In the latter case, we don't want to risk popping
+ // up error dialogs or similar.
+ if (cancelToken->isCancelled()) {
+ return;
+ }
+
+ if (!mediaDataHolder) {
+ const auto [code, description] = mediaDataHolder.error();
+ error(code, description);
+ handleIncorrectMedia(QMediaPlayer::MediaStatus::InvalidMedia);
+ return;
+ }
+
+ m_playbackEngine = std::make_unique<PlaybackEngine>();
+
+ connect(m_playbackEngine.get(), &PlaybackEngine::endOfStream, this,
+ &QFFmpegMediaPlayer::endOfStream);
+ connect(m_playbackEngine.get(), &PlaybackEngine::errorOccured, this,
+ &QFFmpegMediaPlayer::error);
+ connect(m_playbackEngine.get(), &PlaybackEngine::loopChanged, this,
+ &QFFmpegMediaPlayer::onLoopChanged);
+ connect(m_playbackEngine.get(), &PlaybackEngine::buffered, this,
+ &QFFmpegMediaPlayer::onBuffered);
+
+ m_playbackEngine->setMedia(std::move(*mediaDataHolder.value()));
+
+ m_playbackEngine->setAudioSink(m_audioOutput);
+ m_playbackEngine->setVideoSink(m_videoSink);
+ m_playbackEngine->setLoops(loops());
+ m_playbackEngine->setPlaybackRate(m_playbackRate);
+
+ durationChanged(duration());
+ tracksChanged();
metaDataChanged();
- seekableChanged(decoder->isSeekable());
+ seekableChanged(m_playbackEngine->isSeekable());
- audioAvailableChanged(!decoder->m_streamMap[QPlatformMediaPlayer::AudioStream].isEmpty());
- videoAvailableChanged(!decoder->m_streamMap[QPlatformMediaPlayer::VideoStream].isEmpty());
+ audioAvailableChanged(
+ !m_playbackEngine->streamInfo(QPlatformMediaPlayer::AudioStream).isEmpty());
+ videoAvailableChanged(
+ !m_playbackEngine->streamInfo(QPlatformMediaPlayer::VideoStream).isEmpty());
- QMetaObject::invokeMethod(this, "delayedLoadedStatus", Qt::QueuedConnection);
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+
+ if (m_requestedStatus != QMediaPlayer::StoppedState) {
+ if (m_requestedStatus == QMediaPlayer::PlayingState)
+ play();
+ else if (m_requestedStatus == QMediaPlayer::PausedState)
+ pause();
+ }
}
void QFFmpegMediaPlayer::play()
{
- if (!decoder)
+ if (mediaStatus() == QMediaPlayer::LoadingMedia) {
+ m_requestedStatus = QMediaPlayer::PlayingState;
+ return;
+ }
+
+ if (!m_playbackEngine)
return;
- if (mediaStatus() == QMediaPlayer::EndOfMedia && state() == QMediaPlayer::StoppedState)
- decoder->seek(0);
- decoder->play();
+ if (mediaStatus() == QMediaPlayer::EndOfMedia && state() == QMediaPlayer::StoppedState) {
+ m_playbackEngine->seek(0);
+ positionChanged(0);
+ }
+
+ runPlayback();
+}
+
+void QFFmpegMediaPlayer::runPlayback()
+{
+ m_playbackEngine->play();
+ m_positionUpdateTimer.start();
stateChanged(QMediaPlayer::PlayingState);
- mediaStatusChanged(QMediaPlayer::BufferedMedia);
+
+ if (mediaStatus() == QMediaPlayer::LoadedMedia || mediaStatus() == QMediaPlayer::EndOfMedia)
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
}
void QFFmpegMediaPlayer::pause()
{
- if (!decoder)
+ if (mediaStatus() == QMediaPlayer::LoadingMedia) {
+ m_requestedStatus = QMediaPlayer::PausedState;
+ return;
+ }
+
+ if (!m_playbackEngine)
return;
- if (mediaStatus() == QMediaPlayer::EndOfMedia && state() == QMediaPlayer::StoppedState)
- decoder->seek(0);
- decoder->pause();
+
+ if (mediaStatus() == QMediaPlayer::EndOfMedia && state() == QMediaPlayer::StoppedState) {
+ m_playbackEngine->seek(0);
+ positionChanged(0);
+ }
+ m_playbackEngine->pause();
+ m_positionUpdateTimer.stop();
stateChanged(QMediaPlayer::PausedState);
- mediaStatusChanged(QMediaPlayer::BufferedMedia);
+
+ if (mediaStatus() == QMediaPlayer::LoadedMedia || mediaStatus() == QMediaPlayer::EndOfMedia)
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
}
void QFFmpegMediaPlayer::stop()
{
- if (!decoder)
+ if (mediaStatus() == QMediaPlayer::LoadingMedia) {
+ m_requestedStatus = QMediaPlayer::StoppedState;
+ return;
+ }
+
+ if (!m_playbackEngine)
return;
- decoder->stop();
+
+ m_playbackEngine->stop();
+ m_positionUpdateTimer.stop();
+ m_playbackEngine->seek(0);
+ positionChanged(0);
stateChanged(QMediaPlayer::StoppedState);
mediaStatusChanged(QMediaPlayer::LoadedMedia);
}
@@ -192,13 +340,13 @@ void QFFmpegMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
return;
m_audioOutput = output;
- if (decoder)
- decoder->setAudioSink(output);
+ if (m_playbackEngine)
+ m_playbackEngine->setAudioSink(output);
}
QMediaMetaData QFFmpegMediaPlayer::metaData() const
{
- return decoder ? decoder->m_metaData : QMediaMetaData{};
+ return m_playbackEngine ? m_playbackEngine->metaData() : QMediaMetaData{};
}
void QFFmpegMediaPlayer::setVideoSink(QVideoSink *sink)
@@ -207,8 +355,8 @@ void QFFmpegMediaPlayer::setVideoSink(QVideoSink *sink)
return;
m_videoSink = sink;
- if (decoder)
- decoder->setVideoSink(sink);
+ if (m_playbackEngine)
+ m_playbackEngine->setVideoSink(sink);
}
QVideoSink *QFFmpegMediaPlayer::videoSink() const
@@ -218,25 +366,38 @@ QVideoSink *QFFmpegMediaPlayer::videoSink() const
int QFFmpegMediaPlayer::trackCount(TrackType type)
{
- return decoder ? decoder->m_streamMap[type].count() : 0;
+ return m_playbackEngine ? m_playbackEngine->streamInfo(type).count() : 0;
}
QMediaMetaData QFFmpegMediaPlayer::trackMetaData(TrackType type, int streamNumber)
{
- if (!decoder || streamNumber < 0 || streamNumber >= decoder->m_streamMap[type].count())
+ if (!m_playbackEngine || streamNumber < 0
+ || streamNumber >= m_playbackEngine->streamInfo(type).count())
return {};
- return decoder->m_streamMap[type].at(streamNumber).metaData;
+ return m_playbackEngine->streamInfo(type).at(streamNumber).metaData;
}
int QFFmpegMediaPlayer::activeTrack(TrackType type)
{
- return decoder ? decoder->m_requestedStreams[type] : -1;
+ return m_playbackEngine ? m_playbackEngine->activeTrack(type) : -1;
}
void QFFmpegMediaPlayer::setActiveTrack(TrackType type, int streamNumber)
{
- if (decoder)
- decoder->setActiveTrack(type, streamNumber);
+ if (m_playbackEngine)
+ m_playbackEngine->setActiveTrack(type, streamNumber);
+ else
+ qWarning() << "Cannot set active track without open source";
+}
+
+void QFFmpegMediaPlayer::setLoops(int loops)
+{
+ if (m_playbackEngine)
+ m_playbackEngine->setLoops(loops);
+
+ QPlatformMediaPlayer::setLoops(loops);
}
QT_END_NAMESPACE
+
+#include "moc_qffmpegmediaplayer_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h
index 74d95b67c..4e700787e 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -53,13 +17,20 @@
#include <private/qplatformmediaplayer_p.h>
#include <qmediametadata.h>
+#include <qtimer.h>
+#include <qpointer.h>
+#include <qfuture.h>
#include "qffmpeg_p.h"
+#include "playbackengine/qffmpegmediadataholder_p.h"
QT_BEGIN_NAMESPACE
namespace QFFmpeg {
-class Decoder;
+class CancelToken;
+
+class PlaybackEngine;
}
+
class QPlatformAudioOutput;
class QFFmpegMediaPlayer : public QObject, public QPlatformMediaPlayer
@@ -88,8 +59,6 @@ public:
void pause() override;
void stop() override;
-// bool streamPlaybackSupported() const { return false; }
-
void setAudioOutput(QPlatformAudioOutput *) override;
QMediaMetaData metaData() const override;
@@ -101,21 +70,43 @@ public:
QMediaMetaData trackMetaData(TrackType type, int streamNumber) override;
int activeTrack(TrackType) override;
void setActiveTrack(TrackType, int streamNumber) override;
+ void setLoops(int loops) override;
- Q_INVOKABLE void delayedLoadedStatus() { mediaStatusChanged(QMediaPlayer::LoadedMedia); }
+private:
+ void runPlayback();
+ void handleIncorrectMedia(QMediaPlayer::MediaStatus status);
+ void setMediaAsync(QFFmpeg::MediaDataHolder::Maybe mediaDataHolder,
+ const std::shared_ptr<QFFmpeg::CancelToken> &cancelToken);
+
+ void mediaStatusChanged(QMediaPlayer::MediaStatus);
+
+private slots:
+ void updatePosition();
+ void endOfStream();
+ void error(int error, const QString &errorString)
+ {
+ QPlatformMediaPlayer::error(error, errorString);
+ }
+ void onLoopChanged();
+ void onBuffered();
private:
- friend class QFFmpeg::Decoder;
+ QTimer m_positionUpdateTimer;
+ QMediaPlayer::PlaybackState m_requestedStatus = QMediaPlayer::StoppedState;
- QFFmpeg::Decoder *decoder = nullptr;
- void checkStreams();
+ using PlaybackEngine = QFFmpeg::PlaybackEngine;
+ std::unique_ptr<PlaybackEngine> m_playbackEngine;
QPlatformAudioOutput *m_audioOutput = nullptr;
- QVideoSink *m_videoSink = nullptr;
+ QPointer<QVideoSink> m_videoSink;
QUrl m_url;
- QIODevice *m_device = nullptr;
+ QPointer<QIODevice> m_device;
float m_playbackRate = 1.;
+ float m_bufferProgress = 0.f;
+ QFuture<void> m_loadMedia;
+ std::shared_ptr<QFFmpeg::CancelToken> m_cancelToken; // For interrupting ongoing
+ // network connection attempt
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp
index 5acb50c1b..67eb46eb9 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp
@@ -1,71 +1,30 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegmediarecorder_p.h"
#include "qaudiodevice.h"
#include <private/qmediastoragelocation_p.h>
#include <private/qplatformcamera_p.h>
+#include <private/qplatformsurfacecapture_p.h>
#include "qaudiosource.h"
#include "qffmpegaudioinput_p.h"
#include "qaudiobuffer.h"
-#include "qffmpegencoder_p.h"
-#include "qffmpegmediaformatinfo_p.h"
+#include "recordingengine/qffmpegrecordingengine_p.h"
+#include "qffmpegmediacapturesession_p.h"
#include <qdebug.h>
-#include <qeventloop.h>
-#include <qstandardpaths.h>
-#include <qmimetype.h>
#include <qloggingcategory.h>
-Q_LOGGING_CATEGORY(qLcMediaEncoder, "qt.multimedia.encoder")
+static Q_LOGGING_CATEGORY(qLcMediaEncoder, "qt.multimedia.ffmpeg.encoder");
-QFFmpegMediaRecorder::QFFmpegMediaRecorder(QMediaRecorder *parent)
- : QPlatformMediaRecorder(parent)
-{
-}
+QT_BEGIN_NAMESPACE
-QFFmpegMediaRecorder::~QFFmpegMediaRecorder()
+QFFmpegMediaRecorder::QFFmpegMediaRecorder(QMediaRecorder *parent) : QPlatformMediaRecorder(parent)
{
- if (encoder)
- encoder->finalize();
}
+QFFmpegMediaRecorder::~QFFmpegMediaRecorder() = default;
+
bool QFFmpegMediaRecorder::isLocationWritable(const QUrl &) const
{
return true;
@@ -73,7 +32,7 @@ bool QFFmpegMediaRecorder::isLocationWritable(const QUrl &) const
void QFFmpegMediaRecorder::handleSessionError(QMediaRecorder::Error code, const QString &description)
{
- error(code, description);
+ updateError(code, description);
stop();
}
@@ -82,45 +41,67 @@ void QFFmpegMediaRecorder::record(QMediaEncoderSettings &settings)
if (!m_session || state() != QMediaRecorder::StoppedState)
return;
- const auto hasVideo = m_session->camera() && m_session->camera()->isActive();
+ auto videoSources = m_session->activeVideoSources();
+ const auto hasVideo = !videoSources.empty();
const auto hasAudio = m_session->audioInput() != nullptr;
if (!hasVideo && !hasAudio) {
- error(QMediaRecorder::ResourceError, QMediaRecorder::tr("No camera or audio input"));
+ updateError(QMediaRecorder::ResourceError, QMediaRecorder::tr("No video or audio input"));
return;
}
- const auto audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified;
+ if (outputDevice() && !outputLocation().isEmpty())
+ qCWarning(qLcMediaEncoder)
+ << "Both outputDevice and outputLocation has been set to QMediaRecorder";
+
+ if (outputDevice() && !outputDevice()->isWritable())
+ qCWarning(qLcMediaEncoder) << "Output device has been set but not it's not writable";
+
+ QString actualLocation;
+ auto formatContext = std::make_unique<QFFmpeg::EncodingFormatContext>(settings.fileFormat());
- auto primaryLocation = audioOnly ? QStandardPaths::MusicLocation : QStandardPaths::MoviesLocation;
- auto container = settings.mimeType().preferredSuffix();
- auto location = QMediaStorageLocation::generateFileName(outputLocation().toLocalFile(), primaryLocation, container);
+ if (outputDevice() && outputDevice()->isWritable()) {
+ formatContext->openAVIO(outputDevice());
+ } else {
+ actualLocation = findActualLocation(settings);
+ qCDebug(qLcMediaEncoder) << "recording new media to" << actualLocation;
+ formatContext->openAVIO(actualLocation);
+ }
- QUrl actualSink = QUrl::fromLocalFile(QDir::currentPath()).resolved(location);
- qCDebug(qLcMediaEncoder) << "recording new video to" << actualSink;
- qDebug() << "requested format:" << settings.fileFormat() << settings.audioCodec();
+ qCDebug(qLcMediaEncoder) << "requested format:" << settings.fileFormat()
+ << settings.audioCodec();
- Q_ASSERT(!actualSink.isEmpty());
+ if (!formatContext->isAVIOOpen()) {
+ updateError(QMediaRecorder::LocationNotWritable,
+ QMediaRecorder::tr("Cannot open the output location for writing"));
+ return;
+ }
- encoder = new QFFmpeg::Encoder(settings, actualSink);
- encoder->setMetaData(m_metaData);
- connect(encoder, &QFFmpeg::Encoder::durationChanged, this, &QFFmpegMediaRecorder::newDuration);
- connect(encoder, &QFFmpeg::Encoder::finalizationDone, this, &QFFmpegMediaRecorder::finalizationDone);
- connect(encoder, &QFFmpeg::Encoder::error, this, &QFFmpegMediaRecorder::handleSessionError);
+ m_recordingEngine.reset(new RecordingEngine(settings, std::move(formatContext)));
+ m_recordingEngine->setMetaData(m_metaData);
+ connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::durationChanged, this,
+ &QFFmpegMediaRecorder::newDuration);
+ connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::finalizationDone, this,
+ &QFFmpegMediaRecorder::finalizationDone);
+ connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::error, this,
+ &QFFmpegMediaRecorder::handleSessionError);
auto *audioInput = m_session->audioInput();
- if (audioInput)
- encoder->addAudioInput(static_cast<QFFmpegAudioInput *>(audioInput));
+ if (audioInput) {
+ if (audioInput->device.isNull())
+ qWarning() << "Audio input device is null; cannot encode audio";
+ else
+ m_recordingEngine->addAudioInput(static_cast<QFFmpegAudioInput *>(audioInput));
+ }
- auto *camera = m_session->camera();
- if (camera)
- encoder->addVideoSource(camera);
+ for (auto source : videoSources)
+ m_recordingEngine->addVideoSource(source);
durationChanged(0);
stateChanged(QMediaRecorder::RecordingState);
- actualLocationChanged(QUrl::fromLocalFile(location));
+ actualLocationChanged(QUrl::fromLocalFile(actualLocation));
- encoder->start();
+ m_recordingEngine->start();
}
void QFFmpegMediaRecorder::pause()
@@ -128,8 +109,8 @@ void QFFmpegMediaRecorder::pause()
if (!m_session || state() != QMediaRecorder::RecordingState)
return;
- Q_ASSERT(encoder);
- encoder->setPaused(true);
+ Q_ASSERT(m_recordingEngine);
+ m_recordingEngine->setPaused(true);
stateChanged(QMediaRecorder::PausedState);
}
@@ -139,8 +120,8 @@ void QFFmpegMediaRecorder::resume()
if (!m_session || state() != QMediaRecorder::PausedState)
return;
- Q_ASSERT(encoder);
- encoder->setPaused(false);
+ Q_ASSERT(m_recordingEngine);
+ m_recordingEngine->setPaused(false);
stateChanged(QMediaRecorder::RecordingState);
}
@@ -153,12 +134,8 @@ void QFFmpegMediaRecorder::stop()
if (input)
static_cast<QFFmpegAudioInput *>(input)->setRunning(false);
qCDebug(qLcMediaEncoder) << "stop";
- // ### all of the below should be done asynchronous. finalize() should do it's work in a thread
- // to avoid blocking the UI in case of slow codecs
- if (encoder) {
- encoder->finalize();
- encoder = nullptr;
- }
+
+ m_recordingEngine.reset();
}
void QFFmpegMediaRecorder::finalizationDone()
@@ -178,9 +155,9 @@ QMediaMetaData QFFmpegMediaRecorder::metaData() const
return m_metaData;
}
-void QFFmpegMediaRecorder::setCaptureSession(QPlatformMediaCaptureSession *session)
+void QFFmpegMediaRecorder::setCaptureSession(QFFmpegMediaCaptureSession *session)
{
- auto *captureSession = static_cast<QFFmpegMediaCaptureSession *>(session);
+ auto *captureSession = session;
if (m_session == captureSession)
return;
@@ -191,3 +168,15 @@ void QFFmpegMediaRecorder::setCaptureSession(QPlatformMediaCaptureSession *sessi
if (!m_session)
return;
}
+
+void QFFmpegMediaRecorder::RecordingEngineDeleter::operator()(
+ RecordingEngine *recordingEngine) const
+{
+ // ### all of the below should be done asynchronous. finalize() should do it's work in a thread
+ // to avoid blocking the UI in case of slow codecs
+ recordingEngine->finalize();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegmediarecorder_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h
index 8ca9c046d..8b73ad76d 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h
@@ -1,42 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGMEDIARECODER_H
#define QFFMPEGMEDIARECODER_H
@@ -53,9 +16,6 @@
//
#include <private/qplatformmediarecorder_p.h>
-#include "qffmpegmediacapturesession_p.h"
-
-#include "qffmpeg_p.h"
QT_BEGIN_NAMESPACE
@@ -63,9 +23,10 @@ class QAudioSource;
class QAudioSourceIO;
class QAudioBuffer;
class QMediaMetaData;
+class QFFmpegMediaCaptureSession;
namespace QFFmpeg {
-class Encoder;
+class RecordingEngine;
}
class QFFmpegMediaRecorder : public QObject, public QPlatformMediaRecorder
@@ -85,7 +46,7 @@ public:
void setMetaData(const QMediaMetaData &) override;
QMediaMetaData metaData() const override;
- void setCaptureSession(QPlatformMediaCaptureSession *session);
+ void setCaptureSession(QFFmpegMediaCaptureSession *session);
private Q_SLOTS:
void newDuration(qint64 d) { durationChanged(d); }
@@ -93,10 +54,16 @@ private Q_SLOTS:
void handleSessionError(QMediaRecorder::Error code, const QString &description);
private:
+ using RecordingEngine = QFFmpeg::RecordingEngine;
+ struct RecordingEngineDeleter
+ {
+ void operator()(RecordingEngine *) const;
+ };
+
QFFmpegMediaCaptureSession *m_session = nullptr;
QMediaMetaData m_metaData;
- QFFmpeg::Encoder *encoder = nullptr;
+ std::unique_ptr<RecordingEngine, RecordingEngineDeleter> m_recordingEngine;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegopensslsymbols.cpp b/src/plugins/multimedia/ffmpeg/qffmpegopensslsymbols.cpp
new file mode 100644
index 000000000..e0e5de137
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegopensslsymbols.cpp
@@ -0,0 +1,185 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtCore/qlibrary.h>
+
+#include "qffmpegsymbolsresolveutils_p.h"
+
+#include <QtCore/qglobal.h>
+#include <qstringliteral.h>
+
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+static Libs loadLibs()
+{
+ Libs libs(2);
+ libs[0] = std::make_unique<QLibrary>();
+ libs[1] = std::make_unique<QLibrary>();
+
+ const auto majorVersion = OPENSSL_VERSION_NUMBER >> 28;
+
+ auto tryLoad = [&](QString sslName, QString cryptoName, auto version) {
+ libs[0]->setFileNameAndVersion(sslName, version);
+ libs[1]->setFileNameAndVersion(cryptoName, version);
+ return LibSymbolsResolver::tryLoad(libs);
+ };
+
+// Due to binary compatibility issues between 1.x.x openssl version, let's try taking exact version
+#if defined(SHLIB_VERSION_NUMBER)
+ if (majorVersion <= 1 && tryLoad("ssl"_L1, "crypto"_L1, SHLIB_VERSION_NUMBER ""_L1))
+ return libs;
+#endif
+
+// openssl on Android has specific suffixes
+#if defined(Q_OS_ANDROID)
+ {
+ auto suffix = qEnvironmentVariable("ANDROID_OPENSSL_SUFFIX");
+ if (suffix.isEmpty())
+ suffix = QString("_"_L1) + QString::number(majorVersion);
+
+ if (tryLoad("ssl"_L1 + suffix, "crypto"_L1 + suffix, -1))
+ return libs;
+ }
+#endif
+
+ if (tryLoad("ssl"_L1, "crypto"_L1, majorVersion))
+ return libs;
+
+ return {};
+};
+
+Q_GLOBAL_STATIC(LibSymbolsResolver, resolver, "OpenSsl", 75, loadLibs);
+
+void resolveOpenSsl()
+{
+ resolver()->resolve();
+}
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+// BN functions
+
+DEFINE_FUNC(BN_value_one, 0);
+DEFINE_FUNC(BN_mod_word, 2);
+
+DEFINE_FUNC(BN_div_word, 2)
+DEFINE_FUNC(BN_mul_word, 2)
+DEFINE_FUNC(BN_add_word, 2)
+DEFINE_FUNC(BN_sub_word, 2)
+DEFINE_FUNC(BN_set_word, 2)
+DEFINE_FUNC(BN_new, 0)
+DEFINE_FUNC(BN_cmp, 2)
+
+DEFINE_FUNC(BN_free, 1);
+
+DEFINE_FUNC(BN_copy, 2);
+
+DEFINE_FUNC(BN_CTX_new, 0);
+
+DEFINE_FUNC(BN_CTX_free, 1);
+DEFINE_FUNC(BN_CTX_start, 1);
+
+DEFINE_FUNC(BN_CTX_get, 1);
+DEFINE_FUNC(BN_CTX_end, 1);
+
+DEFINE_FUNC(BN_rand, 4);
+DEFINE_FUNC(BN_mod_exp, 5);
+
+DEFINE_FUNC(BN_num_bits, 1);
+DEFINE_FUNC(BN_num_bits_word, 1);
+
+DEFINE_FUNC(BN_bn2hex, 1);
+DEFINE_FUNC(BN_bn2dec, 1);
+
+DEFINE_FUNC(BN_hex2bn, 2);
+DEFINE_FUNC(BN_dec2bn, 2);
+DEFINE_FUNC(BN_asc2bn, 2);
+
+DEFINE_FUNC(BN_bn2bin, 2);
+DEFINE_FUNC(BN_bin2bn, 3);
+
+// BIO-related functions
+
+DEFINE_FUNC(BIO_new, 1);
+DEFINE_FUNC(BIO_free, 1);
+
+DEFINE_FUNC(BIO_read, 3, -1);
+DEFINE_FUNC(BIO_write, 3, -1);
+DEFINE_FUNC(BIO_s_mem, 0);
+
+DEFINE_FUNC(BIO_set_data, 2);
+
+DEFINE_FUNC(BIO_get_data, 1);
+DEFINE_FUNC(BIO_set_init, 2);
+
+DEFINE_FUNC(BIO_set_flags, 2);
+DEFINE_FUNC(BIO_test_flags, 2);
+DEFINE_FUNC(BIO_clear_flags, 2);
+
+DEFINE_FUNC(BIO_meth_new, 2);
+DEFINE_FUNC(BIO_meth_free, 1);
+
+DEFINE_FUNC(BIO_meth_set_write, 2);
+DEFINE_FUNC(BIO_meth_set_read, 2);
+DEFINE_FUNC(BIO_meth_set_puts, 2);
+DEFINE_FUNC(BIO_meth_set_gets, 2);
+DEFINE_FUNC(BIO_meth_set_ctrl, 2);
+DEFINE_FUNC(BIO_meth_set_create, 2);
+DEFINE_FUNC(BIO_meth_set_destroy, 2);
+DEFINE_FUNC(BIO_meth_set_callback_ctrl, 2);
+
+// SSL functions
+
+DEFINE_FUNC(SSL_CTX_new, 1);
+DEFINE_FUNC(SSL_CTX_up_ref, 1);
+DEFINE_FUNC(SSL_CTX_free, 1);
+
+DEFINE_FUNC(SSL_new, 1);
+DEFINE_FUNC(SSL_up_ref, 1);
+DEFINE_FUNC(SSL_free, 1);
+
+DEFINE_FUNC(SSL_accept, 1);
+DEFINE_FUNC(SSL_stateless, 1);
+DEFINE_FUNC(SSL_connect, 1);
+DEFINE_FUNC(SSL_read, 3, -1);
+DEFINE_FUNC(SSL_peek, 3);
+DEFINE_FUNC(SSL_write, 3, -1);
+DEFINE_FUNC(SSL_ctrl, 4);
+DEFINE_FUNC(SSL_shutdown, 1);
+DEFINE_FUNC(SSL_set_bio, 3);
+
+// options are unsigned long in openssl 1.1.1, and uint64 in 3.x.x
+DEFINE_FUNC(SSL_CTX_set_options, 2);
+
+DEFINE_FUNC(SSL_get_error, 2);
+DEFINE_FUNC(SSL_CTX_load_verify_locations, 3, -1);
+
+DEFINE_FUNC(SSL_CTX_set_verify, 3);
+DEFINE_FUNC(SSL_CTX_use_PrivateKey, 2);
+
+DEFINE_FUNC(SSL_CTX_use_PrivateKey_file, 3);
+DEFINE_FUNC(SSL_CTX_use_certificate_chain_file, 2);
+
+DEFINE_FUNC(ERR_get_error, 0);
+
+static char ErrorString[] = "Ssl not found";
+DEFINE_FUNC(ERR_error_string, 2, ErrorString);
+
+// TLS functions
+
+DEFINE_FUNC(TLS_client_method, 0);
+DEFINE_FUNC(TLS_server_method, 0);
+
+// RAND functions
+
+DEFINE_FUNC(RAND_bytes, 2);
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp
new file mode 100644
index 000000000..811feb0d5
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp
@@ -0,0 +1,636 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qffmpegplaybackengine_p.h"
+
+#include "qvideosink.h"
+#include "qaudiooutput.h"
+#include "private/qplatformaudiooutput_p.h"
+#include "private/qplatformvideosink_p.h"
+#include "qiodevice.h"
+#include "playbackengine/qffmpegdemuxer_p.h"
+#include "playbackengine/qffmpegstreamdecoder_p.h"
+#include "playbackengine/qffmpegsubtitlerenderer_p.h"
+#include "playbackengine/qffmpegvideorenderer_p.h"
+#include "playbackengine/qffmpegaudiorenderer_p.h"
+
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+static Q_LOGGING_CATEGORY(qLcPlaybackEngine, "qt.multimedia.ffmpeg.playbackengine");
+
+// The helper is needed since on some compilers std::unique_ptr
+// doesn't have a default constructor in the case of sizeof(CustomDeleter) > 0
+template<typename Array>
+inline Array defaultObjectsArray()
+{
+ using T = typename Array::value_type;
+ return { T{ {}, {} }, T{ {}, {} }, T{ {}, {} } };
+}
+
+// TODO: investigate what's better: profile and try network case
+// Most likely, shouldPauseStreams = false is better because of:
+// - packet and frame buffers are not big, the saturration of the is pretty fast.
+// - after any pause a user has some preloaded buffers, so the playback is
+// supposed to be more stable in cases with a weak processor or bad internet.
+// - the code is simplier, usage is more convenient.
+//
+static constexpr bool shouldPauseStreams = false;
+
+PlaybackEngine::PlaybackEngine()
+ : m_demuxer({}, {}),
+ m_streams(defaultObjectsArray<decltype(m_streams)>()),
+ m_renderers(defaultObjectsArray<decltype(m_renderers)>())
+{
+ qCDebug(qLcPlaybackEngine) << "Create PlaybackEngine";
+ qRegisterMetaType<QFFmpeg::Packet>();
+ qRegisterMetaType<QFFmpeg::Frame>();
+}
+
+PlaybackEngine::~PlaybackEngine() {
+ qCDebug(qLcPlaybackEngine) << "Delete PlaybackEngine";
+
+ finalizeOutputs();
+ forEachExistingObject([](auto &object) { object.reset(); });
+ deleteFreeThreads();
+}
+
+void PlaybackEngine::onRendererFinished()
+{
+ auto isAtEnd = [this](auto trackType) {
+ return !m_renderers[trackType] || m_renderers[trackType]->isAtEnd();
+ };
+
+ if (!isAtEnd(QPlatformMediaPlayer::VideoStream))
+ return;
+
+ if (!isAtEnd(QPlatformMediaPlayer::AudioStream))
+ return;
+
+ if (!isAtEnd(QPlatformMediaPlayer::SubtitleStream) && !hasMediaStream())
+ return;
+
+ if (std::exchange(m_state, QMediaPlayer::StoppedState) == QMediaPlayer::StoppedState)
+ return;
+
+ finilizeTime(duration());
+
+ forceUpdate();
+
+ qCDebug(qLcPlaybackEngine) << "Playback engine end of stream";
+
+ emit endOfStream();
+}
+
+void PlaybackEngine::onRendererLoopChanged(quint64 id, qint64 offset, int loopIndex)
+{
+ if (!hasRenderer(id))
+ return;
+
+ if (loopIndex > m_currentLoopOffset.index) {
+ m_currentLoopOffset = { offset, loopIndex };
+ emit loopChanged();
+ } else if (loopIndex == m_currentLoopOffset.index && offset != m_currentLoopOffset.pos) {
+ qWarning() << "Unexpected offset for loop" << loopIndex << ":" << offset << "vs"
+ << m_currentLoopOffset.pos;
+ m_currentLoopOffset.pos = offset;
+ }
+}
+
+void PlaybackEngine::onRendererSynchronized(quint64 id, std::chrono::steady_clock::time_point tp,
+ qint64 pos)
+{
+ if (!hasRenderer(id))
+ return;
+
+ Q_ASSERT(m_renderers[QPlatformMediaPlayer::AudioStream]
+ && m_renderers[QPlatformMediaPlayer::AudioStream]->id() == id);
+
+ m_timeController.sync(tp, pos);
+
+ forEachExistingObject<Renderer>([&](auto &renderer) {
+ if (id != renderer->id())
+ renderer->syncSoft(tp, pos);
+ });
+}
+
+void PlaybackEngine::setState(QMediaPlayer::PlaybackState state) {
+ if (!m_media.avContext())
+ return;
+
+ if (state == m_state)
+ return;
+
+ const auto prevState = std::exchange(m_state, state);
+
+ if (m_state == QMediaPlayer::StoppedState) {
+ finalizeOutputs();
+ finilizeTime(0);
+ }
+
+ if (prevState == QMediaPlayer::StoppedState || m_state == QMediaPlayer::StoppedState)
+ recreateObjects();
+
+ if (prevState == QMediaPlayer::StoppedState)
+ triggerStepIfNeeded();
+
+ updateObjectsPausedState();
+}
+
+void PlaybackEngine::updateObjectsPausedState()
+{
+ const auto paused = m_state != QMediaPlayer::PlayingState;
+ m_timeController.setPaused(paused);
+
+ forEachExistingObject([&](auto &object) {
+ bool objectPaused = false;
+
+ if constexpr (std::is_same_v<decltype(*object), Renderer &>)
+ objectPaused = paused;
+ else if constexpr (shouldPauseStreams) {
+ auto streamPaused = [](bool p, auto &r) {
+ const auto needMoreFrames = r && r->stepInProgress();
+ return p && !needMoreFrames;
+ };
+
+ if constexpr (std::is_same_v<decltype(*object), StreamDecoder &>)
+ objectPaused = streamPaused(paused, renderer(object->trackType()));
+ else
+ objectPaused = std::accumulate(m_renderers.begin(), m_renderers.end(), paused,
+ streamPaused);
+ }
+
+ object->setPaused(objectPaused);
+ });
+}
+
+void PlaybackEngine::ObjectDeleter::operator()(PlaybackEngineObject *object) const
+{
+ Q_ASSERT(engine);
+ if (!std::exchange(engine->m_threadsDirty, true))
+ QMetaObject::invokeMethod(engine, &PlaybackEngine::deleteFreeThreads, Qt::QueuedConnection);
+
+ object->kill();
+}
+
+void PlaybackEngine::registerObject(PlaybackEngineObject &object)
+{
+ connect(&object, &PlaybackEngineObject::error, this, &PlaybackEngine::errorOccured);
+
+ auto threadName = objectThreadName(object);
+ auto &thread = m_threads[threadName];
+ if (!thread) {
+ thread = std::make_unique<QThread>();
+ thread->setObjectName(threadName);
+ thread->start();
+ }
+
+ Q_ASSERT(object.thread() != thread.get());
+ object.moveToThread(thread.get());
+}
+
+PlaybackEngine::RendererPtr
+PlaybackEngine::createRenderer(QPlatformMediaPlayer::TrackType trackType)
+{
+ switch (trackType) {
+ case QPlatformMediaPlayer::VideoStream:
+ return m_videoSink
+ ? createPlaybackEngineObject<VideoRenderer>(m_timeController, m_videoSink, m_media.rotation())
+ : RendererPtr{ {}, {} };
+ case QPlatformMediaPlayer::AudioStream:
+ return m_audioOutput
+ ? createPlaybackEngineObject<AudioRenderer>(m_timeController, m_audioOutput)
+ : RendererPtr{ {}, {} };
+ case QPlatformMediaPlayer::SubtitleStream:
+ return m_videoSink
+ ? createPlaybackEngineObject<SubtitleRenderer>(m_timeController, m_videoSink)
+ : RendererPtr{ {}, {} };
+ default:
+ return { {}, {} };
+ }
+}
+
+template<typename C, typename Action>
+void PlaybackEngine::forEachExistingObject(Action &&action)
+{
+ auto handleNotNullObject = [&](auto &object) {
+ if constexpr (std::is_base_of_v<C, std::remove_reference_t<decltype(*object)>>)
+ if (object)
+ action(object);
+ };
+
+ handleNotNullObject(m_demuxer);
+ std::for_each(m_streams.begin(), m_streams.end(), handleNotNullObject);
+ std::for_each(m_renderers.begin(), m_renderers.end(), handleNotNullObject);
+}
+
+template<typename Action>
+void PlaybackEngine::forEachExistingObject(Action &&action)
+{
+ forEachExistingObject<PlaybackEngineObject>(std::forward<Action>(action));
+}
+
+void PlaybackEngine::seek(qint64 pos)
+{
+ pos = boundPosition(pos);
+
+ m_timeController.setPaused(true);
+ m_timeController.sync(m_currentLoopOffset.pos + pos);
+
+ forceUpdate();
+}
+
+void PlaybackEngine::setLoops(int loops)
+{
+ if (!isSeekable()) {
+ qWarning() << "Cannot set loops for non-seekable source";
+ return;
+ }
+
+ if (std::exchange(m_loops, loops) == loops)
+ return;
+
+ qCDebug(qLcPlaybackEngine) << "set playback engine loops:" << loops << "prev loops:" << m_loops
+ << "index:" << m_currentLoopOffset.index;
+
+ if (m_demuxer)
+ m_demuxer->setLoops(loops);
+}
+
+void PlaybackEngine::triggerStepIfNeeded()
+{
+ if (m_state != QMediaPlayer::PausedState)
+ return;
+
+ if (m_renderers[QPlatformMediaPlayer::VideoStream])
+ m_renderers[QPlatformMediaPlayer::VideoStream]->doForceStep();
+
+ // TODO: maybe trigger SubtitleStream.
+ // If trigger it, we have to make seeking for the current subtitle frame more stable.
+ // Or set some timeout for seeking.
+}
+
+QString PlaybackEngine::objectThreadName(const PlaybackEngineObject &object)
+{
+ QString result = object.metaObject()->className();
+ if (auto stream = qobject_cast<const StreamDecoder *>(&object))
+ result += QString::number(stream->trackType());
+
+ return result;
+}
+
+void PlaybackEngine::setPlaybackRate(float rate) {
+ if (rate == playbackRate())
+ return;
+
+ m_timeController.setPlaybackRate(rate);
+ forEachExistingObject<Renderer>([rate](auto &renderer) { renderer->setPlaybackRate(rate); });
+}
+
+float PlaybackEngine::playbackRate() const {
+ return m_timeController.playbackRate();
+}
+
+void PlaybackEngine::recreateObjects()
+{
+ m_timeController.setPaused(true);
+
+ forEachExistingObject([](auto &object) { object.reset(); });
+
+ createObjectsIfNeeded();
+}
+
+void PlaybackEngine::createObjectsIfNeeded()
+{
+ if (m_state == QMediaPlayer::StoppedState || !m_media.avContext())
+ return;
+
+ for (int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i)
+ createStreamAndRenderer(static_cast<QPlatformMediaPlayer::TrackType>(i));
+
+ createDemuxer();
+}
+
+void PlaybackEngine::forceUpdate()
+{
+ recreateObjects();
+ triggerStepIfNeeded();
+ updateObjectsPausedState();
+}
+
+void PlaybackEngine::createStreamAndRenderer(QPlatformMediaPlayer::TrackType trackType)
+{
+ auto codec = codecForTrack(trackType);
+
+ auto &renderer = m_renderers[trackType];
+
+ if (!codec)
+ return;
+
+ if (!renderer) {
+ renderer = createRenderer(trackType);
+
+ if (!renderer)
+ return;
+
+ connect(renderer.get(), &Renderer::synchronized, this,
+ &PlaybackEngine::onRendererSynchronized);
+
+ connect(renderer.get(), &Renderer::loopChanged, this,
+ &PlaybackEngine::onRendererLoopChanged);
+
+ if constexpr (shouldPauseStreams)
+ connect(renderer.get(), &Renderer::forceStepDone, this,
+ &PlaybackEngine::updateObjectsPausedState);
+
+ connect(renderer.get(), &PlaybackEngineObject::atEnd, this,
+ &PlaybackEngine::onRendererFinished);
+ }
+
+ auto &stream = m_streams[trackType] =
+ createPlaybackEngineObject<StreamDecoder>(*codec, renderer->seekPosition());
+
+ Q_ASSERT(trackType == stream->trackType());
+
+ connect(stream.get(), &StreamDecoder::requestHandleFrame, renderer.get(), &Renderer::render);
+ connect(stream.get(), &PlaybackEngineObject::atEnd, renderer.get(),
+ &Renderer::onFinalFrameReceived);
+ connect(renderer.get(), &Renderer::frameProcessed, stream.get(),
+ &StreamDecoder::onFrameProcessed);
+}
+
+std::optional<Codec> PlaybackEngine::codecForTrack(QPlatformMediaPlayer::TrackType trackType)
+{
+ const auto streamIndex = m_media.currentStreamIndex(trackType);
+ if (streamIndex < 0)
+ return {};
+
+ auto &result = m_codecs[trackType];
+
+ if (!result) {
+ qCDebug(qLcPlaybackEngine)
+ << "Create codec for stream:" << streamIndex << "trackType:" << trackType;
+ auto maybeCodec =
+ Codec::create(m_media.avContext()->streams[streamIndex], m_media.avContext());
+
+ if (!maybeCodec) {
+ emit errorOccured(QMediaPlayer::FormatError,
+ "Cannot create codec," + maybeCodec.error());
+ return {};
+ }
+
+ result = maybeCodec.value();
+ }
+
+ return result;
+}
+
+bool PlaybackEngine::hasMediaStream() const
+{
+ return m_renderers[QPlatformMediaPlayer::AudioStream]
+ || m_renderers[QPlatformMediaPlayer::VideoStream];
+}
+
+void PlaybackEngine::createDemuxer()
+{
+ std::array<int, QPlatformMediaPlayer::NTrackTypes> streamIndexes = { -1, -1, -1 };
+
+ bool hasStreams = false;
+ forEachExistingObject<StreamDecoder>([&](auto &stream) {
+ hasStreams = true;
+ const auto trackType = stream->trackType();
+ streamIndexes[trackType] = m_media.currentStreamIndex(trackType);
+ });
+
+ if (!hasStreams)
+ return;
+
+ const PositionWithOffset positionWithOffset{ currentPosition(false), m_currentLoopOffset };
+
+ m_demuxer = createPlaybackEngineObject<Demuxer>(m_media.avContext(), positionWithOffset,
+ streamIndexes, m_loops);
+
+ connect(m_demuxer.get(), &Demuxer::packetsBuffered, this, &PlaybackEngine::buffered);
+
+ forEachExistingObject<StreamDecoder>([&](auto &stream) {
+ connect(m_demuxer.get(), Demuxer::signalByTrackType(stream->trackType()), stream.get(),
+ &StreamDecoder::decode);
+ connect(m_demuxer.get(), &PlaybackEngineObject::atEnd, stream.get(),
+ &StreamDecoder::onFinalPacketReceived);
+ connect(stream.get(), &StreamDecoder::packetProcessed, m_demuxer.get(),
+ &Demuxer::onPacketProcessed);
+ });
+
+ if (!isSeekable() || duration() <= 0) {
+ // We need initial synchronization for such streams
+ forEachExistingObject([&](auto &object) {
+ using Type = std::remove_reference_t<decltype(*object)>;
+ if constexpr (!std::is_same_v<Type, Demuxer>)
+ connect(m_demuxer.get(), &Demuxer::firstPacketFound, object.get(),
+ &Type::setInitialPosition);
+ });
+
+ auto updateTimeController = [this](TimeController::TimePoint tp, qint64 pos) {
+ m_timeController.sync(tp, pos);
+ };
+
+ connect(m_demuxer.get(), &Demuxer::firstPacketFound, this, updateTimeController);
+ }
+}
+
+void PlaybackEngine::deleteFreeThreads() {
+ m_threadsDirty = false;
+ auto freeThreads = std::move(m_threads);
+
+ forEachExistingObject([&](auto &object) {
+ m_threads.insert(freeThreads.extract(objectThreadName(*object)));
+ });
+
+ for (auto &[name, thr] : freeThreads)
+ thr->quit();
+
+ for (auto &[name, thr] : freeThreads)
+ thr->wait();
+}
+
+void PlaybackEngine::setMedia(MediaDataHolder media)
+{
+ Q_ASSERT(!m_media.avContext()); // Playback engine does not support reloading media
+ Q_ASSERT(m_state == QMediaPlayer::StoppedState);
+ Q_ASSERT(m_threads.empty());
+
+ m_media = std::move(media);
+ updateVideoSinkSize();
+}
+
+void PlaybackEngine::setVideoSink(QVideoSink *sink)
+{
+ auto prev = std::exchange(m_videoSink, sink);
+ if (prev == sink)
+ return;
+
+ updateVideoSinkSize(prev);
+ updateActiveVideoOutput(sink);
+
+ if (!sink || !prev) {
+ // might need some improvements
+ forceUpdate();
+ }
+}
+
+void PlaybackEngine::setAudioSink(QPlatformAudioOutput *output) {
+ setAudioSink(output ? output->q : nullptr);
+}
+
+void PlaybackEngine::setAudioSink(QAudioOutput *output)
+{
+ auto prev = std::exchange(m_audioOutput, output);
+ if (prev == output)
+ return;
+
+ updateActiveAudioOutput(output);
+
+ if (!output || !prev) {
+ // might need some improvements
+ forceUpdate();
+ }
+}
+
+qint64 PlaybackEngine::currentPosition(bool topPos) const {
+ std::optional<qint64> pos;
+
+ for (size_t i = 0; i < m_renderers.size(); ++i) {
+ const auto &renderer = m_renderers[i];
+ if (!renderer)
+ continue;
+
+ // skip subtitle stream for finding lower rendering position
+ if (!topPos && i == QPlatformMediaPlayer::SubtitleStream && hasMediaStream())
+ continue;
+
+ const auto rendererPos = renderer->lastPosition();
+ pos = !pos ? rendererPos
+ : topPos ? std::max(*pos, rendererPos)
+ : std::min(*pos, rendererPos);
+ }
+
+ if (!pos)
+ pos = m_timeController.currentPosition();
+
+ return boundPosition(*pos - m_currentLoopOffset.pos);
+}
+
+qint64 PlaybackEngine::duration() const
+{
+ return m_media.duration();
+}
+
+bool PlaybackEngine::isSeekable() const { return m_media.isSeekable(); }
+
+const QList<MediaDataHolder::StreamInfo> &
+PlaybackEngine::streamInfo(QPlatformMediaPlayer::TrackType trackType) const
+{
+ return m_media.streamInfo(trackType);
+}
+
+const QMediaMetaData &PlaybackEngine::metaData() const
+{
+ return m_media.metaData();
+}
+
+int PlaybackEngine::activeTrack(QPlatformMediaPlayer::TrackType type) const
+{
+ return m_media.activeTrack(type);
+}
+
+void PlaybackEngine::setActiveTrack(QPlatformMediaPlayer::TrackType trackType, int streamNumber)
+{
+ if (!m_media.setActiveTrack(trackType, streamNumber))
+ return;
+
+ m_codecs[trackType] = {};
+
+ m_renderers[trackType].reset();
+ m_streams = defaultObjectsArray<decltype(m_streams)>();
+ m_demuxer.reset();
+
+ updateVideoSinkSize();
+ createObjectsIfNeeded();
+ updateObjectsPausedState();
+}
+
+void PlaybackEngine::finilizeTime(qint64 pos)
+{
+ Q_ASSERT(pos >= 0 && pos <= duration());
+
+ m_timeController.setPaused(true);
+ m_timeController.sync(pos);
+ m_currentLoopOffset = {};
+}
+
+void PlaybackEngine::finalizeOutputs()
+{
+ updateActiveAudioOutput(nullptr);
+ updateActiveVideoOutput(nullptr, true);
+}
+
+bool PlaybackEngine::hasRenderer(quint64 id) const
+{
+ return std::any_of(m_renderers.begin(), m_renderers.end(),
+ [id](auto &renderer) { return renderer && renderer->id() == id; });
+}
+
+void PlaybackEngine::updateActiveAudioOutput(QAudioOutput *output)
+{
+ if (auto renderer =
+ qobject_cast<AudioRenderer *>(m_renderers[QPlatformMediaPlayer::AudioStream].get()))
+ renderer->setOutput(output);
+}
+
+void PlaybackEngine::updateActiveVideoOutput(QVideoSink *sink, bool cleanOutput)
+{
+ if (auto renderer = qobject_cast<SubtitleRenderer *>(
+ m_renderers[QPlatformMediaPlayer::SubtitleStream].get()))
+ renderer->setOutput(sink, cleanOutput);
+ if (auto renderer =
+ qobject_cast<VideoRenderer *>(m_renderers[QPlatformMediaPlayer::VideoStream].get()))
+ renderer->setOutput(sink, cleanOutput);
+}
+
+void PlaybackEngine::updateVideoSinkSize(QVideoSink *prevSink)
+{
+ auto platformVideoSink = m_videoSink ? m_videoSink->platformVideoSink() : nullptr;
+ if (!platformVideoSink)
+ return;
+
+ if (prevSink && prevSink->platformVideoSink())
+ platformVideoSink->setNativeSize(prevSink->platformVideoSink()->nativeSize());
+ else {
+ const auto streamIndex = m_media.currentStreamIndex(QPlatformMediaPlayer::VideoStream);
+ if (streamIndex >= 0) {
+ const auto context = m_media.avContext();
+ const auto stream = context->streams[streamIndex];
+ const AVRational pixelAspectRatio =
+ av_guess_sample_aspect_ratio(context, stream, nullptr);
+ // auto size = metaData().value(QMediaMetaData::Resolution)
+ const QSize size =
+ qCalculateFrameSize({ stream->codecpar->width, stream->codecpar->height },
+ { pixelAspectRatio.num, pixelAspectRatio.den });
+
+ platformVideoSink->setNativeSize(qRotatedFrameSize(size, m_media.rotation()));
+ }
+ }
+}
+
+qint64 PlaybackEngine::boundPosition(qint64 position) const
+{
+ position = qMax(position, 0);
+ return duration() > 0 ? qMin(position, duration()) : position;
+}
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegplaybackengine_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine_p.h b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine_p.h
new file mode 100644
index 000000000..f36828771
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine_p.h
@@ -0,0 +1,229 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGPLAYBACKENGINE_P_H
+#define QFFMPEGPLAYBACKENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+/* Playback engine design description.
+ *
+ *
+ * PLAYBACK ENGINE OBJECTS
+ *
+ * - Playback engine manages 7 objects inside, each one works in a separate thread.
+ * Each object inherits PlaybackEngineObject. The objects are:
+ * Demuxer
+ * Stream Decoders: audio, video, subtitles
+ * Renderers: audio, video, subtitles
+ *
+ *
+ * THREADS:
+ *
+ * - By default, each object works in a separate thread. It's easy to reconfigure
+ * to using several objects in thread.
+ * - New thread is allocated if a new object is created and the engine doesn't
+ * have free threads. If it does, the thread is to be reused.
+ * - If all objects for some thread are deleted, the thread becomes free and the engine
+ * postpones its termination.
+ *
+ * OBJECTS WEAK CONNECTIVITY
+ *
+ * - The objects know nothing about others and about PlaybackEngine.
+ * For any interactions the objects use slots/signals.
+ *
+ * - PlaybackEngine knows the objects object and is able to create/delete them and
+ * call their public methods.
+ *
+ */
+
+#include "playbackengine/qffmpegplaybackenginedefs_p.h"
+#include "playbackengine/qffmpegtimecontroller_p.h"
+#include "playbackengine/qffmpegmediadataholder_p.h"
+#include "playbackengine/qffmpegcodec_p.h"
+#include "playbackengine/qffmpegpositionwithoffset_p.h"
+
+#include <QtCore/qpointer.h>
+
+#include <unordered_map>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioSink;
+class QVideoSink;
+class QAudioOutput;
+class QFFmpegMediaPlayer;
+
+namespace QFFmpeg
+{
+
+class PlaybackEngine : public QObject
+{
+ Q_OBJECT
+public:
+ PlaybackEngine();
+
+ ~PlaybackEngine() override;
+
+ void setMedia(MediaDataHolder media);
+
+ void setVideoSink(QVideoSink *sink);
+
+ void setAudioSink(QAudioOutput *output);
+
+ void setAudioSink(QPlatformAudioOutput *output);
+
+ void setState(QMediaPlayer::PlaybackState state);
+
+ void play() {
+ setState(QMediaPlayer::PlayingState);
+ }
+ void pause() {
+ setState(QMediaPlayer::PausedState);
+ }
+ void stop() {
+ setState(QMediaPlayer::StoppedState);
+ }
+
+ void seek(qint64 pos);
+
+ void setLoops(int loopsCount);
+
+ void setPlaybackRate(float rate);
+
+ float playbackRate() const;
+
+ void setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber);
+
+ qint64 currentPosition(bool topPos = true) const;
+
+ qint64 duration() const;
+
+ bool isSeekable() const;
+
+ const QList<MediaDataHolder::StreamInfo> &
+ streamInfo(QPlatformMediaPlayer::TrackType trackType) const;
+
+ const QMediaMetaData &metaData() const;
+
+ int activeTrack(QPlatformMediaPlayer::TrackType type) const;
+
+signals:
+ void endOfStream();
+ void errorOccured(int, const QString &);
+ void loopChanged();
+ void buffered();
+
+protected: // objects managing
+ struct ObjectDeleter
+ {
+ void operator()(PlaybackEngineObject *) const;
+
+ PlaybackEngine *engine = nullptr;
+ };
+
+ template<typename T>
+ using ObjectPtr = std::unique_ptr<T, ObjectDeleter>;
+
+ using RendererPtr = ObjectPtr<Renderer>;
+ using StreamPtr = ObjectPtr<StreamDecoder>;
+
+ template<typename T, typename... Args>
+ ObjectPtr<T> createPlaybackEngineObject(Args &&...args);
+
+ virtual RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType);
+
+ void updateActiveAudioOutput(QAudioOutput *output);
+
+ void updateActiveVideoOutput(QVideoSink *sink, bool cleanOutput = false);
+
+private:
+ void createStreamAndRenderer(QPlatformMediaPlayer::TrackType trackType);
+
+ void createDemuxer();
+
+ void registerObject(PlaybackEngineObject &object);
+
+ template<typename C, typename Action>
+ void forEachExistingObject(Action &&action);
+
+ template<typename Action>
+ void forEachExistingObject(Action &&action);
+
+ void forceUpdate();
+
+ void recreateObjects();
+
+ void createObjectsIfNeeded();
+
+ void updateObjectsPausedState();
+
+ void deleteFreeThreads();
+
+ void onRendererSynchronized(quint64 id, std::chrono::steady_clock::time_point time,
+ qint64 trackTime);
+
+ void onRendererFinished();
+
+ void onRendererLoopChanged(quint64 id, qint64 offset, int loopIndex);
+
+ void triggerStepIfNeeded();
+
+ static QString objectThreadName(const PlaybackEngineObject &object);
+
+ std::optional<Codec> codecForTrack(QPlatformMediaPlayer::TrackType trackType);
+
+ bool hasMediaStream() const;
+
+ void finilizeTime(qint64 pos);
+
+ void finalizeOutputs();
+
+ bool hasRenderer(quint64 id) const;
+
+ void updateVideoSinkSize(QVideoSink *prevSink = nullptr);
+
+ qint64 boundPosition(qint64 position) const;
+
+private:
+ MediaDataHolder m_media;
+
+ TimeController m_timeController;
+
+ std::unordered_map<QString, std::unique_ptr<QThread>> m_threads;
+ bool m_threadsDirty = false;
+
+ QPointer<QVideoSink> m_videoSink;
+ QPointer<QAudioOutput> m_audioOutput;
+
+ QMediaPlayer::PlaybackState m_state = QMediaPlayer::StoppedState;
+
+ ObjectPtr<Demuxer> m_demuxer;
+ std::array<StreamPtr, QPlatformMediaPlayer::NTrackTypes> m_streams;
+ std::array<RendererPtr, QPlatformMediaPlayer::NTrackTypes> m_renderers;
+
+ std::array<std::optional<Codec>, QPlatformMediaPlayer::NTrackTypes> m_codecs;
+ int m_loops = QMediaPlayer::Once;
+ LoopOffset m_currentLoopOffset;
+};
+
+template<typename T, typename... Args>
+PlaybackEngine::ObjectPtr<T> PlaybackEngine::createPlaybackEngineObject(Args &&...args)
+{
+ auto result = ObjectPtr<T>(new T(std::forward<Args>(args)...), { this });
+ registerObject(*result);
+ return result;
+}
+}
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGPLAYBACKENGINE_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp b/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
index 911278f63..e5e9ca3bb 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegresampler.cpp
@@ -1,117 +1,111 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegresampler_p.h"
-#include "qffmpegdecoder_p.h"
+#include "playbackengine/qffmpegcodec_p.h"
#include "qffmpegmediaformatinfo_p.h"
#include <qloggingcategory.h>
-extern "C" {
-#include <libavutil/opt.h>
-}
-
-Q_LOGGING_CATEGORY(qLcResampler, "qt.multimedia.ffmpeg.resampler")
+static Q_LOGGING_CATEGORY(qLcResampler, "qt.multimedia.ffmpeg.resampler")
QT_BEGIN_NAMESPACE
-namespace QFFmpeg
+using namespace QFFmpeg;
+
+QFFmpegResampler::QFFmpegResampler(const QAudioFormat &inputFormat, const QAudioFormat &outputFormat) :
+ m_inputFormat(inputFormat), m_outputFormat(outputFormat)
{
+ Q_ASSERT(inputFormat.isValid());
+ Q_ASSERT(outputFormat.isValid());
+
+ m_resampler =
+ createResampleContext(AVAudioFormat(m_inputFormat), AVAudioFormat(m_outputFormat));
+}
-Resampler::Resampler(const Codec *codec, const QAudioFormat &outputFormat)
+QFFmpegResampler::QFFmpegResampler(const Codec* codec, const QAudioFormat &outputFormat)
: m_outputFormat(outputFormat)
{
+ Q_ASSERT(codec);
+
qCDebug(qLcResampler) << "createResampler";
const AVStream *audioStream = codec->stream();
- const auto *codecpar = audioStream->codecpar;
if (!m_outputFormat.isValid())
// want the native format
m_outputFormat = QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(audioStream->codecpar);
- QAudioFormat::ChannelConfig config = m_outputFormat.channelConfig();
- if (config == QAudioFormat::ChannelConfigUnknown)
- config = QAudioFormat::defaultChannelConfigForChannelCount(m_outputFormat.channelCount());
-
- auto inConfig = codecpar->channel_layout;
- if (inConfig == 0)
- inConfig = QFFmpegMediaFormatInfo::avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->channels));
-
- qCDebug(qLcResampler) << "init resampler" << m_outputFormat.sampleRate() << config << codecpar->sample_rate;
- resampler = swr_alloc_set_opts(nullptr, // we're allocating a new context
- QFFmpegMediaFormatInfo::avChannelLayout(config), // out_ch_layout
- QFFmpegMediaFormatInfo::avSampleFormat(m_outputFormat.sampleFormat()), // out_sample_fmt
- m_outputFormat.sampleRate(), // out_sample_rate
- inConfig, // in_ch_layout
- AVSampleFormat(codecpar->format), // in_sample_fmt
- codecpar->sample_rate, // in_sample_rate
- 0, // log_offset
- nullptr);
-
- // if we're not the master clock, we might need to handle clock adjustments, initialize for that
- av_opt_set_double(resampler, "async", m_outputFormat.sampleRate()/50, 0);
-
- swr_init(resampler);
+ m_resampler = createResampleContext(AVAudioFormat(audioStream->codecpar),
+ AVAudioFormat(m_outputFormat));
}
-Resampler::~Resampler()
+QFFmpegResampler::~QFFmpegResampler() = default;
+
+QAudioBuffer QFFmpegResampler::resample(const char* data, size_t size)
{
- swr_free(&resampler);
+ if (!m_inputFormat.isValid())
+ return {};
+
+ return resample(reinterpret_cast<const uint8_t **>(&data),
+ m_inputFormat.framesForBytes(static_cast<qint32>(size)));
}
-QAudioBuffer Resampler::resample(const AVFrame *frame)
+QAudioBuffer QFFmpegResampler::resample(const AVFrame *frame)
{
- const int outSamples = swr_get_out_samples(resampler, frame->nb_samples);
- QByteArray samples(m_outputFormat.bytesForFrames(outSamples), Qt::Uninitialized);
- auto **in = const_cast<const uint8_t **>(frame->extended_data);
+ return resample(const_cast<const uint8_t **>(frame->extended_data), frame->nb_samples);
+}
+
+QAudioBuffer QFFmpegResampler::resample(const uint8_t **inputData, int inputSamplesCount)
+{
+ const int maxOutSamples = adjustMaxOutSamples(inputSamplesCount);
+
+ QByteArray samples(m_outputFormat.bytesForFrames(maxOutSamples), Qt::Uninitialized);
auto *out = reinterpret_cast<uint8_t *>(samples.data());
- const int out_samples = swr_convert(resampler, &out, outSamples,
- in, frame->nb_samples);
- samples.resize(m_outputFormat.bytesForFrames(out_samples));
+ const int outSamples =
+ swr_convert(m_resampler.get(), &out, maxOutSamples, inputData, inputSamplesCount);
+
+ samples.resize(m_outputFormat.bytesForFrames(outSamples));
qint64 startTime = m_outputFormat.durationForFrames(m_samplesProcessed);
- m_samplesProcessed += out_samples;
+ m_samplesProcessed += outSamples;
- qCDebug(qLcResampler) << " new frame" << startTime << "in_samples" << frame->nb_samples << out_samples << outSamples;
- QAudioBuffer buffer(samples, m_outputFormat, startTime);
- return buffer;
+ qCDebug(qLcResampler) << " new frame" << startTime << "in_samples" << inputSamplesCount
+ << outSamples << maxOutSamples;
+ return QAudioBuffer(samples, m_outputFormat, startTime);
}
+int QFFmpegResampler::adjustMaxOutSamples(int inputSamplesCount)
+{
+ int maxOutSamples = swr_get_out_samples(m_resampler.get(), inputSamplesCount);
+
+ const auto remainingCompensationDistance = m_endCompensationSample - m_samplesProcessed;
+
+ if (remainingCompensationDistance > 0 && maxOutSamples > remainingCompensationDistance) {
+ // If the remaining compensation distance less than output frame,
+ // the ffmpeg resampler bufferises the rest of frames that makes
+ // unexpected delays on large frames.
+ // The hack might cause some compensation bias on large frames,
+ // however it's not significant for our logic, in fact.
+ // TODO: probably, it will need some improvements
+ setSampleCompensation(0, 0);
+ maxOutSamples = swr_get_out_samples(m_resampler.get(), inputSamplesCount);
+ }
+
+ return maxOutSamples;
+}
+void QFFmpegResampler::setSampleCompensation(qint32 delta, quint32 distance)
+{
+ const int res = swr_set_compensation(m_resampler.get(), delta, static_cast<int>(distance));
+ if (res < 0)
+ qCWarning(qLcResampler) << "swr_set_compensation fail:" << res;
+ else {
+ m_sampleCompensationDelta = delta;
+ m_endCompensationSample = m_samplesProcessed + distance;
+ }
+}
+
+qint32 QFFmpegResampler::activeSampleCompensationDelta() const
+{
+ return m_samplesProcessed < m_endCompensationSample ? m_sampleCompensationDelta : 0;
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h b/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h
index ddede2aab..5109ecf11 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegresampler_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGRESAMPLER_P_H
#define QFFMPEGRESAMPLER_P_H
@@ -52,31 +16,45 @@
#include "qaudiobuffer.h"
#include "qffmpeg_p.h"
+#include "private/qplatformaudioresampler_p.h"
QT_BEGIN_NAMESPACE
namespace QFFmpeg
{
+class Codec;
+}
-struct Codec;
-
-class Resampler
+class QFFmpegResampler : public QPlatformAudioResampler
{
public:
- Resampler(const Codec *codec, const QAudioFormat &outputFormat);
- ~Resampler();
+ QFFmpegResampler(const QAudioFormat &inputFormat, const QAudioFormat &outputFormat);
+ QFFmpegResampler(const QFFmpeg::Codec* codec, const QAudioFormat &outputFormat);
+
+ ~QFFmpegResampler() override;
+
+ QAudioBuffer resample(const char* data, size_t size) override;
QAudioBuffer resample(const AVFrame *frame);
+
qint64 samplesProcessed() const { return m_samplesProcessed; }
+ void setSampleCompensation(qint32 delta, quint32 distance);
+ qint32 activeSampleCompensationDelta() const;
private:
+ int adjustMaxOutSamples(int inputSamplesCount);
+
+ QAudioBuffer resample(const uint8_t **inputData, int inputSamplesCount);
+
+private:
+ QAudioFormat m_inputFormat;
QAudioFormat m_outputFormat;
- SwrContext *resampler = nullptr;
+ QFFmpeg::SwrContextUPtr m_resampler;
qint64 m_samplesProcessed = 0;
+ qint64 m_endCompensationSample = std::numeric_limits<qint64>::min();
+ qint32 m_sampleCompensationDelta = 0;
};
-}
-
QT_END_NAMESPACE
#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp
new file mode 100644
index 000000000..72e542a34
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi.cpp
@@ -0,0 +1,473 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegscreencapture_dxgi_p.h"
+#include "qffmpegsurfacecapturegrabber_p.h"
+#include <private/qabstractvideobuffer_p.h>
+#include <private/qmultimediautils_p.h>
+#include <private/qwindowsmultimediautils_p.h>
+#include <qtgui/qscreen_platform.h>
+#include "qvideoframe.h"
+
+#include <qloggingcategory.h>
+#include <qwaitcondition.h>
+#include <qmutex.h>
+
+#include "D3d11.h"
+#include "dxgi1_2.h"
+
+#include <system_error>
+#include <thread>
+#include <chrono>
+
+#include <mutex> // std::scoped_lock
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcScreenCaptureDxgi, "qt.multimedia.ffmpeg.screencapturedxgi")
+
+using namespace std::chrono;
+using namespace QWindowsMultimediaUtils;
+using namespace Qt::StringLiterals;
+
+namespace {
+
+// Convenience wrapper that combines an HRESULT
+// status code with an optional textual description.
+class ComStatus
+{
+public:
+ ComStatus() = default;
+ ComStatus(HRESULT hr) : m_hr{ hr } { }
+ ComStatus(HRESULT hr, QAnyStringView msg) : m_hr{ hr }, m_msg{ msg.toString() } { }
+
+ ComStatus(const ComStatus &) = default;
+ ComStatus(ComStatus &&) = default;
+ ComStatus &operator=(const ComStatus &) = default;
+ ComStatus &operator=(ComStatus &&) = default;
+
+ explicit operator bool() const { return m_hr == S_OK; }
+
+ HRESULT code() const { return m_hr; }
+ QString str() const
+ {
+ if (!m_msg)
+ return errorString(m_hr);
+ return *m_msg + " " + errorString(m_hr);
+ }
+
+private:
+ HRESULT m_hr = S_OK;
+ std::optional<QString> m_msg;
+};
+
+template <typename T>
+using ComProduct = QMaybe<ComPtr<T>, ComStatus>;
+
+}
+
+class QD3D11TextureVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ QD3D11TextureVideoBuffer(const ComPtr<ID3D11Device> &device, std::shared_ptr<QMutex> &mutex,
+ const ComPtr<ID3D11Texture2D> &texture, QSize size)
+ : QAbstractVideoBuffer(QVideoFrame::NoHandle)
+ , m_device(device)
+ , m_texture(texture)
+ , m_ctxMutex(mutex)
+ , m_size(size)
+ {}
+
+ ~QD3D11TextureVideoBuffer()
+ {
+ QD3D11TextureVideoBuffer::unmap();
+ }
+
+ QVideoFrame::MapMode mapMode() const override
+ {
+ return m_mapMode;
+ }
+
+ MapData map(QVideoFrame::MapMode mode) override
+ {
+ MapData mapData;
+ if (!m_ctx && mode == QVideoFrame::ReadOnly) {
+ D3D11_TEXTURE2D_DESC texDesc = {};
+ m_texture->GetDesc(&texDesc);
+ texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ texDesc.Usage = D3D11_USAGE_STAGING;
+ texDesc.MiscFlags = 0;
+ texDesc.BindFlags = 0;
+
+ HRESULT hr = m_device->CreateTexture2D(&texDesc, nullptr, m_cpuTexture.GetAddressOf());
+ if (FAILED(hr)) {
+ qCDebug(qLcScreenCaptureDxgi) << "Failed to create texture with CPU access"
+ << std::system_category().message(hr).c_str();
+ qCDebug(qLcScreenCaptureDxgi) << m_device->GetDeviceRemovedReason();
+ return {};
+ }
+
+ m_device->GetImmediateContext(m_ctx.GetAddressOf());
+ m_ctxMutex->lock();
+ m_ctx->CopyResource(m_cpuTexture.Get(), m_texture.Get());
+
+ D3D11_MAPPED_SUBRESOURCE resource = {};
+ hr = m_ctx->Map(m_cpuTexture.Get(), 0, D3D11_MAP_READ, 0, &resource);
+ m_ctxMutex->unlock();
+ if (FAILED(hr)) {
+ qCDebug(qLcScreenCaptureDxgi) << "Failed to map texture" << m_cpuTexture.Get()
+ << std::system_category().message(hr).c_str();
+ return {};
+ }
+
+ m_mapMode = mode;
+ mapData.nPlanes = 1;
+ mapData.bytesPerLine[0] = int(resource.RowPitch);
+ mapData.data[0] = reinterpret_cast<uchar*>(resource.pData);
+ mapData.size[0] = m_size.height() * int(resource.RowPitch);
+ }
+
+ return mapData;
+ }
+
+ void unmap() override
+ {
+ if (m_mapMode == QVideoFrame::NotMapped)
+ return;
+ if (m_ctx) {
+ m_ctxMutex->lock();
+ m_ctx->Unmap(m_cpuTexture.Get(), 0);
+ m_ctxMutex->unlock();
+ m_ctx.Reset();
+ }
+ m_cpuTexture.Reset();
+ m_mapMode = QVideoFrame::NotMapped;
+ }
+
+ QSize getSize() const
+ {
+ if (!m_texture)
+ return {};
+
+ D3D11_TEXTURE2D_DESC desc{};
+ m_texture->GetDesc(&desc);
+
+ return { static_cast<int>(desc.Width), static_cast<int>(desc.Height) };
+ }
+
+private:
+ ComPtr<ID3D11Device> m_device;
+ ComPtr<ID3D11Texture2D> m_texture;
+ ComPtr<ID3D11Texture2D> m_cpuTexture;
+ ComPtr<ID3D11DeviceContext> m_ctx;
+ std::shared_ptr<QMutex> m_ctxMutex;
+ QSize m_size;
+ QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped;
+};
+
+namespace {
+class DxgiDuplication
+{
+ struct DxgiScreen
+ {
+ ComPtr<IDXGIAdapter1> adapter;
+ ComPtr<IDXGIOutput> output;
+ };
+
+public:
+ ~DxgiDuplication()
+ {
+ if (m_releaseFrame)
+ m_dup->ReleaseFrame();
+ }
+
+ ComStatus initialize(QScreen const *screen)
+ {
+ const QMaybe<DxgiScreen, ComStatus> dxgiScreen = findDxgiScreen(screen);
+ if (!dxgiScreen)
+ return dxgiScreen.error();
+
+ const ComPtr<IDXGIAdapter1> adapter = dxgiScreen->adapter;
+
+ ComPtr<ID3D11Device> d3d11dev;
+ HRESULT hr =
+ D3D11CreateDevice(adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, nullptr, 0,
+ D3D11_SDK_VERSION, d3d11dev.GetAddressOf(), nullptr, nullptr);
+ if (FAILED(hr))
+ return { hr, "Failed to create ID3D11Device device"_L1 };
+
+ ComPtr<IDXGIOutput1> output;
+ hr = dxgiScreen->output.As(&output);
+ if (FAILED(hr))
+ return { hr, "Failed to create IDXGIOutput1"_L1 };
+
+ ComPtr<IDXGIOutputDuplication> dup;
+ hr = output->DuplicateOutput(d3d11dev.Get(), dup.GetAddressOf());
+ if (FAILED(hr))
+ return { hr, "Failed to duplicate IDXGIOutput1"_L1 };
+
+ m_adapter = dxgiScreen->adapter;
+ m_output = output;
+ m_device = d3d11dev;
+ m_dup = dup;
+ return { S_OK };
+ }
+
+ bool valid() const { return m_dup != nullptr; }
+
+ QSize getFrameSize() const
+ {
+ DXGI_OUTDUPL_DESC outputDesc = {};
+ m_dup->GetDesc(&outputDesc);
+
+ return { static_cast<int>(outputDesc.ModeDesc.Width),
+ static_cast<int>(outputDesc.ModeDesc.Height) };
+ }
+
+ QMaybe<std::unique_ptr<QD3D11TextureVideoBuffer>, ComStatus> getNextVideoFrame()
+ {
+ const ComProduct<ID3D11Texture2D> texture = getNextFrame();
+
+ if (!texture)
+ return texture.error();
+
+ return std::make_unique<QD3D11TextureVideoBuffer>(m_device, m_ctxMutex, *texture,
+ getFrameSize());
+ }
+
+private:
+ ComProduct<ID3D11Texture2D> getNextFrame()
+ {
+ std::scoped_lock guard{ *m_ctxMutex };
+
+ if (m_releaseFrame) {
+ m_releaseFrame = false;
+
+ HRESULT hr = m_dup->ReleaseFrame();
+
+ if (hr != S_OK)
+ return ComStatus{ hr, "Failed to release duplication frame."_L1 };
+ }
+
+ ComPtr<IDXGIResource> frame;
+ DXGI_OUTDUPL_FRAME_INFO info;
+
+ HRESULT hr = m_dup->AcquireNextFrame(0, &info, frame.GetAddressOf());
+
+ if (hr != S_OK)
+ return { unexpect, hr, "Failed to grab the screen content"_L1 };
+
+ m_releaseFrame = true;
+
+ ComPtr<ID3D11Texture2D> tex;
+ hr = frame.As(&tex);
+ if (hr != S_OK)
+ return { unexpect, hr, "Failed to obtain D3D11 texture"_L1 };
+
+ D3D11_TEXTURE2D_DESC texDesc = {};
+ tex->GetDesc(&texDesc);
+ texDesc.MiscFlags = 0;
+ texDesc.BindFlags = 0;
+
+ ComPtr<ID3D11Texture2D> texCopy;
+ hr = m_device->CreateTexture2D(&texDesc, nullptr, texCopy.GetAddressOf());
+ if (hr != S_OK)
+ return { unexpect, hr, "Failed to create texture with CPU access"_L1 };
+
+ ComPtr<ID3D11DeviceContext> ctx;
+ m_device->GetImmediateContext(ctx.GetAddressOf());
+ ctx->CopyResource(texCopy.Get(), tex.Get());
+
+ return texCopy;
+ }
+
+ static QMaybe<DxgiScreen, ComStatus> findDxgiScreen(const QScreen *screen)
+ {
+ if (!screen)
+ return { unexpect, E_FAIL, "Cannot find nullptr screen"_L1 };
+
+ auto *winScreen = screen->nativeInterface<QNativeInterface::QWindowsScreen>();
+ HMONITOR handle = winScreen ? winScreen->handle() : nullptr;
+
+ ComPtr<IDXGIFactory1> factory;
+ HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
+ if (FAILED(hr))
+ return { unexpect, hr, "Failed to create IDXGIFactory"_L1 };
+
+ ComPtr<IDXGIAdapter1> adapter;
+ for (quint32 i = 0; factory->EnumAdapters1(i, adapter.ReleaseAndGetAddressOf()) == S_OK; i++) {
+ ComPtr<IDXGIOutput> output;
+ for (quint32 j = 0; adapter->EnumOutputs(j, output.ReleaseAndGetAddressOf()) == S_OK; ++j) {
+ DXGI_OUTPUT_DESC desc = {};
+ output->GetDesc(&desc);
+ qCDebug(qLcScreenCaptureDxgi) << i << j << QString::fromWCharArray(desc.DeviceName);
+ auto match = handle ? handle == desc.Monitor
+ : QString::fromWCharArray(desc.DeviceName) == screen->name();
+ if (match)
+ return DxgiScreen{ adapter, output };
+ }
+ }
+ return { unexpect, DXGI_ERROR_NOT_FOUND,
+ "Could not find screen adapter "_L1 + screen->name() };
+ }
+
+ ComPtr<IDXGIAdapter1> m_adapter;
+ ComPtr<IDXGIOutput> m_output;
+ ComPtr<ID3D11Device> m_device;
+ ComPtr<IDXGIOutputDuplication> m_dup;
+ bool m_releaseFrame = false;
+ std::shared_ptr<QMutex> m_ctxMutex = std::make_shared<QMutex>();
+};
+
+QSize getPhysicalSizePixels(const QScreen *screen)
+{
+ const auto *winScreen = screen->nativeInterface<QNativeInterface::QWindowsScreen>();
+ if (!winScreen)
+ return {};
+
+ const HMONITOR handle = winScreen->handle();
+ if (!handle)
+ return {};
+
+ MONITORINFO info{};
+ info.cbSize = sizeof(info);
+
+ if (!GetMonitorInfoW(handle, &info))
+ return {};
+
+ return { info.rcMonitor.right - info.rcMonitor.left,
+ info.rcMonitor.bottom - info.rcMonitor.top };
+}
+
+QVideoFrameFormat getFrameFormat(QScreen* screen)
+{
+ const QSize screenSize = getPhysicalSizePixels(screen);
+
+ QVideoFrameFormat format = { screenSize, QVideoFrameFormat::Format_BGRA8888 };
+ format.setStreamFrameRate(static_cast<int>(screen->refreshRate()));
+
+ return format;
+}
+
+} // namespace
+
+class QFFmpegScreenCaptureDxgi::Grabber : public QFFmpegSurfaceCaptureGrabber
+{
+public:
+ Grabber(QFFmpegScreenCaptureDxgi &screenCapture, QScreen *screen,
+ const QVideoFrameFormat &format)
+ : m_screen(screen)
+ , m_format(format)
+ {
+ setFrameRate(screen->refreshRate());
+ addFrameCallback(screenCapture, &QFFmpegScreenCaptureDxgi::newVideoFrame);
+ connect(this, &Grabber::errorUpdated, &screenCapture, &QFFmpegScreenCaptureDxgi::updateError);
+ }
+
+ ~Grabber() {
+ stop();
+ }
+
+ QVideoFrameFormat format() {
+ return m_format;
+ }
+
+ QVideoFrame grabFrame() override
+ {
+ QVideoFrame frame;
+ if (!m_duplication.valid()) {
+ const ComStatus status = m_duplication.initialize(m_screen);
+ if (!status) {
+ if (status.code() == E_ACCESSDENIED) {
+ // May occur for some time after pushing Ctrl+Alt+Del.
+ updateError(QPlatformSurfaceCapture::NoError, status.str());
+ qCWarning(qLcScreenCaptureDxgi) << status.str();
+ }
+ return frame;
+ }
+ }
+
+ auto maybeBuf = m_duplication.getNextVideoFrame();
+ const ComStatus &status = maybeBuf.error();
+
+ if (status.code() == DXGI_ERROR_WAIT_TIMEOUT) {
+ // All is good, we just didn't get a new frame yet
+ updateError(QPlatformSurfaceCapture::NoError, status.str());
+ } else if (status.code() == DXGI_ERROR_ACCESS_LOST) {
+ // Can happen for example when pushing Ctrl + Alt + Del
+ m_duplication = {};
+ updateError(QPlatformSurfaceCapture::NoError, status.str());
+ qCWarning(qLcScreenCaptureDxgi) << status.str();
+ } else if (!status) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed, status.str());
+ qCWarning(qLcScreenCaptureDxgi) << status.str();
+ } else if (maybeBuf) {
+ std::unique_ptr<QD3D11TextureVideoBuffer> buffer = std::move(*maybeBuf);
+
+ const QSize bufSize = buffer->getSize();
+ if (bufSize != m_format.frameSize())
+ m_format.setFrameSize(bufSize);
+
+ frame = { buffer.release(), format() };
+ }
+
+ return frame;
+ }
+
+ protected:
+ void initializeGrabbingContext() override
+ {
+ m_duplication = DxgiDuplication();
+ const ComStatus status = m_duplication.initialize(m_screen);
+ if (!status) {
+ updateError(CaptureFailed, status.str());
+ return;
+ }
+
+ QFFmpegSurfaceCaptureGrabber::initializeGrabbingContext();
+ }
+
+private:
+ const QScreen *m_screen = nullptr;
+ QVideoFrameFormat m_format;
+ DxgiDuplication m_duplication;
+};
+
+QFFmpegScreenCaptureDxgi::QFFmpegScreenCaptureDxgi() : QPlatformSurfaceCapture(ScreenSource{}) { }
+
+QFFmpegScreenCaptureDxgi::~QFFmpegScreenCaptureDxgi() = default;
+
+QVideoFrameFormat QFFmpegScreenCaptureDxgi::frameFormat() const
+{
+ if (m_grabber)
+ return m_grabber->format();
+ return {};
+}
+
+bool QFFmpegScreenCaptureDxgi::setActiveInternal(bool active)
+{
+ if (static_cast<bool>(m_grabber) == active)
+ return true;
+
+ if (m_grabber) {
+ m_grabber.reset();
+ } else {
+ auto screen = source<ScreenSource>();
+
+ if (!checkScreenWithError(screen))
+ return false;
+
+ const QVideoFrameFormat format = getFrameFormat(screen);
+ if (!format.isValid()) {
+ updateError(NotFound, QLatin1String("Unable to determine screen size or format"));
+ return false;
+ }
+
+ m_grabber.reset(new Grabber(*this, screen, format));
+ m_grabber->start();
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h
new file mode 100644
index 000000000..8f866b135
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegscreencapture_dxgi_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGSCREENCAPTURE_WINDOWS_H
+#define QFFMPEGSCREENCAPTURE_WINDOWS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qvideoframeformat.h"
+#include <private/qcomptr_p.h>
+#include <private/qplatformsurfacecapture_p.h>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegScreenCaptureDxgi : public QPlatformSurfaceCapture
+{
+public:
+ explicit QFFmpegScreenCaptureDxgi();
+
+ ~QFFmpegScreenCaptureDxgi() override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+private:
+ bool setActiveInternal(bool active) override;
+
+private:
+ class Grabber;
+ std::unique_ptr<Grabber> m_grabber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGSCREENCAPTURE_WINDOWS_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp
new file mode 100644
index 000000000..f708f5021
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber.cpp
@@ -0,0 +1,202 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegsurfacecapturegrabber_p.h"
+
+#include <qelapsedtimer.h>
+#include <qloggingcategory.h>
+#include <qthread.h>
+#include <qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcScreenCaptureGrabber, "qt.multimedia.ffmpeg.surfacecapturegrabber");
+
+namespace {
+
+class GrabbingProfiler
+{
+public:
+ auto measure()
+ {
+ m_elapsedTimer.start();
+ return qScopeGuard([&]() {
+ const auto nsecsElapsed = m_elapsedTimer.nsecsElapsed();
+ ++m_number;
+ m_wholeTime += nsecsElapsed;
+
+#ifdef DUMP_SCREEN_CAPTURE_PROFILING
+ qDebug() << "screen grabbing time:" << nsecsElapsed << "avg:" << avgTime()
+ << "number:" << m_number;
+#endif
+ });
+ }
+
+ qreal avgTime() const
+ {
+ return m_number ? m_wholeTime / (m_number * 1000000.) : 0.;
+ }
+
+ qint64 number() const
+ {
+ return m_number;
+ }
+
+private:
+ QElapsedTimer m_elapsedTimer;
+ qint64 m_wholeTime = 0;
+ qint64 m_number = 0;
+};
+
+} // namespace
+
+struct QFFmpegSurfaceCaptureGrabber::GrabbingContext
+{
+ GrabbingProfiler profiler;
+ QTimer timer;
+ QElapsedTimer elapsedTimer;
+ qint64 lastFrameTime = 0;
+};
+
+class QFFmpegSurfaceCaptureGrabber::GrabbingThread : public QThread
+{
+public:
+ GrabbingThread(QFFmpegSurfaceCaptureGrabber& grabber)
+ : m_grabber(grabber)
+ {}
+
+protected:
+ void run() override
+ {
+ m_grabber.initializeGrabbingContext();
+
+ if (!m_grabber.isGrabbingContextInitialized())
+ return;
+
+ exec();
+ m_grabber.finalizeGrabbingContext();
+ }
+
+private:
+ QFFmpegSurfaceCaptureGrabber& m_grabber;
+};
+
+QFFmpegSurfaceCaptureGrabber::QFFmpegSurfaceCaptureGrabber(ThreadPolicy threadPolicy)
+{
+ setFrameRate(DefaultScreenCaptureFrameRate);
+
+ if (threadPolicy == CreateGrabbingThread)
+ m_thread = std::make_unique<GrabbingThread>(*this);
+}
+
+void QFFmpegSurfaceCaptureGrabber::start()
+{
+ if (m_thread)
+ m_thread->start();
+ else if (!isGrabbingContextInitialized())
+ initializeGrabbingContext();
+}
+
+QFFmpegSurfaceCaptureGrabber::~QFFmpegSurfaceCaptureGrabber() = default;
+
+void QFFmpegSurfaceCaptureGrabber::setFrameRate(qreal rate)
+{
+ rate = qBound(MinScreenCaptureFrameRate, rate, MaxScreenCaptureFrameRate);
+ if (std::exchange(m_rate, rate) != rate) {
+ qCDebug(qLcScreenCaptureGrabber) << "Screen capture rate has been changed:" << m_rate;
+
+ updateTimerInterval();
+ }
+}
+
+qreal QFFmpegSurfaceCaptureGrabber::frameRate() const
+{
+ return m_rate;
+}
+
+void QFFmpegSurfaceCaptureGrabber::stop()
+{
+ if (m_thread)
+ {
+ m_thread->quit();
+ m_thread->wait();
+ }
+ else if (isGrabbingContextInitialized())
+ {
+ finalizeGrabbingContext();
+ }
+}
+
+void QFFmpegSurfaceCaptureGrabber::updateError(QPlatformSurfaceCapture::Error error,
+ const QString &description)
+{
+ const auto prevError = std::exchange(m_prevError, error);
+
+ if (error != QPlatformSurfaceCapture::NoError
+ || prevError != QPlatformSurfaceCapture::NoError) {
+ emit errorUpdated(error, description);
+ }
+
+ updateTimerInterval();
+}
+
+void QFFmpegSurfaceCaptureGrabber::updateTimerInterval()
+{
+ const qreal rate = m_prevError && *m_prevError != QPlatformSurfaceCapture::NoError
+ ? MinScreenCaptureFrameRate
+ : m_rate;
+ const int interval = static_cast<int>(1000 / rate);
+ if (m_context && m_context->timer.interval() != interval)
+ m_context->timer.setInterval(interval);
+}
+
+void QFFmpegSurfaceCaptureGrabber::initializeGrabbingContext()
+{
+ Q_ASSERT(!isGrabbingContextInitialized());
+ qCDebug(qLcScreenCaptureGrabber) << "screen capture started";
+
+ m_context = std::make_unique<GrabbingContext>();
+ m_context->timer.setTimerType(Qt::PreciseTimer);
+ updateTimerInterval();
+
+ m_context->elapsedTimer.start();
+
+ auto doGrab = [this]() {
+ auto measure = m_context->profiler.measure();
+
+ auto frame = grabFrame();
+
+ if (frame.isValid()) {
+ frame.setStartTime(m_context->lastFrameTime);
+ frame.setEndTime(m_context->elapsedTimer.nsecsElapsed() / 1000);
+ m_context->lastFrameTime = frame.endTime();
+
+ updateError(QPlatformSurfaceCapture::NoError);
+
+ emit frameGrabbed(frame);
+ }
+ };
+
+ doGrab();
+
+ m_context->timer.callOnTimeout(&m_context->timer, doGrab);
+ m_context->timer.start();
+}
+
+void QFFmpegSurfaceCaptureGrabber::finalizeGrabbingContext()
+{
+ Q_ASSERT(isGrabbingContextInitialized());
+ qCDebug(qLcScreenCaptureGrabber)
+ << "end screen capture thread; avg grabbing time:" << m_context->profiler.avgTime()
+ << "ms, grabbings number:" << m_context->profiler.number();
+ m_context.reset();
+}
+
+bool QFFmpegSurfaceCaptureGrabber::isGrabbingContextInitialized() const
+{
+ return m_context != nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegsurfacecapturegrabber_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber_p.h b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber_p.h
new file mode 100644
index 000000000..9a617bd7a
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegsurfacecapturegrabber_p.h
@@ -0,0 +1,92 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGSURFACECAPTUREGRABBER_P_H
+#define QFFMPEGSURFACECAPTUREGRABBER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qvideoframe.h"
+#include "private/qplatformsurfacecapture_p.h"
+
+#include <memory>
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+class QThread;
+
+static constexpr qreal DefaultScreenCaptureFrameRate = 60.;
+
+// Mac screens often support 120 frames per sec; it looks, this is not
+// needed for the capturing now since it just affects CPI without valuable
+// advantages. In the future, the frame rate should be customized by
+// user's API.
+static constexpr qreal MaxScreenCaptureFrameRate = 60.;
+static constexpr qreal MinScreenCaptureFrameRate = 1.;
+
+class QFFmpegSurfaceCaptureGrabber : public QObject
+{
+ Q_OBJECT
+public:
+ enum ThreadPolicy {
+ UseCurrentThread,
+ CreateGrabbingThread,
+ };
+
+ QFFmpegSurfaceCaptureGrabber(ThreadPolicy threadPolicy = CreateGrabbingThread);
+
+ ~QFFmpegSurfaceCaptureGrabber() override;
+
+ void start();
+ void stop();
+
+ template<typename Object, typename Method>
+ void addFrameCallback(Object &object, Method method)
+ {
+ connect(this, &QFFmpegSurfaceCaptureGrabber::frameGrabbed,
+ &object, method, Qt::DirectConnection);
+ }
+
+signals:
+ void frameGrabbed(const QVideoFrame&);
+ void errorUpdated(QPlatformSurfaceCapture::Error error, const QString &description);
+
+protected:
+ void updateError(QPlatformSurfaceCapture::Error error, const QString &description = {});
+
+ virtual QVideoFrame grabFrame() = 0;
+
+ void setFrameRate(qreal rate);
+
+ qreal frameRate() const;
+
+ void updateTimerInterval();
+
+ virtual void initializeGrabbingContext();
+ virtual void finalizeGrabbingContext();
+
+ bool isGrabbingContextInitialized() const;
+
+private:
+ struct GrabbingContext;
+ class GrabbingThread;
+
+ std::unique_ptr<GrabbingContext> m_context;
+ qreal m_rate = 0;
+ std::optional<QPlatformSurfaceCapture::Error> m_prevError;
+ std::unique_ptr<QThread> m_thread;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGSURFACECAPTUREGRABBER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolve_p.h b/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolve_p.h
new file mode 100644
index 000000000..8064b8d85
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolve_p.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGSYMBOLSRESOLVE_P_H
+#define QFFMPEGSYMBOLSRESOLVE_P_H
+
+#include "qnamespace.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+inline void resolveSymbols()
+{
+#ifdef DYNAMIC_RESOLVE_OPENSSL_SYMBOLS
+ extern bool resolveOpenSsl();
+ resolveOpenSsl();
+#endif
+
+#ifdef DYNAMIC_RESOLVE_VAAPI_SYMBOLS
+ extern bool resolveVAAPI();
+ resolveVAAPI();
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGSYMBOLSRESOLVE_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolveutils.cpp b/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolveutils.cpp
new file mode 100644
index 000000000..c4a4d9666
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolveutils.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegsymbolsresolveutils_p.h"
+
+#include <qdebug.h>
+#include <algorithm>
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcLibSymbolsRelolver, "qt.multimedia.ffmpeg.libsymbolsresolver");
+
+LibSymbolsResolver::LibSymbolsResolver(const char *libName, size_t symbolsCount,
+ LibsLoader libsLoader)
+ : m_libName(libName), m_libsLoader(libsLoader)
+{
+ Q_ASSERT(m_libName);
+ Q_ASSERT(m_libsLoader);
+ m_symbols.reserve(symbolsCount);
+}
+
+bool LibSymbolsResolver::resolve()
+{
+ if (m_state.testAndSetRelaxed(Initial, Requested)
+ || !m_state.testAndSetAcquire(Ready, Finished))
+ return false;
+
+ qCDebug(qLcLibSymbolsRelolver)
+ << "Start" << m_libName << "symbols resolving:" << m_symbols.size() << "symbols";
+
+ Q_ASSERT(m_symbols.size() == m_symbols.capacity());
+
+ auto cleanup = qScopeGuard([this]() { m_symbols = {}; });
+
+ auto libs = m_libsLoader();
+ if (libs.empty()) {
+ qCWarning(qLcLibSymbolsRelolver) << "Couldn't load" << m_libName << "library";
+ return false;
+ }
+
+ std::vector<QFunctionPointer> functions(m_symbols.size());
+
+ auto resolveElement = [&libs](const SymbolElement &element) {
+ return resolve(libs, element.name);
+ };
+
+ std::transform(m_symbols.begin(), m_symbols.end(), functions.begin(), resolveElement);
+
+ if (std::find(functions.begin(), functions.end(), nullptr) != functions.end()) {
+ unload(libs);
+ qCWarning(qLcLibSymbolsRelolver) << "Couldn't resolve" << m_libName << "symbols";
+ return false;
+ }
+
+ for (size_t i = 0; i < functions.size(); ++i)
+ m_symbols[i].setter(functions[i]);
+
+ qCDebug(qLcLibSymbolsRelolver) << m_libName << "symbols resolved";
+ return true;
+}
+
+void LibSymbolsResolver::registerSymbol(const char *name, FunctionSetter setter)
+{
+ Q_ASSERT(setter);
+ Q_ASSERT(m_symbols.size() < m_symbols.capacity());
+
+ m_symbols.push_back({ name, setter });
+
+ // handle the corner case: a user has initialized QtMM with global vars construction
+ // and it happened before the symbols initializing
+ if (m_symbols.size() == m_symbols.capacity() && !m_state.testAndSetRelease(Initial, Ready)
+ && m_state.testAndSetRelease(Requested, Ready))
+ resolve();
+}
+
+void LibSymbolsResolver::unload(const Libs &libs)
+{
+ for (auto &lib : libs)
+ lib->unload();
+}
+
+bool LibSymbolsResolver::tryLoad(const Libs &libs)
+{
+ auto load = [](auto &lib) { return lib->load(); };
+ if (std::all_of(libs.begin(), libs.end(), load))
+ return true;
+
+ unload(libs);
+ return false;
+}
+
+QFunctionPointer LibSymbolsResolver::resolve(const Libs &libs, const char *symbolName)
+{
+ for (auto &lib : libs)
+ if (auto pointer = lib->resolve(symbolName))
+ return pointer;
+
+ qWarning() << "Cannot resolve symbol" << symbolName;
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolveutils_p.h b/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolveutils_p.h
new file mode 100644
index 000000000..f7a2169d3
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegsymbolsresolveutils_p.h
@@ -0,0 +1,142 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGSYMBOLSRESOLVEUTILS_P_H
+#define QFFMPEGSYMBOLSRESOLVEUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qlibrary.h>
+
+#include <qatomic.h>
+
+#include <vector>
+#include <memory>
+#include <tuple>
+
+QT_BEGIN_NAMESPACE
+
+using Libs = std::vector<std::unique_ptr<QLibrary>>;
+
+class LibSymbolsResolver
+{
+public:
+ using FunctionSetter = void (*)(QFunctionPointer);
+ using LibsLoader = Libs (*)();
+
+ LibSymbolsResolver(const char *libName, size_t symbolsCount, LibsLoader libsLoader);
+
+ bool resolve();
+
+ void registerSymbol(const char *name, FunctionSetter setter);
+
+ static void unload(const Libs &libs);
+
+ static bool tryLoad(const Libs &libs);
+
+private:
+ static QFunctionPointer resolve(const Libs &libs, const char *symbolName);
+
+private:
+ const char *const m_libName;
+ LibsLoader m_libsLoader;
+
+ struct SymbolElement
+ {
+ const char *name;
+ FunctionSetter setter;
+ };
+
+ std::vector<SymbolElement> m_symbols;
+
+ enum State { Initial, Requested, Ready, Finished };
+
+ QAtomicInteger<int> m_state = Initial;
+};
+
+QT_END_NAMESPACE
+
+template <typename T>
+struct DefaultReturn
+{
+ template <typename... Arg>
+ T operator()(Arg &&...) { return val; }
+ T val;
+};
+
+template <>
+struct DefaultReturn<void>
+{
+ DefaultReturn(int = 0){};
+ template <typename... Arg>
+ void operator()(Arg &&...) { }
+};
+
+template <typename...>
+struct FuncInfo;
+
+template <typename R, typename... A>
+struct FuncInfo<R(A...)>
+{
+ using Return = R;
+ using Args = std::tuple<A...>;
+};
+
+// clang-format off
+
+#define DEFINE_FUNC_IMPL(F, Vars, TypesWithVars, ReturnFunc) \
+ using F##_ReturnType = FuncInfo<decltype(F)>::Return; \
+ using q_##F##_Type = F##_ReturnType (*)(TypesWithVars(F)); \
+ static q_##F##_Type q_##F = []() { \
+ auto setter = [](QFunctionPointer ptr) { q_##F = (q_##F##_Type)ptr; }; \
+ resolver()->registerSymbol(#F, setter); \
+ return [](TypesWithVars(F)) { return ReturnFunc(Vars()); }; \
+ }(); \
+ extern "C" [[maybe_unused]] F##_ReturnType F(TypesWithVars(F)) { return q_##F(Vars()); }
+
+#define VAR(I) a##I
+#define VARS0()
+#define VARS1() VAR(0)
+#define VARS2() VARS1(), VAR(1)
+#define VARS3() VARS2(), VAR(2)
+#define VARS4() VARS3(), VAR(3)
+#define VARS5() VARS4(), VAR(4)
+#define VARS6() VARS5(), VAR(5)
+#define VARS7() VARS6(), VAR(6)
+#define VARS8() VARS7(), VAR(7)
+#define VARS9() VARS8(), VAR(8)
+#define VARS10() VARS9(), VAR(9)
+#define VARS11() VARS10(), VAR(10)
+
+#define TYPE_WITH_VAR(F, I) std::tuple_element_t<I, FuncInfo<decltype(F)>::Args> VAR(I)
+#define TYPES_WITH_VARS0(F)
+#define TYPES_WITH_VARS1(F) TYPE_WITH_VAR(F, 0)
+#define TYPES_WITH_VARS2(F) TYPES_WITH_VARS1(F), TYPE_WITH_VAR(F, 1)
+#define TYPES_WITH_VARS3(F) TYPES_WITH_VARS2(F), TYPE_WITH_VAR(F, 2)
+#define TYPES_WITH_VARS4(F) TYPES_WITH_VARS3(F), TYPE_WITH_VAR(F, 3)
+#define TYPES_WITH_VARS5(F) TYPES_WITH_VARS4(F), TYPE_WITH_VAR(F, 4)
+#define TYPES_WITH_VARS6(F) TYPES_WITH_VARS5(F), TYPE_WITH_VAR(F, 5)
+#define TYPES_WITH_VARS7(F) TYPES_WITH_VARS6(F), TYPE_WITH_VAR(F, 6)
+#define TYPES_WITH_VARS8(F) TYPES_WITH_VARS7(F), TYPE_WITH_VAR(F, 7)
+#define TYPES_WITH_VARS9(F) TYPES_WITH_VARS8(F), TYPE_WITH_VAR(F, 8)
+#define TYPES_WITH_VARS10(F) TYPES_WITH_VARS9(F), TYPE_WITH_VAR(F, 9)
+#define TYPES_WITH_VARS11(F) TYPES_WITH_VARS10(F), TYPE_WITH_VAR(F, 10)
+
+
+#define RET(F, ...) DefaultReturn<FuncInfo<decltype(F)>::Return>{__VA_ARGS__}
+
+#define DEFINE_FUNC(F, ArgsCount, /*Return value*/...) \
+ DEFINE_FUNC_IMPL(F, VARS##ArgsCount, TYPES_WITH_VARS##ArgsCount, RET(F, __VA_ARGS__));
+
+// clang-format on
+
+#endif // QFFMPEGSYMBOLSRESOLVEUTILS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp b/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp
index c9584c550..fb14ced54 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp
@@ -1,93 +1,53 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegthread_p.h"
-#include <qloggingcategory.h>
QT_BEGIN_NAMESPACE
using namespace QFFmpeg;
-void Thread::kill()
+void ConsumerThread::stopAndDelete()
{
{
- QMutexLocker locker(&mutex);
- exit.storeRelease(true);
- killHelper();
+ QMutexLocker locker(&m_loopDataMutex);
+ m_exit = true;
}
- wake();
+ dataReady();
wait();
delete this;
}
-void Thread::maybePause()
+void ConsumerThread::dataReady()
{
- while (timeOut > 0 || shouldWait()) {
- if (exit.loadAcquire())
- break;
-
- QElapsedTimer timer;
- timer.start();
- if (condition.wait(&mutex, QDeadlineTimer(timeOut, Qt::PreciseTimer))) {
- if (timeOut >= 0) {
- timeOut -= timer.elapsed();
- if (timeOut < 0)
- timeOut = -1;
- }
- } else {
- timeOut = -1;
- }
- }
+ m_condition.wakeAll();
}
-void Thread::run()
+void ConsumerThread::run()
{
init();
- QMutexLocker locker(&mutex);
- while (1) {
- maybePause();
- if (exit.loadAcquire())
- break;
- loop();
+
+ while (true) {
+
+ {
+ QMutexLocker locker(&m_loopDataMutex);
+ while (!hasData() && !m_exit)
+ m_condition.wait(&m_loopDataMutex);
+
+ if (m_exit)
+ break;
+ }
+
+ processOne();
}
+
cleanup();
}
+QMutexLocker<QMutex> ConsumerThread::lockLoopData() const
+{
+ return QMutexLocker(&m_loopDataMutex);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h b/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h
index bd5ecbba5..a7c5b0927 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGTHREAD_P_H
#define QFFMPEGTHREAD_P_H
@@ -63,38 +27,66 @@ class QAudioSink;
namespace QFFmpeg
{
-class Thread : public QThread
+/*!
+ FFmpeg thread that is used to implement a consumer pattern.
+
+ This thread processes work items until no more data is available.
+ When no more data is available, it sleeps until it is notified about
+ more available data.
+ */
+class ConsumerThread : public QThread
{
public:
- mutable QMutex mutex;
- qint64 timeOut = -1;
-private:
- QWaitCondition condition;
+ /*!
+ Stops the thread and deletes this object
+ */
+ void stopAndDelete();
protected:
- QAtomicInteger<bool> exit = false;
-
-public:
- // public API is thread-safe
- void kill();
- virtual void killHelper() {}
-
- void wake() {
- condition.wakeAll();
- }
-
-protected:
- virtual void init() {}
- virtual void cleanup() {}
- // loop() should never block, all blocking has to happen in shouldWait()
- virtual void loop() = 0;
- virtual bool shouldWait() const { return false; }
+ /*!
+ Called on this thread when thread starts
+ */
+ virtual void init() = 0;
+
+ /*!
+ Called on this thread before thread exits
+ */
+ virtual void cleanup() = 0;
+
+ /*!
+ Process one work item. Called repeatedly until hasData() returns
+ false, in which case the thread sleeps until the next dataReady()
+ notification.
+
+ Note: processOne() should never block.
+ */
+ virtual void processOne() = 0;
+
+ /*!
+ Wake thread from sleep and process data until
+ hasData() returns false. The method is supposed to be invoked
+ right after the scope of QMutexLocker that lockLoopData returns.
+ */
+ void dataReady();
+
+ /*!
+ Must return true when data is available for processing
+ */
+ virtual bool hasData() const = 0;
+
+ /*!
+ Locks the loop data mutex. It must be used to protect loop data
+ like a queue of video frames.
+ */
+ QMutexLocker<QMutex> lockLoopData() const;
private:
- void maybePause();
+ void run() final;
- void run() override;
+ mutable QMutex m_loopDataMutex;
+ QWaitCondition m_condition;
+ bool m_exit = false;
};
}
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvaapisymbols.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvaapisymbols.cpp
new file mode 100644
index 000000000..9860b53a0
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvaapisymbols.cpp
@@ -0,0 +1,127 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtCore/qlibrary.h>
+
+#include "qffmpegsymbolsresolveutils_p.h"
+
+#include <QtCore/qglobal.h>
+#include <qstringliteral.h>
+
+#include <va/va.h>
+#ifdef DYNAMIC_RESOLVE_VA_DRM_SYMBOLS
+#include <va/va_drm.h>
+#endif
+#ifdef DYNAMIC_RESOLVE_VA_X11_SYMBOLS
+#include <va/va_x11.h>
+#endif
+#include <va/va_str.h>
+
+QT_BEGIN_NAMESPACE
+
+static Libs loadLibs()
+{
+ constexpr int version = VA_MAJOR_VERSION + 1;
+ Libs libs;
+ libs.push_back(std::make_unique<QLibrary>("va", version));
+#ifdef DYNAMIC_RESOLVE_VA_DRM_SYMBOLS
+ libs.push_back(std::make_unique<QLibrary>("va-drm", version));
+#endif
+
+#ifdef DYNAMIC_RESOLVE_VA_X11_SYMBOLS
+ libs.push_back(std::make_unique<QLibrary>("va-x11", version));
+#endif
+
+ if (LibSymbolsResolver::tryLoad(libs))
+ return libs;
+
+ return {};
+}
+
+constexpr size_t symbolsCount = 40
+#if VA_CHECK_VERSION(1, 9, 0)
+ + 1
+#endif
+#ifdef DYNAMIC_RESOLVE_VA_DRM_SYMBOLS
+ + 1
+#endif
+#ifdef DYNAMIC_RESOLVE_VA_X11_SYMBOLS
+ + 1
+#endif
+ ;
+
+Q_GLOBAL_STATIC(LibSymbolsResolver, resolver, "VAAPI", symbolsCount, loadLibs);
+
+void resolveVAAPI()
+{
+ resolver()->resolve();
+}
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+DEFINE_FUNC(vaInitialize, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaTerminate, 1, VA_STATUS_ERROR_OPERATION_FAILED);
+
+constexpr auto errorStr = "VAAPI is not available";
+DEFINE_FUNC(vaErrorStr, 1, errorStr);
+DEFINE_FUNC(vaSetErrorCallback, 3);
+DEFINE_FUNC(vaSetInfoCallback, 3);
+
+DEFINE_FUNC(vaCreateImage, 5, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaGetImage, 7, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaPutImage, 11, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaDeriveImage, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaDestroyImage, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaQueryImageFormats, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+
+DEFINE_FUNC(vaBeginPicture, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaRenderPicture, 4, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaEndPicture, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+
+DEFINE_FUNC(vaCreateBuffer, 7, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaMapBuffer, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaUnmapBuffer, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+#if VA_CHECK_VERSION(1, 9, 0)
+DEFINE_FUNC(vaSyncBuffer, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+#endif
+DEFINE_FUNC(vaDestroyBuffer, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+
+DEFINE_FUNC(vaCreateSurfaces, 8, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaSyncSurface, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaExportSurfaceHandle, 5, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaDestroySurfaces, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+
+DEFINE_FUNC(vaCreateConfig, 6, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaGetConfigAttributes, 5, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaMaxNumProfiles, 1);
+DEFINE_FUNC(vaMaxNumImageFormats, 1);
+DEFINE_FUNC(vaMaxNumEntrypoints, 1);
+DEFINE_FUNC(vaQueryConfigProfiles, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaQueryConfigEntrypoints, 4, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaQuerySurfaceAttributes, 4, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaDestroyConfig, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+
+DEFINE_FUNC(vaCreateContext, 8);
+DEFINE_FUNC(vaDestroyContext, 2);
+
+constexpr auto emptyString = "";
+DEFINE_FUNC(vaQueryVendorString, 1, emptyString);
+DEFINE_FUNC(vaProfileStr, 1, emptyString);
+DEFINE_FUNC(vaEntrypointStr, 1, emptyString);
+
+DEFINE_FUNC(vaGetDisplayAttributes, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+
+DEFINE_FUNC(vaSetDriverName, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+
+DEFINE_FUNC(vaAcquireBufferHandle, 3, VA_STATUS_ERROR_OPERATION_FAILED);
+DEFINE_FUNC(vaReleaseBufferHandle, 2, VA_STATUS_ERROR_OPERATION_FAILED);
+
+#ifdef DYNAMIC_RESOLVE_VA_DRM_SYMBOLS
+DEFINE_FUNC(vaGetDisplayDRM, 1); // va-drm
+#endif
+
+#ifdef DYNAMIC_RESOLVE_VA_X11_SYMBOLS
+DEFINE_FUNC(vaGetDisplay, 1); // va-x11
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp
index 144281896..5b79af5b3 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer.cpp
@@ -1,45 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegvideobuffer_p.h"
#include "private/qvideotexturehelper_p.h"
+#include "private/qmultimediautils_p.h"
#include "qffmpeghwaccel_p.h"
+#include "qloggingcategory.h"
extern "C" {
#include <libavutil/pixdesc.h>
@@ -49,68 +15,76 @@ extern "C" {
QT_BEGIN_NAMESPACE
-QFFmpegVideoBuffer::QFFmpegVideoBuffer(AVFrame *frame)
- : QAbstractVideoBuffer(QVideoFrame::NoHandle)
- , frame(frame)
+static bool isFrameFlipped(const AVFrame& frame) {
+ for (int i = 0; i < AV_NUM_DATA_POINTERS && frame.data[i]; ++i) {
+ if (frame.linesize[i] < 0)
+ return true;
+ }
+
+ return false;
+}
+
+static Q_LOGGING_CATEGORY(qLcFFmpegVideoBuffer, "qt.multimedia.ffmpeg.videobuffer");
+
+QFFmpegVideoBuffer::QFFmpegVideoBuffer(AVFrameUPtr frame, AVRational pixelAspectRatio)
+ : QAbstractVideoBuffer(QVideoFrame::NoHandle),
+ m_frame(frame.get()),
+ m_size(qCalculateFrameSize({ frame->width, frame->height },
+ { pixelAspectRatio.num, pixelAspectRatio.den }))
{
if (frame->hw_frames_ctx) {
- hwFrame = frame;
- m_pixelFormat = toQtPixelFormat(QFFmpeg::HWAccel::format(frame));
+ m_hwFrame = std::move(frame);
+ m_pixelFormat = toQtPixelFormat(QFFmpeg::HWAccel::format(m_hwFrame.get()));
return;
}
- swFrame = frame;
- m_pixelFormat = toQtPixelFormat(AVPixelFormat(swFrame->format));
+ m_swFrame = std::move(frame);
+ m_pixelFormat = toQtPixelFormat(AVPixelFormat(m_swFrame->format));
convertSWFrame();
}
-QFFmpegVideoBuffer::~QFFmpegVideoBuffer()
-{
- delete textures;
- if (swFrame)
- av_frame_free(&swFrame);
- if (hwFrame)
- av_frame_free(&hwFrame);
-}
+QFFmpegVideoBuffer::~QFFmpegVideoBuffer() = default;
void QFFmpegVideoBuffer::convertSWFrame()
{
- Q_ASSERT(swFrame);
- bool needsConversion = false;
- auto pixelFormat = toQtPixelFormat(AVPixelFormat(swFrame->format), &needsConversion);
-// qDebug() << "SW frame format:" << pixelFormat << swFrame->format << needsConversion;
+ Q_ASSERT(m_swFrame);
+
+ const auto actualAVPixelFormat = AVPixelFormat(m_swFrame->format);
+ const auto targetAVPixelFormat = toAVPixelFormat(m_pixelFormat);
- if (pixelFormat != m_pixelFormat) {
- AVPixelFormat newFormat = toAVPixelFormat(m_pixelFormat);
+ if (actualAVPixelFormat != targetAVPixelFormat || isFrameFlipped(*m_swFrame)
+ || m_size != QSize(m_swFrame->width, m_swFrame->height)) {
+ Q_ASSERT(toQtPixelFormat(targetAVPixelFormat) == m_pixelFormat);
// convert the format into something we can handle
- SwsContext *c = sws_getContext(swFrame->width, swFrame->height, AVPixelFormat(swFrame->format),
- swFrame->width, swFrame->height, newFormat,
+ SwsContext *c = sws_getContext(m_swFrame->width, m_swFrame->height, actualAVPixelFormat,
+ m_size.width(), m_size.height(), targetAVPixelFormat,
SWS_BICUBIC, nullptr, nullptr, nullptr);
- AVFrame *newFrame = av_frame_alloc();
- newFrame->width = swFrame->width;
- newFrame->height = swFrame->height;
- newFrame->format = newFormat;
- av_frame_get_buffer(newFrame, 0);
+ auto newFrame = QFFmpeg::makeAVFrame();
+ newFrame->width = m_size.width();
+ newFrame->height = m_size.height();
+ newFrame->format = targetAVPixelFormat;
+ av_frame_get_buffer(newFrame.get(), 0);
- sws_scale(c, swFrame->data, swFrame->linesize, 0, swFrame->height, newFrame->data, newFrame->linesize);
- av_frame_free(&swFrame);
- swFrame = newFrame;
+ sws_scale(c, m_swFrame->data, m_swFrame->linesize, 0, m_swFrame->height, newFrame->data, newFrame->linesize);
+ if (m_frame == m_swFrame.get())
+ m_frame = newFrame.get();
+ m_swFrame = std::move(newFrame);
sws_freeContext(c);
}
}
void QFFmpegVideoBuffer::setTextureConverter(const QFFmpeg::TextureConverter &converter)
{
- textureConverter = converter;
- textureConverter.init(hwFrame);
+ m_textureConverter = converter;
+ m_textureConverter.init(m_hwFrame.get());
m_type = converter.isNull() ? QVideoFrame::NoHandle : QVideoFrame::RhiTextureHandle;
}
QVideoFrameFormat::ColorSpace QFFmpegVideoBuffer::colorSpace() const
{
- switch (frame->colorspace) {
+ switch (m_frame->colorspace) {
default:
case AVCOL_SPC_UNSPECIFIED:
case AVCOL_SPC_RESERVED:
@@ -137,7 +111,7 @@ QVideoFrameFormat::ColorSpace QFFmpegVideoBuffer::colorSpace() const
QVideoFrameFormat::ColorTransfer QFFmpegVideoBuffer::colorTransfer() const
{
- switch (frame->color_trc) {
+ switch (m_frame->color_trc) {
case AVCOL_TRC_BT709:
// The following three cases have transfer characteristics identical to BT709
case AVCOL_TRC_BT1361_ECG:
@@ -168,7 +142,7 @@ QVideoFrameFormat::ColorTransfer QFFmpegVideoBuffer::colorTransfer() const
QVideoFrameFormat::ColorRange QFFmpegVideoBuffer::colorRange() const
{
- switch (frame->color_range) {
+ switch (m_frame->color_range) {
case AVCOL_RANGE_MPEG:
return QVideoFrameFormat::ColorRange_Video;
case AVCOL_RANGE_JPEG:
@@ -181,12 +155,14 @@ QVideoFrameFormat::ColorRange QFFmpegVideoBuffer::colorRange() const
float QFFmpegVideoBuffer::maxNits()
{
float maxNits = -1;
- for (int i = 0; i <frame->nb_side_data; ++i) {
- AVFrameSideData *sd = frame->side_data[i];
+ for (int i = 0; i < m_frame->nb_side_data; ++i) {
+ AVFrameSideData *sd = m_frame->side_data[i];
// TODO: Longer term we might want to also support HDR10+ dynamic metadata
if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) {
auto *data = reinterpret_cast<AVMasteringDisplayMetadata *>(sd->data);
- maxNits = float(data->max_luminance.num)/float(data->max_luminance.den)*10000.;
+ auto maybeLum = QFFmpeg::mul(10'000., data->max_luminance);
+ if (maybeLum)
+ maxNits = float(maybeLum.value());
}
}
return maxNits;
@@ -199,13 +175,13 @@ QVideoFrame::MapMode QFFmpegVideoBuffer::mapMode() const
QAbstractVideoBuffer::MapData QFFmpegVideoBuffer::map(QVideoFrame::MapMode mode)
{
- if (!swFrame) {
- Q_ASSERT(hwFrame && hwFrame->hw_frames_ctx);
- swFrame = av_frame_alloc();
+ if (!m_swFrame) {
+ Q_ASSERT(m_hwFrame && m_hwFrame->hw_frames_ctx);
+ m_swFrame = QFFmpeg::makeAVFrame();
/* retrieve data from GPU to CPU */
- int ret = av_hwframe_transfer_data(swFrame, hwFrame, 0);
+ int ret = av_hwframe_transfer_data(m_swFrame.get(), m_hwFrame.get(), 0);
if (ret < 0) {
- qWarning() << "Error transferring the data to system memory\n";
+ qWarning() << "Error transferring the data to system memory:" << ret;
return {};
}
convertSWFrame();
@@ -213,42 +189,62 @@ QAbstractVideoBuffer::MapData QFFmpegVideoBuffer::map(QVideoFrame::MapMode mode)
m_mode = mode;
-// qDebug() << "MAP:";
MapData mapData;
auto *desc = QVideoTextureHelper::textureDescription(pixelFormat());
mapData.nPlanes = desc->nplanes;
for (int i = 0; i < mapData.nPlanes; ++i) {
- mapData.data[i] = swFrame->data[i];
- mapData.bytesPerLine[i] = swFrame->linesize[i];
- mapData.size[i] = mapData.bytesPerLine[i]*desc->heightForPlane(swFrame->height, i);
-// qDebug() << " " << i << mapData.data[i] << mapData.size[i];
+ Q_ASSERT(m_swFrame->linesize[i] >= 0);
+
+ mapData.data[i] = m_swFrame->data[i];
+ mapData.bytesPerLine[i] = m_swFrame->linesize[i];
+ mapData.size[i] = mapData.bytesPerLine[i]*desc->heightForPlane(m_swFrame->height, i);
+ }
+
+ if ((mode & QVideoFrame::WriteOnly) != 0 && m_hwFrame) {
+ m_type = QVideoFrame::NoHandle;
+ m_hwFrame.reset();
+ if (m_textures) {
+ qCDebug(qLcFFmpegVideoBuffer)
+ << "Mapping of FFmpeg video buffer with write mode when "
+ "textures have been created. Visual artifacts might "
+ "happen if the frame is still in the rendering pipeline";
+ m_textures.reset();
+ }
}
+
return mapData;
}
void QFFmpegVideoBuffer::unmap()
{
- // nothing to do here for SW buffers
+ // nothing to do here for SW buffers.
+ // Set NotMapped mode to ensure map/unmap/mapMode consisteny.
+ m_mode = QVideoFrame::NotMapped;
}
-void QFFmpegVideoBuffer::mapTextures()
+std::unique_ptr<QVideoFrameTextures> QFFmpegVideoBuffer::mapTextures(QRhi *)
{
- if (textures || !hwFrame)
- return;
-// qDebug() << ">>>>> mapTextures";
- textures = textureConverter.getTextures(hwFrame);
- if (!textures)
- qWarning() << " failed to get textures for frame" << textureConverter.isNull();
-}
+ if (m_textures)
+ return {};
+ if (!m_hwFrame)
+ return {};
+ if (m_textureConverter.isNull()) {
+ m_textures = nullptr;
+ return {};
+ }
-quint64 QFFmpegVideoBuffer::textureHandle(int plane) const
-{
- return textures ? textures->textureHandle(plane) : 0;
+ m_textures.reset(m_textureConverter.getTextures(m_hwFrame.get()));
+ if (!m_textures) {
+ static thread_local int lastFormat = 0;
+ if (std::exchange(lastFormat, m_hwFrame->format) != m_hwFrame->format) // prevent logging spam
+ qWarning() << " failed to get textures for frame; format:" << m_hwFrame->format;
+ }
+ return {};
}
-std::unique_ptr<QRhiTexture> QFFmpegVideoBuffer::texture(int plane) const
+quint64 QFFmpegVideoBuffer::textureHandle(QRhi *rhi, int plane) const
{
- return textures ? textures->texture(plane) : std::unique_ptr<QRhiTexture>();
+ return m_textures ? m_textures->textureHandle(rhi, plane) : 0;
}
QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::pixelFormat() const
@@ -258,7 +254,7 @@ QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::pixelFormat() const
QSize QFFmpegVideoBuffer::size() const
{
- return QSize(frame->width, frame->height);
+ return m_size;
}
QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::toQtPixelFormat(AVPixelFormat avPixelFormat, bool *needsConversion)
@@ -269,6 +265,9 @@ QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::toQtPixelFormat(AVPixelFormat
switch (avPixelFormat) {
default:
break;
+ case AV_PIX_FMT_NONE:
+ Q_ASSERT(!"Invalid avPixelFormat!");
+ return QVideoFrameFormat::Format_Invalid;
case AV_PIX_FMT_ARGB:
return QVideoFrameFormat::Format_ARGB8888;
case AV_PIX_FMT_0RGB:
@@ -309,6 +308,8 @@ QVideoFrameFormat::PixelFormat QFFmpegVideoBuffer::toQtPixelFormat(AVPixelFormat
return QVideoFrameFormat::Format_P010;
case AV_PIX_FMT_P016:
return QVideoFrameFormat::Format_P016;
+ case AV_PIX_FMT_MEDIACODEC:
+ return QVideoFrameFormat::Format_SamplerExternalOES;
}
if (needsConversion)
@@ -341,13 +342,13 @@ AVPixelFormat QFFmpegVideoBuffer::toAVPixelFormat(QVideoFrameFormat::PixelFormat
// We're using the data from the converted QImage here, which is in BGRA.
return AV_PIX_FMT_BGRA;
case QVideoFrameFormat::Format_ARGB8888:
- case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
return AV_PIX_FMT_ARGB;
+ case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
case QVideoFrameFormat::Format_XRGB8888:
return AV_PIX_FMT_0RGB;
case QVideoFrameFormat::Format_BGRA8888:
- case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
return AV_PIX_FMT_BGRA;
+ case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
case QVideoFrameFormat::Format_BGRX8888:
return AV_PIX_FMT_BGR0;
case QVideoFrameFormat::Format_ABGR8888:
@@ -356,6 +357,8 @@ AVPixelFormat QFFmpegVideoBuffer::toAVPixelFormat(QVideoFrameFormat::PixelFormat
return AV_PIX_FMT_0BGR;
case QVideoFrameFormat::Format_RGBA8888:
return AV_PIX_FMT_RGBA;
+ // to be added in 6.8:
+ // case QVideoFrameFormat::Format_RGBA8888_Premultiplied:
case QVideoFrameFormat::Format_RGBX8888:
return AV_PIX_FMT_RGB0;
@@ -382,6 +385,9 @@ AVPixelFormat QFFmpegVideoBuffer::toAVPixelFormat(QVideoFrameFormat::PixelFormat
return AV_PIX_FMT_P010;
case QVideoFrameFormat::Format_P016:
return AV_PIX_FMT_P016;
+
+ case QVideoFrameFormat::Format_SamplerExternalOES:
+ return AV_PIX_FMT_MEDIACODEC;
}
}
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h
index 9f0d0cae6..18a580528 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideobuffer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGVIDEOBUFFER_P_H
#define QFFMPEGVIDEOBUFFER_P_H
@@ -64,17 +28,17 @@ QT_BEGIN_NAMESPACE
class QFFmpegVideoBuffer : public QAbstractVideoBuffer
{
public:
+ using AVFrameUPtr = QFFmpeg::AVFrameUPtr;
- QFFmpegVideoBuffer(AVFrame *frame);
- ~QFFmpegVideoBuffer();
+ QFFmpegVideoBuffer(AVFrameUPtr frame, AVRational pixelAspectRatio = { 1, 1 });
+ ~QFFmpegVideoBuffer() override;
QVideoFrame::MapMode mapMode() const override;
MapData map(QVideoFrame::MapMode mode) override;
void unmap() override;
- virtual void mapTextures() override;
- virtual quint64 textureHandle(int plane) const override;
- std::unique_ptr<QRhiTexture> texture(int plane) const override;
+ virtual std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *) override;
+ virtual quint64 textureHandle(QRhi *rhi, int plane) const override;
QVideoFrameFormat::PixelFormat pixelFormat() const;
QSize size() const;
@@ -84,7 +48,7 @@ public:
void convertSWFrame();
- AVFrame *getHWFrame() const { return hwFrame; }
+ AVFrame *getHWFrame() const { return m_hwFrame.get(); }
void setTextureConverter(const QFFmpeg::TextureConverter &converter);
@@ -96,12 +60,13 @@ public:
private:
QVideoFrameFormat::PixelFormat m_pixelFormat;
- AVFrame *frame = nullptr;
- AVFrame *hwFrame = nullptr;
- AVFrame *swFrame = nullptr;
- QFFmpeg::TextureConverter textureConverter;
+ AVFrame *m_frame = nullptr;
+ AVFrameUPtr m_hwFrame;
+ AVFrameUPtr m_swFrame;
+ QSize m_size;
+ QFFmpeg::TextureConverter m_textureConverter;
QVideoFrame::MapMode m_mode = QVideoFrame::NotMapped;
- QFFmpeg::TextureSet *textures = nullptr;
+ std::unique_ptr<QFFmpeg::TextureSet> m_textures;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp
deleted file mode 100644
index 4b576c441..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder.cpp
+++ /dev/null
@@ -1,407 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qffmpegvideoframeencoder_p.h"
-#include "qffmpegvideobuffer_p.h"
-#include "qffmpegmediaformatinfo_p.h"
-#include "qffmpegencoderoptions_p.h"
-#include "private/qplatformmediarecorder_p.h"
-#include "private/qmultimediautils_p.h"
-#include <qloggingcategory.h>
-
-extern "C" {
-#include <libavutil/pixdesc.h>
-}
-
-/* Infrastructure for HW acceleration goes into this file. */
-
-QT_BEGIN_NAMESPACE
-
-Q_LOGGING_CATEGORY(qLcVideoFrameEncoder, "qt.multimedia.ffmpeg.videoencoder")
-
-namespace QFFmpeg {
-
-VideoFrameEncoder::Data::~Data()
-{
- if (converter)
- sws_freeContext(converter);
- avcodec_free_context(&codecContext);
-}
-
-VideoFrameEncoder::VideoFrameEncoder(const QMediaEncoderSettings &encoderSettings,
- const QSize &sourceSize, float frameRate, AVPixelFormat sourceFormat, AVPixelFormat swFormat)
- : d(new Data)
-{
- d->settings = encoderSettings;
- d->frameRate = frameRate;
- d->sourceSize = sourceSize;
-
- if (!d->settings.videoResolution().isValid())
- d->settings.setVideoResolution(d->sourceSize);
-
- d->sourceFormat = sourceFormat;
- d->sourceSWFormat = swFormat;
-
- auto qVideoCodec = encoderSettings.videoCodec();
- auto codecID = QFFmpegMediaFormatInfo::codecIdForVideoCodec(qVideoCodec);
-
-#ifndef QT_DISABLE_HW_ENCODING
- const auto *accels = HWAccel::preferredDeviceTypes();
- while (*accels != AV_HWDEVICE_TYPE_NONE) {
- auto accel = HWAccel(*accels);
- ++accels;
-
- auto matchesSizeConstraints = [&]() -> bool {
- auto *constraints = av_hwdevice_get_hwframe_constraints(accel.hwDeviceContextAsBuffer(), nullptr);
- if (!constraints)
- return true;
- // Check size constraints
- bool result = (d->sourceSize.width() >= constraints->min_width && d->sourceSize.height() >= constraints->min_height &&
- d->sourceSize.width() <= constraints->max_width && d->sourceSize.height() <= constraints->max_height);
- av_hwframe_constraints_free(&constraints);
- return result;
- };
-
- if (!matchesSizeConstraints())
- continue;
-
- d->codec = accel.hardwareEncoderForCodecId(codecID);
- if (!d->codec)
- continue;
- d->accel = accel;
- break;
- }
-#endif
-
- if (d->accel.isNull()) {
- d->codec = avcodec_find_encoder(codecID);
- if (!d->codec) {
- qWarning() << "Could not find encoder for codecId" << codecID;
- d = {};
- return;
- }
- }
- auto supportsFormat = [&](AVPixelFormat fmt) {
- auto *f = d->codec->pix_fmts;
- while (*f != -1) {
- if (*f == fmt)
- return true;
- ++f;
- }
- return false;
- };
-
- d->targetFormat = d->sourceFormat;
-
- if (!supportsFormat(d->sourceFormat)) {
- if (supportsFormat(swFormat))
- d->targetFormat = swFormat;
- else
- // Take first format the encoder supports. Might want to improve upon this
- d->targetFormat = *d->codec->pix_fmts;
- }
-
- auto desc = av_pix_fmt_desc_get(d->sourceFormat);
- d->sourceFormatIsHWFormat = desc->flags & AV_PIX_FMT_FLAG_HWACCEL;
- desc = av_pix_fmt_desc_get(d->targetFormat);
- d->targetFormatIsHWFormat = desc->flags & AV_PIX_FMT_FLAG_HWACCEL;
-
- bool needToScale = d->sourceSize != d->settings.videoResolution();
- bool zeroCopy = d->sourceFormatIsHWFormat && d->sourceFormat == d->targetFormat && !needToScale;
-
- if (zeroCopy)
- // no need to initialize any converters
- return;
-
- if (d->sourceFormatIsHWFormat) {
- // if source and target formats don't agree, but the source is a HW format or sizes do't agree, we need to download
- if (d->sourceFormat != d->targetFormat || needToScale)
- d->downloadFromHW = true;
- } else {
- d->sourceSWFormat = d->sourceFormat;
- }
-
- if (d->targetFormatIsHWFormat) {
- Q_ASSERT(!d->accel.isNull());
- // if source and target formats don't agree, but the target is a HW format, we need to upload
- if (d->sourceFormat != d->targetFormat || needToScale) {
- d->uploadToHW = true;
-
- // determine the format used by the encoder.
- // We prefer YUV422 based formats such as NV12 or P010. Selection trues to find the best matching
- // format for the encoder depending on the bit depth of the source format
- auto desc = av_pix_fmt_desc_get(d->sourceSWFormat);
- int sourceDepth = desc->comp[0].depth;
-
- d->targetSWFormat = AV_PIX_FMT_NONE;
-
- auto *constraints = av_hwdevice_get_hwframe_constraints(d->accel.hwDeviceContextAsBuffer(), nullptr);
- auto *f = constraints->valid_sw_formats;
- int score = INT_MIN;
- while (*f != AV_PIX_FMT_NONE) {
- auto calcScore = [&](AVPixelFormat fmt) -> int {
- auto *desc = av_pix_fmt_desc_get(fmt);
- int s = 0;
- if (fmt == d->sourceSWFormat)
- // prefer exact matches
- s += 10;
- if (desc->comp[0].depth == sourceDepth)
- s += 100;
- else if (desc->comp[0].depth < sourceDepth)
- s -= 100;
- if (desc->log2_chroma_h == 1)
- s += 1;
- if (desc->log2_chroma_w == 1)
- s += 1;
- if (desc->flags & AV_PIX_FMT_FLAG_BE)
- s -= 10;
- if (desc->flags & AV_PIX_FMT_FLAG_PAL)
- // we don't want paletted formats
- s -= 10000;
- if (desc->flags & AV_PIX_FMT_FLAG_RGB)
- // we don't want RGB formats
- s -= 1000;
- if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
- // we really don't want HW accelerated formats here
- s -= 1000000;
- qCDebug(qLcVideoFrameEncoder) << "checking format" << fmt << Qt::hex << desc->flags << desc->comp[0].depth
- << desc->log2_chroma_h << desc->log2_chroma_w << "score:" << s;
- return s;
- };
-
- int s = calcScore(*f);
- if (s > score) {
- d->targetSWFormat = *f;
- score = s;
- }
- ++f;
- }
- if (d->targetSWFormat == AV_PIX_FMT_NONE) // shouldn't happen
- d->targetSWFormat = *constraints->valid_sw_formats;
-
- qCDebug(qLcVideoFrameEncoder) << "using format" << d->targetSWFormat << "as transfer format.";
-
- av_hwframe_constraints_free(&constraints);
- // need to create a frames context to convert the input data
- d->accel.createFramesContext(d->targetSWFormat, sourceSize);
- }
- } else {
- d->targetSWFormat = d->targetFormat;
- }
-
- if (d->sourceSWFormat != d->targetSWFormat || needToScale) {
- auto resolution = d->settings.videoResolution();
- qCDebug(qLcVideoFrameEncoder) << "camera and encoder use different formats:" << d->sourceSWFormat << d->targetSWFormat;
- d->converter = sws_getContext(d->sourceSize.width(), d->sourceSize.height(), d->sourceSWFormat,
- resolution.width(), resolution.height(), d->targetSWFormat,
- SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
- }
-}
-
-VideoFrameEncoder::~VideoFrameEncoder()
-{
-}
-
-void QFFmpeg::VideoFrameEncoder::initWithFormatContext(AVFormatContext *formatContext)
-{
- d->stream = avformat_new_stream(formatContext, nullptr);
- d->stream->id = formatContext->nb_streams - 1;
- //qCDebug(qLcVideoFrameEncoder) << "Video stream: index" << d->stream->id;
- d->stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
- d->stream->codecpar->codec_id = d->codec->id;
-
- // Apples HEVC decoders don't like the hev1 tag ffmpeg uses by default, use hvc1 as the more commonly accepted tag
- if (d->codec->id == AV_CODEC_ID_HEVC)
- d->stream->codecpar->codec_tag = MKTAG('h','v','c','1');
-
- // ### Fix hardcoded values
- d->stream->codecpar->format = d->targetFormat;
- d->stream->codecpar->width = d->settings.videoResolution().width();
- d->stream->codecpar->height = d->settings.videoResolution().height();
- d->stream->codecpar->sample_aspect_ratio = AVRational{1, 1};
- float requestedRate = d->frameRate;
- d->stream->time_base = AVRational{ 1, (int)(requestedRate*1000) };
-
- float delta = 1e10;
- if (d->codec->supported_framerates) {
- // codec only supports fixed frame rates
- auto *f = d->codec->supported_framerates;
- auto *best = f;
- qCDebug(qLcVideoFrameEncoder) << "Finding fixed rate:";
- while (f->num != 0) {
- float rate = float(f->num)/float(f->den);
- float d = qAbs(rate - requestedRate);
- qCDebug(qLcVideoFrameEncoder) << " " << f->num << f->den << d;
- if (d < delta) {
- best = f;
- delta = d;
- }
- ++f;
- }
- qCDebug(qLcVideoFrameEncoder) << "Fixed frame rate required. Requested:" << requestedRate << "Using:" << best->num << "/" << best->den;
- d->stream->time_base = { best->den, best->num };
- requestedRate = float(best->num)/float(best->den);
- }
-
- Q_ASSERT(d->codec);
- d->codecContext = avcodec_alloc_context3(d->codec);
- if (!d->codecContext) {
- qWarning() << "Could not allocate codec context";
- d = {};
- return;
- }
-
- avcodec_parameters_to_context(d->codecContext, d->stream->codecpar);
- d->codecContext->time_base = d->stream->time_base;
- qCDebug(qLcVideoFrameEncoder) << "requesting time base" << d->codecContext->time_base.num << d->codecContext->time_base.den;
- int num, den;
- qt_real_to_fraction(requestedRate, &num, &den);
- d->codecContext->framerate = { num, den };
- auto deviceContext = d->accel.hwDeviceContextAsBuffer();
- if (deviceContext)
- d->codecContext->hw_device_ctx = av_buffer_ref(deviceContext);
- auto framesContext = d->accel.hwFramesContextAsBuffer();
- if (framesContext)
- d->codecContext->hw_frames_ctx = av_buffer_ref(framesContext);
-}
-
-bool VideoFrameEncoder::open()
-{
- AVDictionary *opts = nullptr;
- applyVideoEncoderOptions(d->settings, d->codec->name, d->codecContext, &opts);
- int res = avcodec_open2(d->codecContext, d->codec, &opts);
- if (res < 0) {
- avcodec_free_context(&d->codecContext);
- qWarning() << "Couldn't open codec for writing" << err2str(res);
- return false;
- }
- qCDebug(qLcVideoFrameEncoder) << "video codec opened" << res << "time base" << d->codecContext->time_base.num << d->codecContext->time_base.den;
- d->stream->time_base = d->codecContext->time_base;
- return true;
-}
-
-qint64 VideoFrameEncoder::getPts(qint64 us)
-{
- Q_ASSERT(d);
- qint64 div = 1000000*d->stream->time_base.num;
- return (us*d->stream->time_base.den + (div>>1))/div;
-}
-
-int VideoFrameEncoder::sendFrame(AVFrame *frame)
-{
- if (!frame)
- return avcodec_send_frame(d->codecContext, frame);
- auto pts = frame->pts;
-
- if (d->downloadFromHW) {
- auto *f = av_frame_alloc();
- f->format = d->sourceSWFormat;
- int err = av_hwframe_transfer_data(f, frame, 0);
- if (err < 0) {
- qCDebug(qLcVideoFrameEncoder) << "Error transferring frame data to surface." << err2str(err);
- return err;
- }
- av_frame_free(&frame);
- frame = f;
- }
-
- if (d->converter) {
- auto *f = av_frame_alloc();
- f->format = d->targetSWFormat;
- f->width = d->settings.videoResolution().width();
- f->height = d->settings.videoResolution().height();
- av_frame_get_buffer(f, 0);
- sws_scale(d->converter, frame->data, frame->linesize, 0, f->height, f->data, f->linesize);
- av_frame_free(&frame);
- frame = f;
- }
-
- if (d->uploadToHW) {
- auto *hwFramesContext = d->accel.hwFramesContextAsBuffer();
- Q_ASSERT(hwFramesContext);
- auto *f = av_frame_alloc();
- if (!f)
- return AVERROR(ENOMEM);
- int err = av_hwframe_get_buffer(hwFramesContext, f, 0);
- if (err < 0) {
- qCDebug(qLcVideoFrameEncoder) << "Error getting HW buffer" << err2str(err);
- return err;
- } else {
- qCDebug(qLcVideoFrameEncoder) << "got HW buffer";
- }
- if (!f->hw_frames_ctx) {
- qCDebug(qLcVideoFrameEncoder) << "no hw frames context";
- return AVERROR(ENOMEM);
- }
- err = av_hwframe_transfer_data(f, frame, 0);
- if (err < 0) {
- qCDebug(qLcVideoFrameEncoder) << "Error transferring frame data to surface." << err2str(err);
- return err;
- }
- av_frame_free(&frame);
- frame = f;
- }
-
- qCDebug(qLcVideoFrameEncoder) << "sending frame" << pts;
- frame->pts = pts;
- int ret = avcodec_send_frame(d->codecContext, frame);
- av_frame_free(&frame);
- return ret;
-}
-
-AVPacket *VideoFrameEncoder::retrievePacket()
-{
- if (!d || !d->codecContext)
- return nullptr;
- AVPacket *packet = av_packet_alloc();
- int ret = avcodec_receive_packet(d->codecContext, packet);
- if (ret < 0) {
- av_packet_free(&packet);
- if (ret != AVERROR(EOF) && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
- qCDebug(qLcVideoFrameEncoder) << "Error receiving packet" << ret << err2str(ret);
- return nullptr;
- }
- qCDebug(qLcVideoFrameEncoder) << "got a packet" << packet->pts << timeStamp(packet->pts, d->stream->time_base);
- packet->stream_index = d->stream->id;
- return packet;
-}
-
-} // namespace QFFmpeg
-
-QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder_p.h
deleted file mode 100644
index 9aa707181..000000000
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideoframeencoder_p.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QFFMPEGVIDEOFRAMEENCODER_P_H
-#define QFFMPEGVIDEOFRAMEENCODER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qffmpeghwaccel_p.h"
-#include "qvideoframeformat.h"
-#include "private/qplatformmediarecorder_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QFFmpeg {
-
-class VideoFrameEncoder
-{
- class Data final
- {
- public:
- ~Data();
- QAtomicInt ref = 0;
- QMediaEncoderSettings settings;
- float frameRate = 0.;
- QSize sourceSize;
-
- HWAccel accel;
- const AVCodec *codec = nullptr;
- AVStream *stream = nullptr;
- AVCodecContext *codecContext = nullptr;
- SwsContext *converter = nullptr;
- AVPixelFormat sourceFormat = AV_PIX_FMT_NONE;
- AVPixelFormat sourceSWFormat = AV_PIX_FMT_NONE;
- AVPixelFormat targetFormat = AV_PIX_FMT_NONE;
- AVPixelFormat targetSWFormat = AV_PIX_FMT_NONE;
- bool sourceFormatIsHWFormat = false;
- bool targetFormatIsHWFormat = false;
- bool downloadFromHW = false;
- bool uploadToHW = false;
- };
-
- QExplicitlySharedDataPointer<Data> d;
-public:
- VideoFrameEncoder() = default;
- VideoFrameEncoder(const QMediaEncoderSettings &encoderSettings, const QSize &sourceSize, float frameRate, AVPixelFormat sourceFormat, AVPixelFormat swFormat);
- ~VideoFrameEncoder();
-
- void initWithFormatContext(AVFormatContext *formatContext);
- bool open();
-
- bool isNull() const { return !d; }
-
- AVPixelFormat sourceFormat() const { return d ? d->sourceFormat : AV_PIX_FMT_NONE; }
- AVPixelFormat targetFormat() const { return d ? d->targetFormat : AV_PIX_FMT_NONE; }
-
- qint64 getPts(qint64 ms);
-
- int sendFrame(AVFrame *frame);
- AVPacket *retrievePacket();
-};
-
-
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp
index ffc887495..ec99d8dd2 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideosink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qffmpegvideosink_p.h>
#include <qffmpegvideobuffer_p.h>
@@ -65,3 +29,5 @@ void QFFmpegVideoSink::setVideoFrame(const QVideoFrame &frame)
}
QT_END_NAMESPACE
+
+#include "moc_qffmpegvideosink_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h b/src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h
index 8da7aa452..92b537ee3 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegvideosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGVIDEOSINK_H
#define QFFMPEGVIDEOSINK_H
@@ -69,9 +33,6 @@ public:
void setVideoFrame(const QVideoFrame &frame) override;
-Q_SIGNALS:
- void rhiChanged(QRhi *rhi);
-
private:
QFFmpeg::TextureConverter textureConverter;
QRhi *m_rhi = nullptr;
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp b/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp
new file mode 100644
index 000000000..70746ea4c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp.cpp
@@ -0,0 +1,509 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegwindowcapture_uwp_p.h"
+#include "qffmpegsurfacecapturegrabber_p.h"
+#include <private/qabstractvideobuffer_p.h>
+
+#include <unknwn.h>
+#include <winrt/base.h>
+#include <QtCore/private/qfactorycacheregistration_p.h>
+// Workaround for Windows SDK bug.
+// See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47
+namespace winrt::impl
+{
+template <typename Async>
+auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout);
+}
+#include <winrt/Windows.Foundation.Collections.h>
+#include <winrt/Windows.Graphics.Capture.h>
+#include <winrt/Windows.Graphics.DirectX.h>
+#include <winrt/Windows.Graphics.DirectX.Direct3D11.h>
+#include <Windows.Graphics.Capture.h>
+#include <Windows.Graphics.Capture.Interop.h>
+#include <windows.graphics.directx.direct3d11.interop.h>
+
+#include <D3d11.h>
+#include <dwmapi.h>
+#include <lowlevelmonitorconfigurationapi.h>
+#include <physicalmonitorenumerationapi.h>
+
+#include "qvideoframe.h"
+#include <qwindow.h>
+#include <qthread.h>
+#include <qloggingcategory.h>
+#include <qguiapplication.h>
+#include <private/qmultimediautils_p.h>
+#include <private/qwindowsmultimediautils_p.h>
+#include <private/qcapturablewindow_p.h>
+#include <qpa/qplatformscreen_p.h>
+
+#include <memory>
+#include <system_error>
+
+QT_BEGIN_NAMESPACE
+
+using namespace winrt::Windows::Graphics::Capture;
+using namespace winrt::Windows::Graphics::DirectX;
+using namespace winrt::Windows::Graphics::DirectX::Direct3D11;
+using namespace Windows::Graphics::DirectX::Direct3D11;
+using namespace QWindowsMultimediaUtils;
+
+using winrt::check_hresult;
+using winrt::com_ptr;
+using winrt::guid_of;
+
+namespace {
+
+Q_LOGGING_CATEGORY(qLcWindowCaptureUwp, "qt.multimedia.ffmpeg.windowcapture.uwp");
+
+winrt::Windows::Graphics::SizeInt32 getWindowSize(HWND hwnd)
+{
+ RECT windowRect{};
+ ::GetWindowRect(hwnd, &windowRect);
+
+ return { windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
+}
+
+QSize asQSize(winrt::Windows::Graphics::SizeInt32 size)
+{
+ return { size.Width, size.Height };
+}
+
+struct MultithreadedApartment
+{
+ MultithreadedApartment(const MultithreadedApartment &) = delete;
+ MultithreadedApartment &operator=(const MultithreadedApartment &) = delete;
+
+ MultithreadedApartment() { winrt::init_apartment(); }
+ ~MultithreadedApartment() { winrt::uninit_apartment(); }
+};
+
+class QUwpTextureVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ QUwpTextureVideoBuffer(com_ptr<IDXGISurface> &&surface)
+ : QAbstractVideoBuffer(QVideoFrame::NoHandle), m_surface(surface)
+ {
+ }
+
+ ~QUwpTextureVideoBuffer() override { QUwpTextureVideoBuffer::unmap(); }
+
+ QVideoFrame::MapMode mapMode() const override { return m_mapMode; }
+
+ MapData map(QVideoFrame::MapMode mode) override
+ {
+ if (m_mapMode != QVideoFrame::NotMapped)
+ return {};
+
+ if (mode == QVideoFrame::ReadOnly) {
+ DXGI_MAPPED_RECT rect = {};
+ HRESULT hr = m_surface->Map(&rect, DXGI_MAP_READ);
+ if (SUCCEEDED(hr)) {
+ DXGI_SURFACE_DESC desc = {};
+ hr = m_surface->GetDesc(&desc);
+
+ MapData md = {};
+ md.nPlanes = 1;
+ md.bytesPerLine[0] = rect.Pitch;
+ md.data[0] = rect.pBits;
+ md.size[0] = rect.Pitch * desc.Height;
+
+ m_mapMode = QVideoFrame::ReadOnly;
+
+ return md;
+ } else {
+ qCDebug(qLcWindowCaptureUwp) << "Failed to map DXGI surface" << errorString(hr);
+ return {};
+ }
+ }
+
+ return {};
+ }
+
+ void unmap() override
+ {
+ if (m_mapMode == QVideoFrame::NotMapped)
+ return;
+
+ const HRESULT hr = m_surface->Unmap();
+ if (FAILED(hr))
+ qCDebug(qLcWindowCaptureUwp) << "Failed to unmap surface" << errorString(hr);
+
+ m_mapMode = QVideoFrame::NotMapped;
+ }
+
+private:
+ QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped;
+ com_ptr<IDXGISurface> m_surface;
+};
+
+struct WindowGrabber
+{
+ WindowGrabber() = default;
+
+ WindowGrabber(IDXGIAdapter1 *adapter, HWND hwnd)
+ : m_frameSize{ getWindowSize(hwnd) }, m_captureWindow{ hwnd }
+ {
+ check_hresult(D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, nullptr, 0,
+ D3D11_SDK_VERSION, m_device.put(), nullptr, nullptr));
+
+ const auto captureItem = createCaptureItem(hwnd);
+
+ m_framePool = Direct3D11CaptureFramePool::CreateFreeThreaded(
+ getCaptureDevice(m_device), m_pixelFormat, 1,
+ captureItem.Size());
+
+ m_session = m_framePool.CreateCaptureSession(captureItem);
+
+ // If supported, enable cursor capture
+ if (const auto session2 = m_session.try_as<IGraphicsCaptureSession2>())
+ session2.IsCursorCaptureEnabled(true);
+
+ // If supported, disable colored border around captured window to match other platforms
+ if (const auto session3 = m_session.try_as<IGraphicsCaptureSession3>())
+ session3.IsBorderRequired(false);
+
+ m_session.StartCapture();
+ }
+
+ ~WindowGrabber()
+ {
+ m_framePool.Close();
+ m_session.Close();
+ }
+
+ com_ptr<IDXGISurface> tryGetFrame()
+ {
+ const Direct3D11CaptureFrame frame = m_framePool.TryGetNextFrame();
+ if (!frame) {
+
+ // Stop capture and report failure if window was closed. If we don't stop,
+ // testing shows that either we don't get any frames, or we get blank frames.
+ // Emitting an error will prevent this inconsistent behavior, and makes the
+ // Windows implementation behave like the Linux implementation
+ if (!IsWindow(m_captureWindow))
+ throw std::runtime_error("Window was closed");
+
+ // Blank frames may come spuriously if no new window texture
+ // is available yet.
+ return {};
+ }
+
+ if (m_frameSize != frame.ContentSize()) {
+ m_frameSize = frame.ContentSize();
+ m_framePool.Recreate(getCaptureDevice(m_device), m_pixelFormat, 1, frame.ContentSize());
+ return {};
+ }
+
+ return copyTexture(m_device, frame.Surface());
+ }
+
+private:
+ static GraphicsCaptureItem createCaptureItem(HWND hwnd)
+ {
+ const auto factory = winrt::get_activation_factory<GraphicsCaptureItem>();
+ const auto interop = factory.as<IGraphicsCaptureItemInterop>();
+
+ GraphicsCaptureItem item = { nullptr };
+ winrt::hresult status = S_OK;
+
+ // Attempt to create capture item with retry, because this occasionally fails,
+ // particularly in unit tests. When the failure code is E_INVALIDARG, it
+ // seems to help to sleep for a bit and retry. See QTBUG-116025.
+ constexpr int maxRetry = 10;
+ constexpr std::chrono::milliseconds retryDelay{ 100 };
+ for (int retryNum = 0; retryNum < maxRetry; ++retryNum) {
+
+ status = interop->CreateForWindow(hwnd, winrt::guid_of<GraphicsCaptureItem>(),
+ winrt::put_abi(item));
+
+ if (status != E_INVALIDARG)
+ break;
+
+ qCWarning(qLcWindowCaptureUwp)
+ << "Failed to create capture item:"
+ << QString::fromStdWString(winrt::hresult_error(status).message().c_str())
+ << "Retry number" << retryNum;
+
+ if (retryNum + 1 < maxRetry)
+ QThread::sleep(retryDelay);
+ }
+
+ // Throw if we fail to create the capture item
+ check_hresult(status);
+
+ return item;
+ }
+
+ static IDirect3DDevice getCaptureDevice(const com_ptr<ID3D11Device> &d3dDevice)
+ {
+ const auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
+
+ com_ptr<IInspectable> device;
+ check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.get(), device.put()));
+
+ return device.as<IDirect3DDevice>();
+ }
+
+ static com_ptr<IDXGISurface> copyTexture(const com_ptr<ID3D11Device> &device,
+ const IDirect3DSurface &capturedTexture)
+ {
+ const auto dxgiInterop{ capturedTexture.as<IDirect3DDxgiInterfaceAccess>() };
+ if (!dxgiInterop)
+ return {};
+
+ com_ptr<IDXGISurface> dxgiSurface;
+ check_hresult(dxgiInterop->GetInterface(guid_of<IDXGISurface>(), dxgiSurface.put_void()));
+
+ DXGI_SURFACE_DESC desc = {};
+ check_hresult(dxgiSurface->GetDesc(&desc));
+
+ D3D11_TEXTURE2D_DESC texDesc = {};
+ texDesc.Width = desc.Width;
+ texDesc.Height = desc.Height;
+ texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ texDesc.Usage = D3D11_USAGE_STAGING;
+ texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ texDesc.MiscFlags = 0;
+ texDesc.BindFlags = 0;
+ texDesc.ArraySize = 1;
+ texDesc.MipLevels = 1;
+ texDesc.SampleDesc = { 1, 0 };
+
+ com_ptr<ID3D11Texture2D> texture;
+ check_hresult(device->CreateTexture2D(&texDesc, nullptr, texture.put()));
+
+ com_ptr<ID3D11DeviceContext> ctx;
+ device->GetImmediateContext(ctx.put());
+ ctx->CopyResource(texture.get(), dxgiSurface.as<ID3D11Resource>().get());
+
+ return texture.as<IDXGISurface>();
+ }
+
+ MultithreadedApartment m_comApartment{};
+ HWND m_captureWindow{};
+ winrt::Windows::Graphics::SizeInt32 m_frameSize{};
+ com_ptr<ID3D11Device> m_device;
+ Direct3D11CaptureFramePool m_framePool{ nullptr };
+ GraphicsCaptureSession m_session{ nullptr };
+ const DirectXPixelFormat m_pixelFormat = DirectXPixelFormat::R8G8B8A8UIntNormalized;
+};
+
+} // namespace
+
+class QFFmpegWindowCaptureUwp::Grabber : public QFFmpegSurfaceCaptureGrabber
+{
+ Q_OBJECT
+public:
+ Grabber(QFFmpegWindowCaptureUwp &capture, HWND hwnd)
+ : m_hwnd(hwnd),
+ m_format(QVideoFrameFormat(asQSize(getWindowSize(hwnd)),
+ QVideoFrameFormat::Format_RGBX8888))
+ {
+ const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
+ m_adapter = getAdapter(monitor);
+
+ const qreal refreshRate = getMonitorRefreshRateHz(monitor);
+
+ m_format.setStreamFrameRate(refreshRate);
+ setFrameRate(refreshRate);
+
+ addFrameCallback(capture, &QFFmpegWindowCaptureUwp::newVideoFrame);
+ connect(this, &Grabber::errorUpdated, &capture, &QFFmpegWindowCaptureUwp::updateError);
+ }
+
+ ~Grabber() override { stop(); }
+
+ QVideoFrameFormat frameFormat() const { return m_format; }
+
+protected:
+
+ void initializeGrabbingContext() override
+ {
+ if (!m_adapter || !IsWindow(m_hwnd))
+ return; // Error already logged
+
+ try {
+ m_windowGrabber = std::make_unique<WindowGrabber>(m_adapter.get(), m_hwnd);
+
+ QFFmpegSurfaceCaptureGrabber::initializeGrabbingContext();
+ } catch (const winrt::hresult_error &err) {
+
+ const QString message = QLatin1String("Unable to capture window: ")
+ + QString::fromWCharArray(err.message().c_str());
+
+ updateError(InternalError, message);
+ }
+ }
+
+ void finalizeGrabbingContext() override
+ {
+ QFFmpegSurfaceCaptureGrabber::finalizeGrabbingContext();
+ m_windowGrabber = nullptr;
+ }
+
+ QVideoFrame grabFrame() override
+ {
+ try {
+ com_ptr<IDXGISurface> texture = m_windowGrabber->tryGetFrame();
+ if (!texture)
+ return {}; // No frame available yet
+
+ const QSize size = getTextureSize(texture);
+
+ m_format.setFrameSize(size);
+
+ return QVideoFrame(new QUwpTextureVideoBuffer(std::move(texture)), m_format);
+
+ } catch (const winrt::hresult_error &err) {
+
+ const QString message = QLatin1String("Window capture failed: ")
+ + QString::fromWCharArray(err.message().c_str());
+
+ updateError(InternalError, message);
+ } catch (const std::runtime_error& e) {
+ updateError(CaptureFailed, QString::fromLatin1(e.what()));
+ }
+
+ return {};
+ }
+
+private:
+ static com_ptr<IDXGIAdapter1> getAdapter(HMONITOR handle)
+ {
+ com_ptr<IDXGIFactory1> factory;
+ check_hresult(CreateDXGIFactory1(guid_of<IDXGIFactory1>(), factory.put_void()));
+
+ com_ptr<IDXGIAdapter1> adapter;
+ for (quint32 i = 0; factory->EnumAdapters1(i, adapter.put()) == S_OK; adapter = nullptr, i++) {
+ com_ptr<IDXGIOutput> output;
+ for (quint32 j = 0; adapter->EnumOutputs(j, output.put()) == S_OK; output = nullptr, j++) {
+ DXGI_OUTPUT_DESC desc = {};
+ HRESULT hr = output->GetDesc(&desc);
+ if (hr == S_OK && desc.Monitor == handle)
+ return adapter;
+ }
+ }
+ return {};
+ }
+
+ static QSize getTextureSize(const com_ptr<IDXGISurface> &surf)
+ {
+ if (!surf)
+ return {};
+
+ DXGI_SURFACE_DESC desc;
+ check_hresult(surf->GetDesc(&desc));
+
+ return { static_cast<int>(desc.Width), static_cast<int>(desc.Height) };
+ }
+
+ static qreal getMonitorRefreshRateHz(HMONITOR handle)
+ {
+ DWORD count = 0;
+ if (GetNumberOfPhysicalMonitorsFromHMONITOR(handle, &count)) {
+ std::vector<PHYSICAL_MONITOR> monitors{ count };
+ if (GetPhysicalMonitorsFromHMONITOR(handle, count, monitors.data())) {
+ for (const auto &monitor : std::as_const(monitors)) {
+ MC_TIMING_REPORT screenTiming = {};
+ if (GetTimingReport(monitor.hPhysicalMonitor, &screenTiming)) {
+ // Empirically we found that GetTimingReport does not return
+ // the frequency in updates per second as documented, but in
+ // updates per 100 seconds.
+ return static_cast<qreal>(screenTiming.dwVerticalFrequencyInHZ) / 100.0;
+ }
+ }
+ }
+ }
+ return DefaultScreenCaptureFrameRate;
+ }
+
+ HWND m_hwnd{};
+ com_ptr<IDXGIAdapter1> m_adapter{};
+ std::unique_ptr<WindowGrabber> m_windowGrabber;
+ QVideoFrameFormat m_format;
+};
+
+QFFmpegWindowCaptureUwp::QFFmpegWindowCaptureUwp() : QPlatformSurfaceCapture(WindowSource{})
+{
+ qCDebug(qLcWindowCaptureUwp) << "Creating UWP screen capture";
+}
+
+QFFmpegWindowCaptureUwp::~QFFmpegWindowCaptureUwp() = default;
+
+static QString isCapturableWindow(HWND hwnd)
+{
+ if (!IsWindow(hwnd))
+ return "Invalid window handle";
+
+ if (hwnd == GetShellWindow())
+ return "Cannot capture the shell window";
+
+ wchar_t className[MAX_PATH] = {};
+ GetClassName(hwnd, className, MAX_PATH);
+ if (QString::fromWCharArray(className).length() == 0)
+ return "Cannot capture windows without a class name";
+
+ if (!IsWindowVisible(hwnd))
+ return "Cannot capture invisible windows";
+
+ if (GetAncestor(hwnd, GA_ROOT) != hwnd)
+ return "Can only capture root windows";
+
+ const LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
+ if (style & WS_DISABLED)
+ return "Cannot capture disabled windows";
+
+ const LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
+ if (exStyle & WS_EX_TOOLWINDOW)
+ return "No tooltips";
+
+ DWORD cloaked = FALSE;
+ const HRESULT hr = DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked));
+ if (SUCCEEDED(hr) && cloaked == DWM_CLOAKED_SHELL)
+ return "Cannot capture cloaked windows";
+
+ return {};
+}
+
+bool QFFmpegWindowCaptureUwp::setActiveInternal(bool active)
+{
+ if (static_cast<bool>(m_grabber) == active)
+ return false;
+
+ if (m_grabber) {
+ m_grabber.reset();
+ return true;
+ }
+
+ const auto window = source<WindowSource>();
+ const auto handle = QCapturableWindowPrivate::handle(window);
+
+ const auto hwnd = reinterpret_cast<HWND>(handle ? handle->id : 0);
+ if (const QString error = isCapturableWindow(hwnd); !error.isEmpty()) {
+ updateError(InternalError, error);
+ return false;
+ }
+
+ m_grabber = std::make_unique<Grabber>(*this, hwnd);
+ m_grabber->start();
+
+ return true;
+}
+
+bool QFFmpegWindowCaptureUwp::isSupported()
+{
+ return GraphicsCaptureSession::IsSupported();
+}
+
+QVideoFrameFormat QFFmpegWindowCaptureUwp::frameFormat() const
+{
+ if (m_grabber)
+ return m_grabber->frameFormat();
+ return {};
+}
+
+QT_END_NAMESPACE
+
+#include "qffmpegwindowcapture_uwp.moc"
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp_p.h b/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp_p.h
new file mode 100644
index 000000000..10f6e62be
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qffmpegwindowcapture_uwp_p.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGWINDOWCAPTURE_UWP_P_H
+#define QFFMPEGWINDOWCAPTURE_UWP_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qnamespace.h>
+#include "private/qplatformsurfacecapture_p.h"
+#include "qvideoframeformat.h"
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegWindowCaptureUwp : public QPlatformSurfaceCapture
+{
+public:
+ QFFmpegWindowCaptureUwp();
+ ~QFFmpegWindowCaptureUwp() override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+ static bool isSupported();
+
+private:
+ class Grabber;
+
+ bool setActiveInternal(bool active) override;
+
+private:
+ std::unique_ptr<Grabber> m_grabber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGWINDOWCAPTURE_UWP_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qgdiwindowcapture.cpp b/src/plugins/multimedia/ffmpeg/qgdiwindowcapture.cpp
new file mode 100644
index 000000000..ed85cd73a
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qgdiwindowcapture.cpp
@@ -0,0 +1,195 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qgdiwindowcapture_p.h"
+
+#include "qvideoframe.h"
+#include "qffmpegsurfacecapturegrabber_p.h"
+#include "private/qcapturablewindow_p.h"
+#include "private/qmemoryvideobuffer_p.h"
+
+#include <qt_windows.h>
+#include <QtCore/qloggingcategory.h>
+
+static Q_LOGGING_CATEGORY(qLcGdiWindowCapture, "qt.multimedia.ffmpeg.gdiwindowcapture");
+
+QT_BEGIN_NAMESPACE
+
+class QGdiWindowCapture::Grabber : public QFFmpegSurfaceCaptureGrabber
+{
+public:
+ static std::unique_ptr<Grabber> create(QGdiWindowCapture &capture, HWND hWnd)
+ {
+ auto hdcWindow = GetDC(hWnd);
+ if (!hdcWindow) {
+ capture.updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot create a window drawing context"));
+ return nullptr;
+ }
+
+ auto hdcMem = CreateCompatibleDC(hdcWindow);
+
+ if (!hdcMem) {
+ capture.updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot create a compatible drawing context"));
+ return nullptr;
+ }
+
+ std::unique_ptr<Grabber> result(new Grabber(capture, hWnd, hdcWindow, hdcMem));
+ if (!result->update())
+ return nullptr;
+
+ result->start();
+ return result;
+ }
+
+ ~Grabber() override
+ {
+ stop();
+
+ if (m_hBitmap)
+ DeleteObject(m_hBitmap);
+
+ if (m_hdcMem)
+ DeleteDC(m_hdcMem);
+
+ if (m_hdcWindow)
+ ReleaseDC(m_hwnd, m_hdcWindow);
+ }
+
+ QVideoFrameFormat format() const { return m_format; }
+
+private:
+ Grabber(QGdiWindowCapture &capture, HWND hWnd, HDC hdcWindow, HDC hdcMem)
+ : m_hwnd(hWnd), m_hdcWindow(hdcWindow), m_hdcMem(hdcMem)
+ {
+ if (auto rate = GetDeviceCaps(hdcWindow, VREFRESH); rate > 0)
+ setFrameRate(rate);
+
+ addFrameCallback(capture, &QGdiWindowCapture::newVideoFrame);
+ connect(this, &Grabber::errorUpdated, &capture, &QGdiWindowCapture::updateError);
+ }
+
+ bool update()
+ {
+ RECT windowRect{};
+ if (!GetWindowRect(m_hwnd, &windowRect)) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot get window size"));
+ return false;
+ }
+
+ const QSize size{ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
+
+ if (m_format.isValid() && size == m_format.frameSize() && m_hBitmap)
+ return true;
+
+ if (m_hBitmap)
+ DeleteObject(std::exchange(m_hBitmap, nullptr));
+
+ if (size.isEmpty()) {
+ m_format = {};
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Invalid window size"));
+ return false;
+ }
+
+ m_hBitmap = CreateCompatibleBitmap(m_hdcWindow, size.width(), size.height());
+
+ if (!m_hBitmap) {
+ m_format = {};
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot create a compatible bitmap"));
+ return false;
+ }
+
+ QVideoFrameFormat format(size, QVideoFrameFormat::Format_BGRX8888);
+ format.setStreamFrameRate(frameRate());
+ m_format = format;
+ return true;
+ }
+
+ QVideoFrame grabFrame() override
+ {
+ if (!update())
+ return {};
+
+ const auto oldBitmap = SelectObject(m_hdcMem, m_hBitmap);
+ auto deselect = qScopeGuard([&]() { SelectObject(m_hdcMem, oldBitmap); });
+
+ const auto size = m_format.frameSize();
+
+ if (!BitBlt(m_hdcMem, 0, 0, size.width(), size.height(), m_hdcWindow, 0, 0, SRCCOPY)) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot copy image to the compatible DC"));
+ return {};
+ }
+
+ BITMAPINFO info{};
+ auto &header = info.bmiHeader;
+ header.biSize = sizeof(BITMAPINFOHEADER);
+ header.biWidth = size.width();
+ header.biHeight = -size.height(); // negative height to ensure top-down orientation
+ header.biPlanes = 1;
+ header.biBitCount = 32;
+ header.biCompression = BI_RGB;
+
+ const auto bytesPerLine = size.width() * 4;
+
+ QByteArray array(size.height() * bytesPerLine, Qt::Uninitialized);
+
+ const auto copiedHeight = GetDIBits(m_hdcMem, m_hBitmap, 0, size.height(), array.data(), &info, DIB_RGB_COLORS);
+ if (copiedHeight != size.height()) {
+ qCWarning(qLcGdiWindowCapture) << copiedHeight << "lines have been copied, expected:" << size.height();
+ // In practice, it might fail randomly first time after start. So we don't consider it as an error.
+ // TODO: investigate reasons and properly handle the error
+ // updateError(QPlatformSurfaceCapture::CaptureFailed,
+ // QLatin1String("Cannot get raw image data"));
+ return {};
+ }
+
+ if (header.biWidth != size.width() || header.biHeight != -size.height()
+ || header.biPlanes != 1 || header.biBitCount != 32 || header.biCompression != BI_RGB) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Output bitmap info is unexpected"));
+ return {};
+ }
+
+ return QVideoFrame(new QMemoryVideoBuffer(array, bytesPerLine), m_format);
+ }
+
+private:
+ HWND m_hwnd = {};
+ QVideoFrameFormat m_format;
+ HDC m_hdcWindow = {};
+ HDC m_hdcMem = {};
+ HBITMAP m_hBitmap = {};
+};
+
+QGdiWindowCapture::QGdiWindowCapture() : QPlatformSurfaceCapture(WindowSource{}) { }
+
+QGdiWindowCapture::~QGdiWindowCapture() = default;
+
+QVideoFrameFormat QGdiWindowCapture::frameFormat() const
+{
+ return m_grabber ? m_grabber->format() : QVideoFrameFormat();
+}
+
+bool QGdiWindowCapture::setActiveInternal(bool active)
+{
+ if (active == static_cast<bool>(m_grabber))
+ return true;
+
+ if (m_grabber) {
+ m_grabber.reset();
+ } else {
+ auto window = source<WindowSource>();
+ auto handle = QCapturableWindowPrivate::handle(window);
+
+ m_grabber = Grabber::create(*this, reinterpret_cast<HWND>(handle ? handle->id : 0));
+ }
+
+ return static_cast<bool>(m_grabber) == active;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qgdiwindowcapture_p.h b/src/plugins/multimedia/ffmpeg/qgdiwindowcapture_p.h
new file mode 100644
index 000000000..d4498455c
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qgdiwindowcapture_p.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGDIWINDOWCAPTURE_P_H
+#define QGDIWINDOWCAPTURE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qplatformsurfacecapture_p.h"
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QGdiWindowCapture : public QPlatformSurfaceCapture
+{
+ class Grabber;
+
+public:
+ QGdiWindowCapture();
+ ~QGdiWindowCapture() override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+protected:
+ bool setActiveInternal(bool active) override;
+
+private:
+ std::unique_ptr<Grabber> m_grabber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGDIWINDOWCAPTURE_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp b/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp
new file mode 100644
index 000000000..57eda3495
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture.cpp
@@ -0,0 +1,220 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qvideoframe.h"
+#include "qgrabwindowsurfacecapture_p.h"
+#include "qscreencapture.h"
+#include "qffmpegsurfacecapturegrabber_p.h"
+
+#include "private/qimagevideobuffer_p.h"
+#include "private/qcapturablewindow_p.h"
+
+#include "qscreen.h"
+#include "qmutex.h"
+#include "qwaitcondition.h"
+#include "qpixmap.h"
+#include "qguiapplication.h"
+#include "qwindow.h"
+#include "qpointer.h"
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+using WindowUPtr = std::unique_ptr<QWindow>;
+
+} // namespace
+
+class QGrabWindowSurfaceCapture::Grabber : public QFFmpegSurfaceCaptureGrabber
+{
+public:
+ Grabber(QGrabWindowSurfaceCapture &capture, QScreen *screen) : Grabber(capture, screen, nullptr)
+ {
+ Q_ASSERT(screen);
+ }
+
+ Grabber(QGrabWindowSurfaceCapture &capture, WindowUPtr window)
+ : Grabber(capture, nullptr, std::move(window))
+ {
+ Q_ASSERT(m_window);
+ }
+
+ ~Grabber() override {
+ stop();
+
+ Q_ASSERT(!m_screenRemovingLocked);
+ }
+
+ const QVideoFrameFormat &format()
+ {
+ QMutexLocker locker(&m_formatMutex);
+ while (!m_format)
+ m_waitForFormat.wait(&m_formatMutex);
+ return *m_format;
+ }
+
+private:
+ Grabber(QGrabWindowSurfaceCapture &capture, QScreen *screen, WindowUPtr window)
+ : QFFmpegSurfaceCaptureGrabber(
+ QGuiApplication::platformName() == QLatin1String("eglfs")
+ ? QFFmpegSurfaceCaptureGrabber::UseCurrentThread
+ : QFFmpegSurfaceCaptureGrabber::CreateGrabbingThread),
+ m_capture(capture),
+ m_screen(screen),
+ m_window(std::move(window))
+ {
+ connect(qApp, &QGuiApplication::screenRemoved, this, &Grabber::onScreenRemoved);
+ addFrameCallback(m_capture, &QGrabWindowSurfaceCapture::newVideoFrame);
+ connect(this, &Grabber::errorUpdated, &m_capture, &QGrabWindowSurfaceCapture::updateError);
+ }
+
+ void onScreenRemoved(QScreen *screen)
+ {
+ /* The hack allows to lock screens removing while QScreen::grabWindow is in progress.
+ * The current solution works since QGuiApplication::screenRemoved is emitted from
+ * the destructor of QScreen before destruction members of the object.
+ * Note, QGuiApplication works with screens in the main thread, and any removing of a screen
+ * must be synchronized with grabbing thread.
+ */
+ QMutexLocker locker(&m_screenRemovingMutex);
+
+ if (m_screenRemovingLocked) {
+ qDebug() << "Screen" << screen->name()
+ << "is removed while screen window grabbing lock is active";
+ }
+
+ while (m_screenRemovingLocked)
+ m_screenRemovingWc.wait(&m_screenRemovingMutex);
+ }
+
+ void setScreenRemovingLocked(bool locked)
+ {
+ Q_ASSERT(locked != m_screenRemovingLocked);
+
+ {
+ QMutexLocker locker(&m_screenRemovingMutex);
+ m_screenRemovingLocked = locked;
+ }
+
+ if (!locked)
+ m_screenRemovingWc.wakeAll();
+ }
+
+ void updateFormat(const QVideoFrameFormat &format)
+ {
+ if (m_format && m_format->isValid())
+ return;
+
+ {
+ QMutexLocker locker(&m_formatMutex);
+ m_format = format;
+ }
+
+ m_waitForFormat.wakeAll();
+ }
+
+ QVideoFrame grabFrame() override
+ {
+ setScreenRemovingLocked(true);
+ auto screenGuard = qScopeGuard(std::bind(&Grabber::setScreenRemovingLocked, this, false));
+
+ WId wid = m_window ? m_window->winId() : 0;
+ QScreen *screen = m_window ? m_window->screen() : m_screen ? m_screen.data() : nullptr;
+
+ if (!screen) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed, "Screen not found");
+ return {};
+ }
+
+ setFrameRate(screen->refreshRate());
+
+ QPixmap p = screen->grabWindow(wid);
+ auto buffer = std::make_unique<QImageVideoBuffer>(p.toImage());
+ const auto img = buffer->underlyingImage();
+
+ QVideoFrameFormat format(img.size(),
+ QVideoFrameFormat::pixelFormatFromImageFormat(img.format()));
+ format.setStreamFrameRate(screen->refreshRate());
+ updateFormat(format);
+
+ if (!format.isValid()) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ "Failed to grab the screen content");
+ return {};
+ }
+
+ return QVideoFrame(buffer.release(), format);
+ }
+
+private:
+ QGrabWindowSurfaceCapture &m_capture;
+ QPointer<QScreen> m_screen;
+ WindowUPtr m_window;
+
+ QMutex m_formatMutex;
+ QWaitCondition m_waitForFormat;
+ std::optional<QVideoFrameFormat> m_format;
+
+ QMutex m_screenRemovingMutex;
+ bool m_screenRemovingLocked = false;
+ QWaitCondition m_screenRemovingWc;
+};
+
+QGrabWindowSurfaceCapture::QGrabWindowSurfaceCapture(Source initialSource)
+ : QPlatformSurfaceCapture(initialSource)
+{
+}
+
+QGrabWindowSurfaceCapture::~QGrabWindowSurfaceCapture() = default;
+
+QVideoFrameFormat QGrabWindowSurfaceCapture::frameFormat() const
+{
+ if (m_grabber)
+ return m_grabber->format();
+ else
+ return {};
+}
+
+bool QGrabWindowSurfaceCapture::setActiveInternal(bool active)
+{
+ if (active == static_cast<bool>(m_grabber))
+ return true;
+
+ if (m_grabber)
+ m_grabber.reset();
+ else
+ std::visit([this](auto source) { activate(source); }, source());
+
+ return static_cast<bool>(m_grabber) == active;
+}
+
+void QGrabWindowSurfaceCapture::activate(ScreenSource screen)
+{
+ if (!checkScreenWithError(screen))
+ return;
+
+ m_grabber = std::make_unique<Grabber>(*this, screen);
+ m_grabber->start();
+}
+
+void QGrabWindowSurfaceCapture::activate(WindowSource window)
+{
+ auto handle = QCapturableWindowPrivate::handle(window);
+ auto wid = handle ? handle->id : 0;
+ if (auto wnd = WindowUPtr(QWindow::fromWinId(wid))) {
+ if (!wnd->screen()) {
+ updateError(InternalError,
+ "Window " + QString::number(wid) + " doesn't belong to any screen");
+ } else {
+ m_grabber = std::make_unique<Grabber>(*this, std::move(wnd));
+ m_grabber->start();
+ }
+ } else {
+ updateError(NotFound,
+ "Window " + QString::number(wid) + "doesn't exist or permissions denied");
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture_p.h b/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture_p.h
new file mode 100644
index 000000000..a76ce9507
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qgrabwindowsurfacecapture_p.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGRABWINDOWSURFACECAPTURE_P_H
+#define QGRABWINDOWSURFACECAPTURE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qplatformsurfacecapture_p.h"
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QGrabWindowSurfaceCapture : public QPlatformSurfaceCapture
+{
+ class Grabber;
+
+public:
+ explicit QGrabWindowSurfaceCapture(Source initialSource);
+ ~QGrabWindowSurfaceCapture() override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+protected:
+ bool setActiveInternal(bool active) override;
+
+private:
+ void activate(ScreenSource);
+
+ void activate(WindowSource);
+
+private:
+ std::unique_ptr<Grabber> m_grabber;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGRABWINDOWSURFACECAPTURE_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qopenglvideobuffer.cpp b/src/plugins/multimedia/ffmpeg/qopenglvideobuffer.cpp
new file mode 100644
index 000000000..c3e739ffd
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qopenglvideobuffer.cpp
@@ -0,0 +1,101 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qopenglvideobuffer_p.h"
+
+#include <qoffscreensurface.h>
+#include <qthread.h>
+#include <private/qimagevideobuffer_p.h>
+
+#include <QtOpenGL/private/qopenglcompositor_p.h>
+#include <QtOpenGL/private/qopenglframebufferobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static QOpenGLContext *createContext(QOpenGLContext *shareContext)
+{
+ // Create an OpenGL context for the current thread. The lifetime of the context is tied to the
+ // lifetime of the current thread.
+ auto context = std::make_unique<QOpenGLContext>();
+ context->setShareContext(shareContext);
+
+ if (!context->create()) {
+ qWarning() << "Couldn't create an OpenGL context for QOpenGLVideoBuffer";
+ return nullptr;
+ }
+
+ QObject::connect(QThread::currentThread(), &QThread::finished,
+ context.get(), &QOpenGLContext::deleteLater);
+ return context.release();
+}
+
+static bool setCurrentOpenGLContext()
+{
+ auto compositorContext = QOpenGLCompositor::instance()->context();
+
+ // A thread-local variable is used to avoid creating a new context if we're called on the same
+ // thread. The context lifetime is tied to the current thread lifetime (see createContext()).
+ static thread_local QOpenGLContext *context = nullptr;
+ static thread_local QOffscreenSurface *surface = nullptr;
+
+ if (!context) {
+ context = (compositorContext->thread() == QThread::currentThread())
+ ? compositorContext
+ : createContext(compositorContext);
+
+ if (!context)
+ return false;
+
+ surface = new QOffscreenSurface(nullptr, context);
+ surface->setFormat(context->format());
+ surface->create();
+ }
+
+ return context->makeCurrent(surface);
+}
+
+QOpenGLVideoBuffer::QOpenGLVideoBuffer(std::unique_ptr<QOpenGLFramebufferObject> fbo)
+ : QAbstractVideoBuffer(QVideoFrame::RhiTextureHandle), m_fbo(std::move(fbo))
+{
+ Q_ASSERT(m_fbo);
+}
+
+QOpenGLVideoBuffer::~QOpenGLVideoBuffer() { }
+
+QVideoFrame::MapMode QOpenGLVideoBuffer::mapMode() const
+{
+ return m_imageBuffer ? m_imageBuffer->mapMode() : QVideoFrame::NotMapped;
+}
+
+QAbstractVideoBuffer::MapData QOpenGLVideoBuffer::map(QVideoFrame::MapMode mode)
+{
+ return ensureImageBuffer().map(mode);
+}
+
+void QOpenGLVideoBuffer::unmap()
+{
+ if (m_imageBuffer)
+ m_imageBuffer->unmap();
+}
+
+quint64 QOpenGLVideoBuffer::textureHandle(QRhi *, int plane) const
+{
+ Q_UNUSED(plane);
+ return m_fbo->texture();
+}
+
+QImageVideoBuffer &QOpenGLVideoBuffer::ensureImageBuffer()
+{
+ // Create image buffer if not yet created.
+ // This is protected by mapMutex in QVideoFrame::map.
+ if (!m_imageBuffer) {
+ if (!setCurrentOpenGLContext())
+ qWarning() << "Failed to set current OpenGL context";
+
+ m_imageBuffer = std::make_unique<QImageVideoBuffer>(m_fbo->toImage(false));
+ }
+
+ return *m_imageBuffer;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qopenglvideobuffer_p.h b/src/plugins/multimedia/ffmpeg/qopenglvideobuffer_p.h
new file mode 100644
index 000000000..bbbb2f2c7
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qopenglvideobuffer_p.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QOPENGLVIDEOBUFFER_P_H
+#define QOPENGLVIDEOBUFFER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qabstractvideobuffer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QImageVideoBuffer;
+class QOpenGLFramebufferObject;
+
+class QOpenGLVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ QOpenGLVideoBuffer(std::unique_ptr<QOpenGLFramebufferObject> fbo);
+ ~QOpenGLVideoBuffer();
+
+ QVideoFrame::MapMode mapMode() const override;
+ MapData map(QVideoFrame::MapMode mode) override;
+ void unmap() override;
+ quint64 textureHandle(QRhi *, int plane) const override;
+
+ QImageVideoBuffer &ensureImageBuffer();
+
+private:
+ std::unique_ptr<QOpenGLFramebufferObject> m_fbo;
+ std::unique_ptr<QImageVideoBuffer> m_imageBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOPENGLVIDEOBUFFER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp b/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp
index 36ef020db..2086af10d 100644
--- a/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp
+++ b/src/plugins/multimedia/ffmpeg/qv4l2camera.cpp
@@ -1,85 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qv4l2camera_p.h"
+#include "qv4l2filedescriptor_p.h"
+#include "qv4l2memorytransfer_p.h"
-#include <qdir.h>
-#include <qmutex.h>
-#include <qendian.h>
#include <private/qcameradevice_p.h>
-#include <private/qabstractvideobuffer_p.h>
-#include <private/qvideotexturehelper_p.h>
#include <private/qmultimediautils_p.h>
-#include <private/qplatformmediadevices_p.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <fcntl.h>
+#include <private/qmemoryvideobuffer_p.h>
#include <private/qcore_unix_p.h>
-#include <sys/mman.h>
-#include <linux/videodev2.h>
+#include <qsocketnotifier.h>
+#include <qloggingcategory.h>
QT_BEGIN_NAMESPACE
-QV4L2CameraDevices::QV4L2CameraDevices(QPlatformMediaIntegration *integration)
- : QPlatformVideoDevices(integration)
-{
- deviceWatcher.addPath(QLatin1String("/dev"));
- connect(&deviceWatcher, &QFileSystemWatcher::directoryChanged, this, &QV4L2CameraDevices::checkCameras);
- doCheckCameras();
-}
-
-QList<QCameraDevice> QV4L2CameraDevices::videoDevices() const
-{
- return cameras;
-}
-
-void QV4L2CameraDevices::checkCameras()
-{
- doCheckCameras();
- videoInputsChanged();
-}
+static Q_LOGGING_CATEGORY(qLcV4L2Camera, "qt.multimedia.ffmpeg.v4l2camera");
-const struct {
+static const struct {
QVideoFrameFormat::PixelFormat fmt;
uint32_t v4l2Format;
} formatMap[] = {
@@ -105,7 +43,7 @@ const struct {
{ QVideoFrameFormat::Format_Invalid, 0 },
};
-static QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format)
+QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format)
{
auto *f = formatMap;
while (f->v4l2Format) {
@@ -116,7 +54,7 @@ static QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format)
return QVideoFrameFormat::Format_Invalid;
}
-static uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format)
+uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format)
{
auto *f = formatMap;
while (f->v4l2Format) {
@@ -127,177 +65,6 @@ static uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format)
return 0;
}
-
-void QV4L2CameraDevices::doCheckCameras()
-{
- cameras.clear();
-
- QDir dir(QLatin1String("/dev"));
- const auto devices = dir.entryList(QDir::System);
-
- bool first = true;
-
- for (auto device : devices) {
-// qDebug() << "device:" << device;
- if (!device.startsWith(QLatin1String("video")))
- continue;
-
- QByteArray file = QFile::encodeName(dir.filePath(device));
- int fd = open(file.constData(), O_RDONLY);
- if (fd < 0)
- continue;
-
- QCameraDevicePrivate *camera = nullptr;
- v4l2_fmtdesc formatDesc = {};
-
- struct v4l2_capability cap;
- if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
- goto fail;
-
- if (cap.device_caps & V4L2_CAP_META_CAPTURE)
- goto fail;
- if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
- goto fail;
- if (!(cap.capabilities & V4L2_CAP_STREAMING))
- goto fail;
-
- camera = new QCameraDevicePrivate;
- camera->id = file;
- camera->description = QString::fromUtf8((const char *)cap.card);
-// qDebug() << "found camera" << camera->id << camera->description;
-
- formatDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- while (!ioctl(fd, VIDIOC_ENUM_FMT, &formatDesc)) {
- auto pixelFmt = formatForV4L2Format(formatDesc.pixelformat);
- qDebug() << " " << pixelFmt;
-
- if (pixelFmt == QVideoFrameFormat::Format_Invalid) {
- ++formatDesc.index;
- continue;
- }
-
-// qDebug() << "frame sizes:";
- v4l2_frmsizeenum frameSize = {};
- frameSize.pixel_format = formatDesc.pixelformat;
-
- while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) {
- if (frameSize.type != V4L2_FRMSIZE_TYPE_DISCRETE)
- continue;
-
- QSize resolution(frameSize.discrete.width, frameSize.discrete.height);
- float min = 1e10;
- float max = 0;
-
- v4l2_frmivalenum frameInterval = {};
- frameInterval.pixel_format = formatDesc.pixelformat;
- frameInterval.width = frameSize.discrete.width;
- frameInterval.height = frameSize.discrete.height;
-
- while (!ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameInterval)) {
- if (frameInterval.type != V4L2_FRMIVAL_TYPE_DISCRETE)
- continue;
- ++frameInterval.index;
- float rate = float(frameInterval.discrete.denominator)/float(frameInterval.discrete.numerator);
- if (rate > max)
- max = rate;
- if (rate < min)
- min = rate;
- }
-
-// qDebug() << " " << resolution << min << max;
- ++frameSize.index;
-
- if (min <= max) {
- QCameraFormatPrivate *fmt = new QCameraFormatPrivate;
- fmt->pixelFormat = pixelFmt;
- fmt->resolution = resolution;
- fmt->minFrameRate = min;
- fmt->maxFrameRate = max;
- camera->videoFormats.append(fmt->create());
- camera->photoResolutions.append(resolution);
- }
- }
-
- ++formatDesc.index;
- }
-
- // first camera is default
- camera->isDefault = first;
- first = false;
-
- cameras.append(camera->create());
-
- close(fd);
- continue;
-
- fail:
- if (camera)
- delete camera;
- close(fd);
- }
-}
-
-class QV4L2VideoBuffer : public QAbstractVideoBuffer
-{
-public:
- QV4L2VideoBuffer(QV4L2CameraBuffers *d, int index)
- : QAbstractVideoBuffer(QVideoFrame::NoHandle, nullptr)
- , index(index)
- , d(d)
- {}
- ~QV4L2VideoBuffer()
- {
- d->release(index);
- }
-
- QVideoFrame::MapMode mapMode() const override { return m_mode; }
- MapData map(QVideoFrame::MapMode mode) override {
- m_mode = mode;
- return d->v4l2FileDescriptor >= 0 ? data : MapData{};
- }
- void unmap() override {
- m_mode = QVideoFrame::NotMapped;
- }
-
- QVideoFrame::MapMode m_mode = QVideoFrame::NotMapped;
- MapData data;
- int index = 0;
- QExplicitlySharedDataPointer<QV4L2CameraBuffers> d;
-};
-
-QV4L2CameraBuffers::~QV4L2CameraBuffers()
-{
- QMutexLocker locker(&mutex);
- Q_ASSERT(v4l2FileDescriptor < 0);
- unmapBuffers();
-}
-
-
-
-void QV4L2CameraBuffers::release(int index)
-{
- QMutexLocker locker(&mutex);
- if (v4l2FileDescriptor < 0 || index >= mappedBuffers.size())
- return;
-
- struct v4l2_buffer buf = {};
-
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- buf.index = index;
-
- if (ioctl(v4l2FileDescriptor, VIDIOC_QBUF, &buf) < 0)
- qWarning() << "Couldn't release V4L2 buffer" << errno << strerror(errno) << index;
-}
-
-void QV4L2CameraBuffers::unmapBuffers()
-{
- for (const auto &b : qAsConst(mappedBuffers))
- munmap(b.data, b.size);
- mappedBuffers.clear();
-}
-
QV4L2Camera::QV4L2Camera(QCamera *camera)
: QPlatformCamera(camera)
{
@@ -305,7 +72,6 @@ QV4L2Camera::QV4L2Camera(QCamera *camera)
QV4L2Camera::~QV4L2Camera()
{
- setActive(false);
stopCapturing();
closeV4L2Fd();
}
@@ -326,13 +92,11 @@ void QV4L2Camera::setActive(bool active)
resolveCameraFormat({});
m_active = active;
- if (m_active) {
- setV4L2CameraFormat();
- initMMap();
+ if (m_active)
startCapturing();
- } else {
+ else
stopCapturing();
- }
+
emit newVideoFrame({});
emit activeChanged(active);
@@ -342,9 +106,8 @@ void QV4L2Camera::setCamera(const QCameraDevice &camera)
{
if (m_cameraDevice == camera)
return;
- if (m_active)
- stopCapturing();
+ stopCapturing();
closeV4L2Fd();
m_cameraDevice = camera;
@@ -352,11 +115,8 @@ void QV4L2Camera::setCamera(const QCameraDevice &camera)
initV4L2Controls();
- if (m_active) {
- setV4L2CameraFormat();
- initMMap();
+ if (m_active)
startCapturing();
- }
}
bool QV4L2Camera::setCameraFormat(const QCameraFormat &format)
@@ -370,9 +130,8 @@ bool QV4L2Camera::setCameraFormat(const QCameraFormat &format)
if (m_active) {
stopCapturing();
closeV4L2Fd();
+
initV4L2Controls();
- setV4L2CameraFormat();
- initMMap();
startCapturing();
}
@@ -398,31 +157,31 @@ void QV4L2Camera::setFocusMode(QCamera::FocusMode mode)
return;
bool focusDist = supportedFeatures() & QCamera::Feature::FocusDistance;
- if (!focusDist && !v4l2RangedFocus)
+ if (!focusDist && !m_v4l2Info.rangedFocus)
return;
switch (mode) {
default:
case QCamera::FocusModeAuto:
setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
- if (v4l2RangedFocus)
+ if (m_v4l2Info.rangedFocus)
setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_AUTO);
break;
case QCamera::FocusModeAutoNear:
setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
- if (v4l2RangedFocus)
+ if (m_v4l2Info.rangedFocus)
setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_MACRO);
else if (focusDist)
- setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, v4l2MinFocus);
+ setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, m_v4l2Info.minFocus);
break;
case QCamera::FocusModeAutoFar:
setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 1);
- if (v4l2RangedFocus)
+ if (m_v4l2Info.rangedFocus)
setV4L2Parameter(V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_INFINITY);
break;
case QCamera::FocusModeInfinity:
setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
- setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, v4l2MaxFocus);
+ setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, m_v4l2Info.maxFocus);
break;
case QCamera::FocusModeManual:
setV4L2Parameter(V4L2_CID_FOCUS_AUTO, 0);
@@ -434,17 +193,17 @@ void QV4L2Camera::setFocusMode(QCamera::FocusMode mode)
void QV4L2Camera::setFocusDistance(float d)
{
- int distance = v4l2MinFocus + int((v4l2MaxFocus - v4l2MinFocus)*d);
+ int distance = m_v4l2Info.minFocus + int((m_v4l2Info.maxFocus - m_v4l2Info.minFocus) * d);
setV4L2Parameter(V4L2_CID_FOCUS_ABSOLUTE, distance);
focusDistanceChanged(d);
}
void QV4L2Camera::zoomTo(float factor, float)
{
- if (v4l2MaxZoom == v4l2MinZoom)
+ if (m_v4l2Info.maxZoom == m_v4l2Info.minZoom)
return;
factor = qBound(1., factor, 2.);
- int zoom = v4l2MinZoom + (factor - 1.)*(v4l2MaxZoom - v4l2MinZoom);
+ int zoom = m_v4l2Info.minZoom + (factor - 1.) * (m_v4l2Info.maxZoom - m_v4l2Info.minZoom);
setV4L2Parameter(V4L2_CID_ZOOM_ABSOLUTE, zoom);
zoomFactorChanged(factor);
}
@@ -460,7 +219,7 @@ bool QV4L2Camera::isFocusModeSupported(QCamera::FocusMode mode) const
void QV4L2Camera::setFlashMode(QCamera::FlashMode mode)
{
- if (!v4l2FlashSupported || mode == QCamera::FlashOn)
+ if (!m_v4l2Info.flashSupported || mode == QCamera::FlashOn)
return;
setV4L2Parameter(V4L2_CID_FLASH_LED_MODE, mode == QCamera::FlashAuto ? V4L2_FLASH_LED_MODE_FLASH : V4L2_FLASH_LED_MODE_NONE);
flashModeChanged(mode);
@@ -468,7 +227,7 @@ void QV4L2Camera::setFlashMode(QCamera::FlashMode mode)
bool QV4L2Camera::isFlashModeSupported(QCamera::FlashMode mode) const
{
- if (v4l2FlashSupported && mode == QCamera::FlashAuto)
+ if (m_v4l2Info.flashSupported && mode == QCamera::FlashAuto)
return true;
return mode == QCamera::FlashOff;
}
@@ -479,15 +238,12 @@ bool QV4L2Camera::isFlashReady() const
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0)
- return true;
-
- return false;
+ return m_v4l2FileDescriptor && m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl);
}
void QV4L2Camera::setTorchMode(QCamera::TorchMode mode)
{
- if (!v4l2TorchSupported || mode == QCamera::TorchOn)
+ if (!m_v4l2Info.torchSupported || mode == QCamera::TorchOn)
return;
setV4L2Parameter(V4L2_CID_FLASH_LED_MODE, mode == QCamera::TorchOn ? V4L2_FLASH_LED_MODE_TORCH : V4L2_FLASH_LED_MODE_NONE);
torchModeChanged(mode);
@@ -496,13 +252,13 @@ void QV4L2Camera::setTorchMode(QCamera::TorchMode mode)
bool QV4L2Camera::isTorchModeSupported(QCamera::TorchMode mode) const
{
if (mode == QCamera::TorchOn)
- return v4l2TorchSupported;
+ return m_v4l2Info.torchSupported;
return mode == QCamera::TorchOff;
}
void QV4L2Camera::setExposureMode(QCamera::ExposureMode mode)
{
- if (v4l2AutoExposureSupported && v4l2ManualExposureSupported) {
+ if (m_v4l2Info.autoExposureSupported && m_v4l2Info.manualExposureSupported) {
if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual)
return;
int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
@@ -516,15 +272,16 @@ bool QV4L2Camera::isExposureModeSupported(QCamera::ExposureMode mode) const
{
if (mode == QCamera::ExposureAuto)
return true;
- if (v4l2ManualExposureSupported && v4l2AutoExposureSupported)
+ if (m_v4l2Info.manualExposureSupported && m_v4l2Info.autoExposureSupported)
return mode == QCamera::ExposureManual;
return false;
}
void QV4L2Camera::setExposureCompensation(float compensation)
{
- if ((v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) {
- int value = qBound(v4l2MinExposureAdjustment, (int)(compensation*1000), v4l2MaxExposureAdjustment);
+ if ((m_v4l2Info.minExposureAdjustment != 0 || m_v4l2Info.maxExposureAdjustment != 0)) {
+ int value = qBound(m_v4l2Info.minExposureAdjustment, (int)(compensation * 1000),
+ m_v4l2Info.maxExposureAdjustment);
setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value);
exposureCompensationChanged(value/1000.);
return;
@@ -552,8 +309,9 @@ int QV4L2Camera::isoSensitivity() const
void QV4L2Camera::setManualExposureTime(float secs)
{
- if (v4l2ManualExposureSupported && v4l2AutoExposureSupported) {
- int exposure = qBound(v4l2MinExposure, qRound(secs*10000.), v4l2MaxExposure);
+ if (m_v4l2Info.manualExposureSupported && m_v4l2Info.autoExposureSupported) {
+ int exposure =
+ qBound(m_v4l2Info.minExposure, qRound(secs * 10000.), m_v4l2Info.maxExposure);
setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure);
exposureTimeChanged(exposure/10000.);
return;
@@ -567,7 +325,7 @@ float QV4L2Camera::exposureTime() const
bool QV4L2Camera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
{
- if (v4l2AutoWhiteBalanceSupported && v4l2ColorTemperatureSupported)
+ if (m_v4l2Info.autoWhiteBalanceSupported && m_v4l2Info.colorTemperatureSupported)
return true;
return mode == QCamera::WhiteBalanceAuto;
@@ -600,127 +358,114 @@ void QV4L2Camera::setColorTemperature(int temperature)
void QV4L2Camera::readFrame()
{
- if (!d)
- return;
+ Q_ASSERT(m_memoryTransfer);
- v4l2_buffer buf = {};
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
+ auto buffer = m_memoryTransfer->dequeueBuffer();
+ if (!buffer) {
+ qCWarning(qLcV4L2Camera) << "Cannot take buffer";
- if (ioctl(d->v4l2FileDescriptor, VIDIOC_DQBUF, &buf) < 0) {
if (errno == ENODEV) {
// camera got removed while being active
stopCapturing();
closeV4L2Fd();
- return;
}
- if (errno != EAGAIN)
- qWarning() << "error calling VIDIOC_DQBUF" << errno << strerror(errno);
+
+ return;
}
- Q_ASSERT(buf.index < d->mappedBuffers.size());
- int i = buf.index;
-
-// auto textureDesc = QVideoTextureHelper::textureDescription(m_format.pixelFormat());
-
- QV4L2VideoBuffer *buffer = new QV4L2VideoBuffer(d.get(), i);
- buffer->data.nPlanes = 1;
- buffer->data.bytesPerLine[0] = bytesPerLine;
- buffer->data.data[0] = (uchar *)d->mappedBuffers.at(i).data;
- buffer->data.size[0] = d->mappedBuffers.at(i).size;
- QVideoFrameFormat fmt(m_cameraFormat.resolution(), m_cameraFormat.pixelFormat());
- fmt.setColorSpace(colorSpace);
-// qDebug() << "got a frame" << d->mappedBuffers.at(i).data << d->mappedBuffers.at(i).size << fmt << i;
- QVideoFrame frame(buffer, fmt);
-
- if (firstFrameTime.tv_sec == -1)
- firstFrameTime = buf.timestamp;
- qint64 secs = buf.timestamp.tv_sec - firstFrameTime.tv_sec;
- qint64 usecs = buf.timestamp.tv_usec - firstFrameTime.tv_usec;
+ auto videoBuffer = new QMemoryVideoBuffer(buffer->data, m_bytesPerLine);
+ QVideoFrame frame(videoBuffer, frameFormat());
+
+ auto &v4l2Buffer = buffer->v4l2Buffer;
+
+ if (m_firstFrameTime.tv_sec == -1)
+ m_firstFrameTime = v4l2Buffer.timestamp;
+ qint64 secs = v4l2Buffer.timestamp.tv_sec - m_firstFrameTime.tv_sec;
+ qint64 usecs = v4l2Buffer.timestamp.tv_usec - m_firstFrameTime.tv_usec;
frame.setStartTime(secs*1000000 + usecs);
- frame.setEndTime(frame.startTime() + frameDuration);
+ frame.setEndTime(frame.startTime() + m_frameDuration);
emit newVideoFrame(frame);
+
+ if (!m_memoryTransfer->enqueueBuffer(v4l2Buffer.index))
+ qCWarning(qLcV4L2Camera) << "Cannot add buffer";
}
void QV4L2Camera::setCameraBusy()
{
- cameraBusy = true;
- error(QCamera::CameraError, tr("Camera is in use."));
+ m_cameraBusy = true;
+ emit error(QCamera::CameraError, QLatin1String("Camera is in use"));
}
void QV4L2Camera::initV4L2Controls()
{
- v4l2AutoWhiteBalanceSupported = false;
- v4l2ColorTemperatureSupported = false;
- v4l2RangedFocus = false;
- v4l2FlashSupported = false;
- v4l2TorchSupported = false;
+ m_v4l2Info = {};
QCamera::Features features;
-
const QByteArray deviceName = m_cameraDevice.id();
Q_ASSERT(!deviceName.isEmpty());
closeV4L2Fd();
- Q_ASSERT(!d);
- d = new QV4L2CameraBuffers;
-
- d->v4l2FileDescriptor = qt_safe_open(deviceName.constData(), O_RDWR);
- if (d->v4l2FileDescriptor == -1) {
- qWarning() << "Unable to open the camera" << deviceName
- << "for read to query the parameter info:" << qt_error_string(errno);
+ const int descriptor = qt_safe_open(deviceName.constData(), O_RDWR);
+ if (descriptor == -1) {
+ qCWarning(qLcV4L2Camera) << "Unable to open the camera" << deviceName
+ << "for read to query the parameter info:"
+ << qt_error_string(errno);
+ emit error(QCamera::CameraError, QLatin1String("Cannot open camera"));
return;
}
- qDebug() << "FD=" << d->v4l2FileDescriptor;
+
+ m_v4l2FileDescriptor = std::make_shared<QV4L2FileDescriptor>(descriptor);
+
+ qCDebug(qLcV4L2Camera) << "FD=" << descriptor;
struct v4l2_queryctrl queryControl;
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2AutoWhiteBalanceSupported = true;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.autoWhiteBalanceSupported = true;
setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, true);
}
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2MinColorTemp = queryControl.minimum;
- v4l2MaxColorTemp = queryControl.maximum;
- v4l2ColorTemperatureSupported = true;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.minColorTemp = queryControl.minimum;
+ m_v4l2Info.maxColorTemp = queryControl.maximum;
+ m_v4l2Info.colorTemperatureSupported = true;
features |= QCamera::Feature::ColorTemperature;
}
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_EXPOSURE_AUTO;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2AutoExposureSupported = true;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.autoExposureSupported = true;
}
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2ManualExposureSupported = true;
- v4l2MinExposure = queryControl.minimum;
- v4l2MaxExposure = queryControl.maximum;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.manualExposureSupported = true;
+ m_v4l2Info.minExposure = queryControl.minimum;
+ m_v4l2Info.maxExposure = queryControl.maximum;
features |= QCamera::Feature::ManualExposureTime;
}
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2MinExposureAdjustment = queryControl.minimum;
- v4l2MaxExposureAdjustment = queryControl.maximum;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.minExposureAdjustment = queryControl.minimum;
+ m_v4l2Info.maxExposureAdjustment = queryControl.maximum;
features |= QCamera::Feature::ExposureCompensation;
}
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
queryControl.id = V4L2_CID_ISO_SENSITIVITY;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
features |= QCamera::Feature::IsoSensitivity;
minIsoChanged(queryControl.minimum);
maxIsoChanged(queryControl.minimum);
@@ -729,50 +474,48 @@ void QV4L2Camera::initV4L2Controls()
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_FOCUS_ABSOLUTE;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2MinExposureAdjustment = queryControl.minimum;
- v4l2MaxExposureAdjustment = queryControl.maximum;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.minExposureAdjustment = queryControl.minimum;
+ m_v4l2Info.maxExposureAdjustment = queryControl.maximum;
features |= QCamera::Feature::FocusDistance;
}
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_AUTO_FOCUS_RANGE;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2RangedFocus = true;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.rangedFocus = true;
}
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_FLASH_LED_MODE;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2FlashSupported = queryControl.minimum <= V4L2_FLASH_LED_MODE_FLASH && queryControl.maximum >= V4L2_FLASH_LED_MODE_FLASH;
- v4l2TorchSupported = queryControl.minimum <= V4L2_FLASH_LED_MODE_TORCH && queryControl.maximum >= V4L2_FLASH_LED_MODE_TORCH;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.flashSupported = queryControl.minimum <= V4L2_FLASH_LED_MODE_FLASH
+ && queryControl.maximum >= V4L2_FLASH_LED_MODE_FLASH;
+ m_v4l2Info.torchSupported = queryControl.minimum <= V4L2_FLASH_LED_MODE_TORCH
+ && queryControl.maximum >= V4L2_FLASH_LED_MODE_TORCH;
}
- v4l2MinZoom = 0;
- v4l2MaxZoom = 0;
::memset(&queryControl, 0, sizeof(queryControl));
queryControl.id = V4L2_CID_ZOOM_ABSOLUTE;
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2MinZoom = queryControl.minimum;
- v4l2MaxZoom = queryControl.maximum;
+ if (m_v4l2FileDescriptor->call(VIDIOC_QUERYCTRL, &queryControl)) {
+ m_v4l2Info.minZoom = queryControl.minimum;
+ m_v4l2Info.maxZoom = queryControl.maximum;
}
// zoom factors are in arbitrary units, so we simply normalize them to go from 1 to 2
// if they are different
minimumZoomFactorChanged(1);
- maximumZoomFactorChanged(v4l2MinZoom != v4l2MaxZoom ? 2 : 1);
+ maximumZoomFactorChanged(m_v4l2Info.minZoom != m_v4l2Info.maxZoom ? 2 : 1);
supportedFeaturesChanged(features);
}
void QV4L2Camera::closeV4L2Fd()
{
- if (d && d->v4l2FileDescriptor >= 0) {
- QMutexLocker locker(&d->mutex);
- d->unmapBuffers();
- qt_safe_close(d->v4l2FileDescriptor);
- d->v4l2FileDescriptor = -1;
- }
- d = nullptr;
+ Q_ASSERT(!m_memoryTransfer);
+
+ m_v4l2Info = {};
+ m_cameraBusy = false;
+ m_v4l2FileDescriptor = nullptr;
}
int QV4L2Camera::setV4L2ColorTemperature(int temperature)
@@ -780,15 +523,17 @@ int QV4L2Camera::setV4L2ColorTemperature(int temperature)
struct v4l2_control control;
::memset(&control, 0, sizeof(control));
- if (v4l2AutoWhiteBalanceSupported) {
+ if (m_v4l2Info.autoWhiteBalanceSupported) {
setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ? true : false);
} else if (temperature == 0) {
temperature = 5600;
}
- if (temperature != 0 && v4l2ColorTemperatureSupported) {
- temperature = qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp);
- if (!setV4L2Parameter(V4L2_CID_WHITE_BALANCE_TEMPERATURE, qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp)))
+ if (temperature != 0 && m_v4l2Info.colorTemperatureSupported) {
+ temperature = qBound(m_v4l2Info.minColorTemp, temperature, m_v4l2Info.maxColorTemp);
+ if (!setV4L2Parameter(
+ V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ qBound(m_v4l2Info.minColorTemp, temperature, m_v4l2Info.maxColorTemp)))
temperature = 0;
} else {
temperature = 0;
@@ -799,8 +544,8 @@ int QV4L2Camera::setV4L2ColorTemperature(int temperature)
bool QV4L2Camera::setV4L2Parameter(quint32 id, qint32 value)
{
- struct v4l2_control control{id, value};
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_S_CTRL, &control) != 0) {
+ v4l2_control control{ id, value };
+ if (!m_v4l2FileDescriptor->call(VIDIOC_S_CTRL, &control)) {
qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value << qt_error_string(errno);
return false;
}
@@ -810,7 +555,7 @@ bool QV4L2Camera::setV4L2Parameter(quint32 id, qint32 value)
int QV4L2Camera::getV4L2Parameter(quint32 id) const
{
struct v4l2_control control{id, 0};
- if (::ioctl(d->v4l2FileDescriptor, VIDIOC_G_CTRL, &control) != 0) {
+ if (!m_v4l2FileDescriptor->call(VIDIOC_G_CTRL, &control)) {
qWarning() << "Unable to get the V4L2 Parameter" << Qt::hex << id << qt_error_string(errno);
return 0;
}
@@ -819,8 +564,12 @@ int QV4L2Camera::getV4L2Parameter(quint32 id) const
void QV4L2Camera::setV4L2CameraFormat()
{
+ if (m_v4l2Info.formatInitialized || !m_v4l2FileDescriptor)
+ return;
+
Q_ASSERT(!m_cameraFormat.isNull());
- qDebug() << "XXXXX" << this << m_cameraDevice.id() << m_cameraFormat.pixelFormat() << m_cameraFormat.resolution();
+ qCDebug(qLcV4L2Camera) << "XXXXX" << this << m_cameraDevice.id() << m_cameraFormat.pixelFormat()
+ << m_cameraFormat.resolution();
v4l2_format fmt = {};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -831,9 +580,9 @@ void QV4L2Camera::setV4L2CameraFormat()
fmt.fmt.pix.pixelformat = v4l2FormatForPixelFormat(m_cameraFormat.pixelFormat());
fmt.fmt.pix.field = V4L2_FIELD_ANY;
- qDebug() << "setting camera format to" << size;
+ qCDebug(qLcV4L2Camera) << "setting camera format to" << size << fmt.fmt.pix.pixelformat;
- if (ioctl(d->v4l2FileDescriptor, VIDIOC_S_FMT, &fmt) < 0) {
+ if (!m_v4l2FileDescriptor->call(VIDIOC_S_FMT, &fmt)) {
if (errno == EBUSY) {
setCameraBusy();
return;
@@ -841,25 +590,29 @@ void QV4L2Camera::setV4L2CameraFormat()
qWarning() << "Couldn't set video format on v4l2 camera" << strerror(errno);
}
- bytesPerLine = fmt.fmt.pix.bytesperline;
+ m_v4l2Info.formatInitialized = true;
+ m_cameraBusy = false;
+
+ m_bytesPerLine = fmt.fmt.pix.bytesperline;
+ m_imageSize = std::max(fmt.fmt.pix.sizeimage, m_bytesPerLine * fmt.fmt.pix.height);
switch (v4l2_colorspace(fmt.fmt.pix.colorspace)) {
default:
case V4L2_COLORSPACE_DCI_P3:
- colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
+ m_colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
break;
case V4L2_COLORSPACE_REC709:
- colorSpace = QVideoFrameFormat::ColorSpace_BT709;
+ m_colorSpace = QVideoFrameFormat::ColorSpace_BT709;
break;
case V4L2_COLORSPACE_JPEG:
- colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
+ m_colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
break;
case V4L2_COLORSPACE_SRGB:
// ##### is this correct???
- colorSpace = QVideoFrameFormat::ColorSpace_BT601;
+ m_colorSpace = QVideoFrameFormat::ColorSpace_BT601;
break;
case V4L2_COLORSPACE_BT2020:
- colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
+ m_colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
break;
}
@@ -867,107 +620,88 @@ void QV4L2Camera::setV4L2CameraFormat()
streamParam.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
streamParam.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- int num, den;
- qt_real_to_fraction(1./m_cameraFormat.maxFrameRate(), &num, &den);
+ auto [num, den] = qRealToFraction(1./m_cameraFormat.maxFrameRate());
streamParam.parm.capture.timeperframe = { (uint)num, (uint)den };
- ioctl(d->v4l2FileDescriptor, VIDIOC_S_PARM, &streamParam);
+ m_v4l2FileDescriptor->call(VIDIOC_S_PARM, &streamParam);
- frameDuration = 1000000*streamParam.parm.capture.timeperframe.numerator
- /streamParam.parm.capture.timeperframe.denominator;
+ m_frameDuration = 1000000 * streamParam.parm.capture.timeperframe.numerator
+ / streamParam.parm.capture.timeperframe.denominator;
}
-void QV4L2Camera::initMMap()
+void QV4L2Camera::initV4L2MemoryTransfer()
{
- if (cameraBusy)
+ if (m_cameraBusy)
return;
- v4l2_requestbuffers req = {};
- req.count = 4;
- req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- req.memory = V4L2_MEMORY_MMAP;
+ Q_ASSERT(!m_memoryTransfer);
- if (ioctl(d->v4l2FileDescriptor, VIDIOC_REQBUFS, &req) < 0) {
- if (errno == EBUSY)
- setCameraBusy();
- qWarning() << "requesting mmap'ed buffers failed" << strerror(errno);
+ m_memoryTransfer = makeUserPtrMemoryTransfer(m_v4l2FileDescriptor, m_imageSize);
+
+ if (m_memoryTransfer)
return;
- }
- if (req.count < 2) {
- qWarning() << "Can't map 2 or more buffers";
+ if (errno == EBUSY) {
+ setCameraBusy();
return;
}
- for (uint32_t n = 0; n < req.count; ++n) {
- v4l2_buffer buf = {};
- buf.index = n;
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
-
- if (ioctl(d->v4l2FileDescriptor, VIDIOC_QUERYBUF, &buf) != 0) {
- qWarning() << "Can't map buffer" << n;
- return;
- }
-
- QV4L2CameraBuffers::MappedBuffer buffer;
- buffer.size = buf.length;
- buffer.data = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
- d->v4l2FileDescriptor, buf.m.offset);
+ qCDebug(qLcV4L2Camera) << "Cannot init V4L2_MEMORY_USERPTR; trying V4L2_MEMORY_MMAP";
- if (buffer.data == MAP_FAILED) {
- qWarning() << "mmap failed" << n << buf.length << buf.m.offset;
- return;
- }
+ m_memoryTransfer = makeMMapMemoryTransfer(m_v4l2FileDescriptor);
- d->mappedBuffers.append(buffer);
+ if (!m_memoryTransfer) {
+ qCWarning(qLcV4L2Camera) << "Cannot init v4l2 memory transfer," << qt_error_string(errno);
+ emit error(QCamera::CameraError, QLatin1String("Cannot init V4L2 memory transfer"));
}
-
}
void QV4L2Camera::stopCapturing()
{
- if (!d)
+ if (!m_memoryTransfer || !m_v4l2FileDescriptor)
return;
- delete notifier;
- notifier = nullptr;
+ m_notifier = nullptr;
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if (ioctl(d->v4l2FileDescriptor, VIDIOC_STREAMOFF, &type) < 0) {
+ if (!m_v4l2FileDescriptor->stopStream()) {
+ // TODO: handle the case carefully to avoid possible memory corruption
if (errno != ENODEV)
qWarning() << "failed to stop capture";
}
- cameraBusy = false;
+
+ m_memoryTransfer = nullptr;
+ m_cameraBusy = false;
}
void QV4L2Camera::startCapturing()
{
- if (cameraBusy)
+ if (!m_v4l2FileDescriptor)
return;
- // #### better to use the user data method instead of mmap???
- unsigned int i;
+ setV4L2CameraFormat();
+ initV4L2MemoryTransfer();
- for (i = 0; i < d->mappedBuffers.size(); ++i) {
- v4l2_buffer buf = {};
- buf.index = i;
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
+ if (m_cameraBusy || !m_memoryTransfer)
+ return;
- if (ioctl(d->v4l2FileDescriptor, VIDIOC_QBUF, &buf) < 0) {
- qWarning() << "failed to setup mapped buffer";
- return;
- }
+ if (!m_v4l2FileDescriptor->startStream()) {
+ qWarning() << "Couldn't start v4l2 camera stream";
+ return;
}
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (ioctl(d->v4l2FileDescriptor, VIDIOC_STREAMON, &type) < 0)
- qWarning() << "failed to start capture";
- notifier = new QSocketNotifier(d->v4l2FileDescriptor, QSocketNotifier::Read);
- connect(notifier, &QSocketNotifier::activated, this, &QV4L2Camera::readFrame);
+ m_notifier =
+ std::make_unique<QSocketNotifier>(m_v4l2FileDescriptor->get(), QSocketNotifier::Read);
+ connect(m_notifier.get(), &QSocketNotifier::activated, this, &QV4L2Camera::readFrame);
- firstFrameTime = { -1, -1 };
+ m_firstFrameTime = { -1, -1 };
+}
+
+QVideoFrameFormat QV4L2Camera::frameFormat() const
+{
+ auto result = QPlatformCamera::frameFormat();
+ result.setColorSpace(m_colorSpace);
+ return result;
}
QT_END_NAMESPACE
+
+#include "moc_qv4l2camera_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2camera_p.h b/src/plugins/multimedia/ffmpeg/qv4l2camera_p.h
index bc94df8d8..3033f5ff9 100644
--- a/src/plugins/multimedia/ffmpeg/qv4l2camera_p.h
+++ b/src/plugins/multimedia/ffmpeg/qv4l2camera_p.h
@@ -1,44 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QFFMPEGCAMERA_H
-#define QFFMPEGCAMERA_H
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QV4L2CAMERA_H
+#define QV4L2CAMERA_H
//
// W A R N I N G
@@ -52,53 +16,42 @@
//
#include <private/qplatformcamera_p.h>
-#include <private/qplatformvideodevices_p.h>
-#include <private/qplatformmediaintegration_p.h>
-
-#include <qfilesystemwatcher.h>
-#include <qsocketnotifier.h>
-#include <qmutex.h>
+#include <sys/time.h>
QT_BEGIN_NAMESPACE
-class QV4L2CameraDevices : public QObject,
- public QPlatformVideoDevices
-{
- Q_OBJECT
-public:
- QV4L2CameraDevices(QPlatformMediaIntegration *integration);
-
- QList<QCameraDevice> videoDevices() const override;
-
-public Q_SLOTS:
- void checkCameras();
+class QV4L2FileDescriptor;
+class QV4L2MemoryTransfer;
+class QSocketNotifier;
-private:
- void doCheckCameras();
-
- QList<QCameraDevice> cameras;
- QFileSystemWatcher deviceWatcher;
-};
-
-struct QV4L2CameraBuffers
+struct V4L2CameraInfo
{
-public:
- ~QV4L2CameraBuffers();
-
- void release(int index);
- void unmapBuffers();
-
- QAtomicInt ref;
- QMutex mutex;
- struct MappedBuffer {
- void *data;
- qsizetype size;
- };
- QList<MappedBuffer> mappedBuffers;
- int v4l2FileDescriptor = -1;
+ bool formatInitialized = false;
+
+ bool autoWhiteBalanceSupported = false;
+ bool colorTemperatureSupported = false;
+ bool autoExposureSupported = false;
+ bool manualExposureSupported = false;
+ bool flashSupported = false;
+ bool torchSupported = false;
+ qint32 minColorTemp = 5600; // Daylight...
+ qint32 maxColorTemp = 5600;
+ qint32 minExposure = 0;
+ qint32 maxExposure = 0;
+ qint32 minExposureAdjustment = 0;
+ qint32 maxExposureAdjustment = 0;
+ qint32 minFocus = 0;
+ qint32 maxFocus = 0;
+ qint32 rangedFocus = false;
+
+ int minZoom = 0;
+ int maxZoom = 0;
};
-class Q_MULTIMEDIA_EXPORT QV4L2Camera : public QPlatformCamera
+QVideoFrameFormat::PixelFormat formatForV4L2Format(uint32_t v4l2Format);
+uint32_t v4l2FormatForPixelFormat(QVideoFrameFormat::PixelFormat format);
+
+class QV4L2Camera : public QPlatformCamera
{
Q_OBJECT
@@ -139,18 +92,13 @@ public:
void setWhiteBalanceMode(QCamera::WhiteBalanceMode /*mode*/) override;
void setColorTemperature(int /*temperature*/) override;
- void releaseBuffer(int index);
+ QVideoFrameFormat frameFormat() const override;
private Q_SLOTS:
void readFrame();
private:
void setCameraBusy();
-
- bool m_active = false;
-
- QCameraDevice m_cameraDevice;
-
void initV4L2Controls();
void closeV4L2Fd();
int setV4L2ColorTemperature(int temperature);
@@ -158,39 +106,28 @@ private:
int getV4L2Parameter(quint32 id) const;
void setV4L2CameraFormat();
- void initMMap();
+ void initV4L2MemoryTransfer();
void startCapturing();
void stopCapturing();
- QSocketNotifier *notifier = nullptr;
- QExplicitlySharedDataPointer<QV4L2CameraBuffers> d;
-
- bool v4l2AutoWhiteBalanceSupported = false;
- bool v4l2ColorTemperatureSupported = false;
- bool v4l2AutoExposureSupported = false;
- bool v4l2ManualExposureSupported = false;
- qint32 v4l2MinColorTemp = 5600; // Daylight...
- qint32 v4l2MaxColorTemp = 5600;
- qint32 v4l2MinExposure = 0;
- qint32 v4l2MaxExposure = 0;
- qint32 v4l2MinExposureAdjustment = 0;
- qint32 v4l2MaxExposureAdjustment = 0;
- qint32 v4l2MinFocus = 0;
- qint32 v4l2MaxFocus = 0;
- qint32 v4l2RangedFocus = false;
- bool v4l2FlashSupported = false;
- bool v4l2TorchSupported = false;
- int v4l2MinZoom = 0;
- int v4l2MaxZoom = 0;
- timeval firstFrameTime = {-1, -1};
- int bytesPerLine = -1;
- QVideoFrameFormat::ColorSpace colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
- qint64 frameDuration = -1;
- bool cameraBusy = false;
-};
+private:
+ bool m_active = false;
+ QCameraDevice m_cameraDevice;
-QT_END_NAMESPACE
+ std::unique_ptr<QSocketNotifier> m_notifier;
+ std::unique_ptr<QV4L2MemoryTransfer> m_memoryTransfer;
+ std::shared_ptr<QV4L2FileDescriptor> m_v4l2FileDescriptor;
+ V4L2CameraInfo m_v4l2Info;
-#endif // QFFMPEGCAMERA_H
+ timeval m_firstFrameTime = { -1, -1 };
+ quint32 m_bytesPerLine = 0;
+ quint32 m_imageSize = 0;
+ QVideoFrameFormat::ColorSpace m_colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
+ qint64 m_frameDuration = -1;
+ bool m_cameraBusy = false;
+};
+
+QT_END_NAMESPACE
+#endif // QV4L2CAMERA_H
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2cameradevices.cpp b/src/plugins/multimedia/ffmpeg/qv4l2cameradevices.cpp
new file mode 100644
index 000000000..e450cf7bc
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qv4l2cameradevices.cpp
@@ -0,0 +1,182 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qv4l2cameradevices_p.h"
+#include "qv4l2filedescriptor_p.h"
+#include "qv4l2camera_p.h"
+
+#include <private/qcameradevice_p.h>
+#include <private/qcore_unix_p.h>
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qdebug.h>
+#include <qloggingcategory.h>
+
+#include <linux/videodev2.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcV4L2CameraDevices, "qt.multimedia.ffmpeg.v4l2cameradevices");
+
+static bool areCamerasEqual(QList<QCameraDevice> a, QList<QCameraDevice> b)
+{
+ auto areCamerasDataEqual = [](const QCameraDevice &a, const QCameraDevice &b) {
+ Q_ASSERT(QCameraDevicePrivate::handle(a));
+ Q_ASSERT(QCameraDevicePrivate::handle(b));
+ return *QCameraDevicePrivate::handle(a) == *QCameraDevicePrivate::handle(b);
+ };
+
+ return std::equal(a.cbegin(), a.cend(), b.cbegin(), b.cend(), areCamerasDataEqual);
+}
+
+QV4L2CameraDevices::QV4L2CameraDevices(QPlatformMediaIntegration *integration)
+ : QPlatformVideoDevices(integration)
+{
+ m_deviceWatcher.addPath(QLatin1String("/dev"));
+ connect(&m_deviceWatcher, &QFileSystemWatcher::directoryChanged, this,
+ &QV4L2CameraDevices::checkCameras);
+ doCheckCameras();
+}
+
+QList<QCameraDevice> QV4L2CameraDevices::videoDevices() const
+{
+ return m_cameras;
+}
+
+void QV4L2CameraDevices::checkCameras()
+{
+ if (doCheckCameras())
+ emit videoInputsChanged();
+}
+
+bool QV4L2CameraDevices::doCheckCameras()
+{
+ QList<QCameraDevice> newCameras;
+
+ QDir dir(QLatin1String("/dev"));
+ const auto devices = dir.entryList(QDir::System);
+
+ bool first = true;
+
+ for (auto device : devices) {
+ // qCDebug(qLcV4L2Camera) << "device:" << device;
+ if (!device.startsWith(QLatin1String("video")))
+ continue;
+
+ QByteArray file = QFile::encodeName(dir.filePath(device));
+ const int fd = open(file.constData(), O_RDONLY);
+ if (fd < 0)
+ continue;
+
+ auto fileCloseGuard = qScopeGuard([fd]() { close(fd); });
+
+ v4l2_fmtdesc formatDesc = {};
+
+ struct v4l2_capability cap;
+ if (xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
+ continue;
+
+ if (cap.device_caps & V4L2_CAP_META_CAPTURE)
+ continue;
+ if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+ continue;
+ if (!(cap.capabilities & V4L2_CAP_STREAMING))
+ continue;
+
+ auto camera = std::make_unique<QCameraDevicePrivate>();
+
+ camera->id = file;
+ camera->description = QString::fromUtf8((const char *)cap.card);
+ qCDebug(qLcV4L2CameraDevices) << "found camera" << camera->id << camera->description;
+
+ formatDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ while (!xioctl(fd, VIDIOC_ENUM_FMT, &formatDesc)) {
+ auto pixelFmt = formatForV4L2Format(formatDesc.pixelformat);
+ qCDebug(qLcV4L2CameraDevices) << " " << pixelFmt;
+
+ if (pixelFmt == QVideoFrameFormat::Format_Invalid) {
+ ++formatDesc.index;
+ continue;
+ }
+
+ qCDebug(qLcV4L2CameraDevices) << "frame sizes:";
+ v4l2_frmsizeenum frameSize = {};
+ frameSize.pixel_format = formatDesc.pixelformat;
+
+ while (!xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) {
+ QList<QSize> resolutions;
+ if (frameSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ resolutions.append(QSize(frameSize.discrete.width,
+ frameSize.discrete.height));
+ } else {
+ resolutions.append(QSize(frameSize.stepwise.max_width,
+ frameSize.stepwise.max_height));
+ resolutions.append(QSize(frameSize.stepwise.min_width,
+ frameSize.stepwise.min_height));
+ }
+
+ for (auto resolution : resolutions) {
+ float min = 1e10;
+ float max = 0;
+ auto updateMaxMinFrameRate = [&max, &min](auto discreteFrameRate) {
+ const float rate = float(discreteFrameRate.denominator)
+ / float(discreteFrameRate.numerator);
+ if (rate > max)
+ max = rate;
+ if (rate < min)
+ min = rate;
+ };
+
+ v4l2_frmivalenum frameInterval = {};
+ frameInterval.pixel_format = formatDesc.pixelformat;
+ frameInterval.width = resolution.width();
+ frameInterval.height = resolution.height();
+
+ while (!xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameInterval)) {
+ if (frameInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
+ updateMaxMinFrameRate(frameInterval.discrete);
+ } else {
+ updateMaxMinFrameRate(frameInterval.stepwise.max);
+ updateMaxMinFrameRate(frameInterval.stepwise.min);
+ }
+ ++frameInterval.index;
+ }
+
+ qCDebug(qLcV4L2CameraDevices) << " " << resolution << min << max;
+
+ if (min <= max) {
+ auto fmt = std::make_unique<QCameraFormatPrivate>();
+ fmt->pixelFormat = pixelFmt;
+ fmt->resolution = resolution;
+ fmt->minFrameRate = min;
+ fmt->maxFrameRate = max;
+ camera->videoFormats.append(fmt.release()->create());
+ camera->photoResolutions.append(resolution);
+ }
+ }
+ ++frameSize.index;
+ }
+ ++formatDesc.index;
+ }
+
+ if (camera->videoFormats.empty())
+ continue;
+
+ // first camera is default
+ camera->isDefault = std::exchange(first, false);
+
+ newCameras.append(camera.release()->create());
+ }
+
+ if (areCamerasEqual(m_cameras, newCameras))
+ return false;
+
+ m_cameras = std::move(newCameras);
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qv4l2cameradevices_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2cameradevices_p.h b/src/plugins/multimedia/ffmpeg/qv4l2cameradevices_p.h
new file mode 100644
index 000000000..ce424d3b6
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qv4l2cameradevices_p.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QV4L2CAMERADEVICES_P_H
+#define QV4L2CAMERADEVICES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformvideodevices_p.h>
+#include <private/qplatformmediaintegration_p.h>
+
+#include <qfilesystemwatcher.h>
+
+QT_BEGIN_NAMESPACE
+
+class QV4L2CameraDevices : public QPlatformVideoDevices
+{
+ Q_OBJECT
+public:
+ QV4L2CameraDevices(QPlatformMediaIntegration *integration);
+
+ QList<QCameraDevice> videoDevices() const override;
+
+public Q_SLOTS:
+ void checkCameras();
+
+private:
+ bool doCheckCameras();
+
+private:
+ QList<QCameraDevice> m_cameras;
+ QFileSystemWatcher m_deviceWatcher;
+};
+
+QT_END_NAMESPACE
+
+#endif // QV4L2CAMERADEVICES_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2filedescriptor.cpp b/src/plugins/multimedia/ffmpeg/qv4l2filedescriptor.cpp
new file mode 100644
index 000000000..7f7b099c7
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qv4l2filedescriptor.cpp
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qv4l2filedescriptor_p.h"
+
+#include <sys/ioctl.h>
+#include <private/qcore_unix_p.h>
+
+#include <linux/videodev2.h>
+
+QT_BEGIN_NAMESPACE
+
+int xioctl(int fd, int request, void *arg)
+{
+ int res;
+
+ do {
+ res = ::ioctl(fd, request, arg);
+ } while (res == -1 && EINTR == errno);
+
+ return res;
+}
+
+QV4L2FileDescriptor::QV4L2FileDescriptor(int descriptor) : m_descriptor(descriptor)
+{
+ Q_ASSERT(descriptor >= 0);
+}
+
+QV4L2FileDescriptor::~QV4L2FileDescriptor()
+{
+ qt_safe_close(m_descriptor);
+}
+
+bool QV4L2FileDescriptor::call(int request, void *arg) const
+{
+ return ::xioctl(m_descriptor, request, arg) >= 0;
+}
+
+bool QV4L2FileDescriptor::requestBuffers(quint32 memoryType, quint32 &buffersCount) const
+{
+ v4l2_requestbuffers req = {};
+ req.count = buffersCount;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = memoryType;
+
+ if (!call(VIDIOC_REQBUFS, &req))
+ return false;
+
+ buffersCount = req.count;
+ return true;
+}
+
+bool QV4L2FileDescriptor::startStream()
+{
+ int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (!call(VIDIOC_STREAMON, &type))
+ return false;
+
+ m_streamStarted = true;
+ return true;
+}
+
+bool QV4L2FileDescriptor::stopStream()
+{
+ int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ auto result = call(VIDIOC_STREAMOFF, &type);
+ m_streamStarted = false;
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2filedescriptor_p.h b/src/plugins/multimedia/ffmpeg/qv4l2filedescriptor_p.h
new file mode 100644
index 000000000..1058c7a82
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qv4l2filedescriptor_p.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QV4L2FILEDESCRIPTOR_P_H
+#define QV4L2FILEDESCRIPTOR_P_H
+
+#include <private/qtmultimediaglobal_p.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+int xioctl(int fd, int request, void *arg);
+
+class QV4L2FileDescriptor
+{
+public:
+ QV4L2FileDescriptor(int descriptor);
+
+ ~QV4L2FileDescriptor();
+
+ bool call(int request, void *arg) const;
+
+ int get() const { return m_descriptor; }
+
+ bool requestBuffers(quint32 memoryType, quint32 &buffersCount) const;
+
+ bool startStream();
+
+ bool stopStream();
+
+ bool streamStarted() const { return m_streamStarted; }
+
+private:
+ int m_descriptor;
+ bool m_streamStarted = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QV4L2FILEDESCRIPTOR_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2memorytransfer.cpp b/src/plugins/multimedia/ffmpeg/qv4l2memorytransfer.cpp
new file mode 100644
index 000000000..32ee4f8f8
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qv4l2memorytransfer.cpp
@@ -0,0 +1,223 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qv4l2memorytransfer_p.h"
+#include "qv4l2filedescriptor_p.h"
+
+#include <qloggingcategory.h>
+#include <qdebug.h>
+#include <sys/mman.h>
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcV4L2MemoryTransfer, "qt.multimedia.ffmpeg.v4l2camera.memorytransfer");
+
+namespace {
+
+v4l2_buffer makeV4l2Buffer(quint32 memoryType, quint32 index = 0)
+{
+ v4l2_buffer buf = {};
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = memoryType;
+ buf.index = index;
+ return buf;
+}
+
+class UserPtrMemoryTransfer : public QV4L2MemoryTransfer
+{
+public:
+ static QV4L2MemoryTransferUPtr create(QV4L2FileDescriptorPtr fileDescriptor, quint32 imageSize)
+ {
+ quint32 buffersCount = 2;
+ if (!fileDescriptor->requestBuffers(V4L2_MEMORY_USERPTR, buffersCount)) {
+ qCWarning(qLcV4L2MemoryTransfer) << "Cannot request V4L2_MEMORY_USERPTR buffers";
+ return {};
+ }
+
+ std::unique_ptr<UserPtrMemoryTransfer> result(
+ new UserPtrMemoryTransfer(std::move(fileDescriptor), buffersCount, imageSize));
+
+ return result->enqueueBuffers() ? std::move(result) : nullptr;
+ }
+
+ std::optional<Buffer> dequeueBuffer() override
+ {
+ auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_USERPTR);
+ if (!fileDescriptor().call(VIDIOC_DQBUF, &v4l2Buffer))
+ return {};
+
+ Q_ASSERT(v4l2Buffer.index < m_byteArrays.size());
+ Q_ASSERT(!m_byteArrays[v4l2Buffer.index].isEmpty());
+
+ return Buffer{ v4l2Buffer, std::move(m_byteArrays[v4l2Buffer.index]) };
+ }
+
+ bool enqueueBuffer(quint32 index) override
+ {
+ Q_ASSERT(index < m_byteArrays.size());
+ Q_ASSERT(m_byteArrays[index].isEmpty());
+
+ auto buf = makeV4l2Buffer(V4L2_MEMORY_USERPTR, index);
+ static_assert(sizeof(decltype(buf.m.userptr)) == sizeof(size_t), "Not compatible sizes");
+
+ m_byteArrays[index] = QByteArray(static_cast<int>(m_imageSize), Qt::Uninitialized);
+
+ buf.m.userptr = (decltype(buf.m.userptr))m_byteArrays[index].data();
+ buf.length = m_byteArrays[index].size();
+
+ if (!fileDescriptor().call(VIDIOC_QBUF, &buf)) {
+ qWarning() << "Couldn't add V4L2 buffer" << errno << strerror(errno) << index;
+ return false;
+ }
+
+ return true;
+ }
+
+ quint32 buffersCount() const override { return static_cast<quint32>(m_byteArrays.size()); }
+
+private:
+ UserPtrMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor, quint32 buffersCount,
+ quint32 imageSize)
+ : QV4L2MemoryTransfer(std::move(fileDescriptor)),
+ m_imageSize(imageSize),
+ m_byteArrays(buffersCount)
+ {
+ }
+
+private:
+ quint32 m_imageSize;
+ std::vector<QByteArray> m_byteArrays;
+};
+
+class MMapMemoryTransfer : public QV4L2MemoryTransfer
+{
+public:
+ struct MemorySpan
+ {
+ void *data = nullptr;
+ size_t size = 0;
+ bool inQueue = false;
+ };
+
+ static QV4L2MemoryTransferUPtr create(QV4L2FileDescriptorPtr fileDescriptor)
+ {
+ quint32 buffersCount = 2;
+ if (!fileDescriptor->requestBuffers(V4L2_MEMORY_MMAP, buffersCount)) {
+ qCWarning(qLcV4L2MemoryTransfer) << "Cannot request V4L2_MEMORY_MMAP buffers";
+ return {};
+ }
+
+ std::unique_ptr<MMapMemoryTransfer> result(
+ new MMapMemoryTransfer(std::move(fileDescriptor)));
+
+ return result->init(buffersCount) ? std::move(result) : nullptr;
+ }
+
+ bool init(quint32 buffersCount)
+ {
+ for (quint32 index = 0; index < buffersCount; ++index) {
+ auto buf = makeV4l2Buffer(V4L2_MEMORY_MMAP, index);
+
+ if (!fileDescriptor().call(VIDIOC_QUERYBUF, &buf)) {
+ qWarning() << "Can't map buffer" << index;
+ return false;
+ }
+
+ auto mappedData = mmap(nullptr, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fileDescriptor().get(), buf.m.offset);
+
+ if (mappedData == MAP_FAILED) {
+ qWarning() << "mmap failed" << index << buf.length << buf.m.offset;
+ return false;
+ }
+
+ m_spans.push_back(MemorySpan{ mappedData, buf.length, false });
+ }
+
+ m_spans.shrink_to_fit();
+
+ return enqueueBuffers();
+ }
+
+ ~MMapMemoryTransfer() override
+ {
+ for (const auto &span : m_spans)
+ munmap(span.data, span.size);
+ }
+
+ std::optional<Buffer> dequeueBuffer() override
+ {
+ auto v4l2Buffer = makeV4l2Buffer(V4L2_MEMORY_MMAP);
+ if (!fileDescriptor().call(VIDIOC_DQBUF, &v4l2Buffer))
+ return {};
+
+ const auto index = v4l2Buffer.index;
+
+ Q_ASSERT(index < m_spans.size());
+
+ auto &span = m_spans[index];
+
+ Q_ASSERT(span.inQueue);
+ span.inQueue = false;
+
+ return Buffer{ v4l2Buffer,
+ QByteArray(reinterpret_cast<const char *>(span.data), span.size) };
+ }
+
+ bool enqueueBuffer(quint32 index) override
+ {
+ Q_ASSERT(index < m_spans.size());
+ Q_ASSERT(!m_spans[index].inQueue);
+
+ auto buf = makeV4l2Buffer(V4L2_MEMORY_MMAP, index);
+ if (!fileDescriptor().call(VIDIOC_QBUF, &buf))
+ return false;
+
+ m_spans[index].inQueue = true;
+ return true;
+ }
+
+ quint32 buffersCount() const override { return static_cast<quint32>(m_spans.size()); }
+
+private:
+ using QV4L2MemoryTransfer::QV4L2MemoryTransfer;
+
+private:
+ std::vector<MemorySpan> m_spans;
+};
+} // namespace
+
+QV4L2MemoryTransfer::QV4L2MemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor)
+ : m_fileDescriptor(std::move(fileDescriptor))
+{
+ Q_ASSERT(m_fileDescriptor);
+ Q_ASSERT(!m_fileDescriptor->streamStarted());
+}
+
+QV4L2MemoryTransfer::~QV4L2MemoryTransfer()
+{
+ Q_ASSERT(!m_fileDescriptor->streamStarted()); // to avoid possible corruptions
+}
+
+bool QV4L2MemoryTransfer::enqueueBuffers()
+{
+ for (quint32 i = 0; i < buffersCount(); ++i)
+ if (!enqueueBuffer(i))
+ return false;
+
+ return true;
+}
+
+QV4L2MemoryTransferUPtr makeUserPtrMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor,
+ quint32 imageSize)
+{
+ return UserPtrMemoryTransfer::create(std::move(fileDescriptor), imageSize);
+}
+
+QV4L2MemoryTransferUPtr makeMMapMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor)
+{
+ return MMapMemoryTransfer::create(std::move(fileDescriptor));
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qv4l2memorytransfer_p.h b/src/plugins/multimedia/ffmpeg/qv4l2memorytransfer_p.h
new file mode 100644
index 000000000..6b5e3913f
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qv4l2memorytransfer_p.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QV4L2MEMORYTRANSFER_P_H
+#define QV4L2MEMORYTRANSFER_P_H
+
+#include <private/qtmultimediaglobal_p.h>
+#include <qbytearray.h>
+#include <linux/videodev2.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+class QV4L2FileDescriptor;
+using QV4L2FileDescriptorPtr = std::shared_ptr<QV4L2FileDescriptor>;
+
+class QV4L2MemoryTransfer
+{
+public:
+ struct Buffer
+ {
+ v4l2_buffer v4l2Buffer = {};
+ QByteArray data;
+ };
+
+ QV4L2MemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor);
+
+ virtual ~QV4L2MemoryTransfer();
+
+ virtual std::optional<Buffer> dequeueBuffer() = 0;
+
+ virtual bool enqueueBuffer(quint32 index) = 0;
+
+ virtual quint32 buffersCount() const = 0;
+
+protected:
+ bool enqueueBuffers();
+
+ const QV4L2FileDescriptor &fileDescriptor() const { return *m_fileDescriptor; }
+
+private:
+ QV4L2FileDescriptorPtr m_fileDescriptor;
+};
+
+using QV4L2MemoryTransferUPtr = std::unique_ptr<QV4L2MemoryTransfer>;
+
+QV4L2MemoryTransferUPtr makeUserPtrMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor,
+ quint32 imageSize);
+
+QV4L2MemoryTransferUPtr makeMMapMemoryTransfer(QV4L2FileDescriptorPtr fileDescriptor);
+
+QT_END_NAMESPACE
+
+#endif // QV4L2MEMORYTRANSFER_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qwincapturablewindows.cpp b/src/plugins/multimedia/ffmpeg/qwincapturablewindows.cpp
new file mode 100644
index 000000000..aac77aec4
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qwincapturablewindows.cpp
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwincapturablewindows_p.h"
+#include "private/qcapturablewindow_p.h"
+
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+static bool isTopLevelWindow(HWND hwnd)
+{
+ return hwnd && ::GetAncestor(hwnd, GA_ROOT) == hwnd;
+}
+
+static bool canCaptureWindow(HWND hwnd)
+{
+ Q_ASSERT(hwnd);
+
+ if (!::IsWindowVisible(hwnd))
+ return false;
+
+ RECT rect{};
+ if (!::GetWindowRect(hwnd, &rect))
+ return false;
+
+ if (rect.left >= rect.right || rect.top >= rect.bottom)
+ return false;
+
+ return true;
+}
+
+static QString windowTitle(HWND hwnd) {
+ // QTBUG-114890
+ // TODO: investigate the case when hwnd is inner and belows to another thread.
+ // It might causes deadlocks in specific cases.
+ auto titleLength = ::GetWindowTextLengthW(hwnd);
+ std::wstring buffer(titleLength + 1, L'\0');
+ titleLength = ::GetWindowTextW(hwnd, buffer.data(), titleLength + 1);
+ buffer.resize(titleLength);
+
+ return QString::fromStdWString(buffer);
+}
+
+QList<QCapturableWindow> QWinCapturableWindows::windows() const
+{
+ QList<QCapturableWindow> result;
+
+ auto windowHandler = [](HWND hwnd, LPARAM lParam) {
+ if (!canCaptureWindow(hwnd))
+ return TRUE; // Ignore window and continue enumerating
+
+ auto& windows = *reinterpret_cast<QList<QCapturableWindow>*>(lParam);
+
+ auto windowData = std::make_unique<QCapturableWindowPrivate>();
+ windowData->id = reinterpret_cast<QCapturableWindowPrivate::Id>(hwnd);
+ windowData->description = windowTitle(hwnd);
+ windows.push_back(windowData.release()->create());
+
+ return TRUE;
+ };
+
+ ::EnumWindows(windowHandler, reinterpret_cast<LPARAM>(&result));
+
+ return result;
+}
+
+bool QWinCapturableWindows::isWindowValid(const QCapturableWindowPrivate &window) const
+{
+ const auto hwnd = reinterpret_cast<HWND>(window.id);
+ return isTopLevelWindow(hwnd) && canCaptureWindow(hwnd);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qwincapturablewindows_p.h b/src/plugins/multimedia/ffmpeg/qwincapturablewindows_p.h
new file mode 100644
index 000000000..1e38708ef
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qwincapturablewindows_p.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWINCAPTURABLEWINDOWS_P_H
+#define QWINCAPTURABLEWINDOWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qplatformcapturablewindows_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWinCapturableWindows : public QPlatformCapturableWindows
+{
+public:
+ QList<QCapturableWindow> windows() const override;
+
+ bool isWindowValid(const QCapturableWindowPrivate &window) const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINCAPTURABLEWINDOWS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp b/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp
index b6e031633..39aac3527 100644
--- a/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp
+++ b/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowscamera_p.h"
#include "qsemaphore.h"
@@ -43,53 +7,23 @@
#include <private/qmemoryvideobuffer_p.h>
#include <private/qwindowsmfdefs_p.h>
+#include <private/qwindowsmultimediautils_p.h>
+#include <private/qcomobject_p.h>
#include <mfapi.h>
#include <mfidl.h>
-#include <Mferror.h>
-#include <Mfreadwrite.h>
+#include <mferror.h>
+#include <mfreadwrite.h>
#include <system_error>
QT_BEGIN_NAMESPACE
-class CameraReaderCallback : public IMFSourceReaderCallback
+using namespace QWindowsMultimediaUtils;
+
+class CameraReaderCallback : public QComObject<IMFSourceReaderCallback>
{
public:
- CameraReaderCallback() : m_cRef(1) {}
- virtual ~CameraReaderCallback() {}
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override
- {
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFSourceReaderCallback) {
- *ppvObject = static_cast<IMFSourceReaderCallback*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(static_cast<IMFSourceReaderCallback*>(this));
- } else {
- *ppvObject = nullptr;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
- }
-
- STDMETHODIMP_(ULONG) AddRef() override
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release() override
- {
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0) {
- delete this;
- }
- return cRef;
- }
-
//from IMFSourceReaderCallback
STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample) override;
STDMETHODIMP OnFlush(DWORD) override;
@@ -101,22 +35,24 @@ public:
m_activeCamera = activeCamera;
}
private:
- LONG m_cRef;
+ // Destructor is not public. Caller should call Release.
+ ~CameraReaderCallback() override = default;
+
ActiveCamera *m_activeCamera = nullptr;
QMutex m_mutex;
};
-static QWindowsIUPointer<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource,
- const QWindowsIUPointer<CameraReaderCallback> &callback)
+static ComPtr<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource,
+ const ComPtr<CameraReaderCallback> &callback)
{
- QWindowsIUPointer<IMFSourceReader> sourceReader;
- QWindowsIUPointer<IMFAttributes> readerAttributes;
+ ComPtr<IMFSourceReader> sourceReader;
+ ComPtr<IMFAttributes> readerAttributes;
- HRESULT hr = MFCreateAttributes(readerAttributes.address(), 1);
+ HRESULT hr = MFCreateAttributes(readerAttributes.GetAddressOf(), 1);
if (SUCCEEDED(hr)) {
- hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.get());
+ hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.Get());
if (SUCCEEDED(hr)) {
- hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.get(), sourceReader.address());
+ hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.Get(), sourceReader.GetAddressOf());
if (SUCCEEDED(hr))
return sourceReader;
}
@@ -126,18 +62,18 @@ static QWindowsIUPointer<IMFSourceReader> createCameraReader(IMFMediaSource *med
return sourceReader;
}
-static QWindowsIUPointer<IMFMediaSource> createCameraSource(const QString &deviceId)
+static ComPtr<IMFMediaSource> createCameraSource(const QString &deviceId)
{
- QWindowsIUPointer<IMFMediaSource> mediaSource;
- QWindowsIUPointer<IMFAttributes> sourceAttributes;
- HRESULT hr = MFCreateAttributes(sourceAttributes.address(), 2);
+ ComPtr<IMFMediaSource> mediaSource;
+ ComPtr<IMFAttributes> sourceAttributes;
+ HRESULT hr = MFCreateAttributes(sourceAttributes.GetAddressOf(), 2);
if (SUCCEEDED(hr)) {
hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (SUCCEEDED(hr)) {
hr = sourceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
reinterpret_cast<LPCWSTR>(deviceId.utf16()));
if (SUCCEEDED(hr)) {
- hr = MFCreateDeviceSource(sourceAttributes.get(), mediaSource.address());
+ hr = MFCreateDeviceSource(sourceAttributes.Get(), mediaSource.GetAddressOf());
if (SUCCEEDED(hr))
return mediaSource;
}
@@ -147,53 +83,68 @@ static QWindowsIUPointer<IMFMediaSource> createCameraSource(const QString &devic
return mediaSource;
}
-static int calculateVideoFrameStride(IMFSourceReader *sourceReader, qsizetype formatIndex, int width)
+static int calculateVideoFrameStride(IMFMediaType *videoType, int width)
{
- Q_ASSERT(sourceReader);
- QWindowsIUPointer<IMFMediaType> videoType;
- HRESULT hr = sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
- formatIndex, videoType.address());
+ Q_ASSERT(videoType);
+
+ GUID subtype = GUID_NULL;
+ HRESULT hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (SUCCEEDED(hr)) {
- GUID subtype = GUID_NULL;
- hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype);
- if (SUCCEEDED(hr)) {
- LONG stride = 0;
- hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride);
- if (SUCCEEDED(hr))
- return int(stride);
- }
+ LONG stride = 0;
+ hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride);
+ if (SUCCEEDED(hr))
+ return int(qAbs(stride));
}
- qWarning() << "Failed to calculate video stride" << hr;
+ qWarning() << "Failed to calculate video stride" << errorString(hr);
return 0;
}
-static bool setCameraReaderFormat(IMFSourceReader *sourceReader, qsizetype formatIndex)
+static bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType)
{
Q_ASSERT(sourceReader);
+ Q_ASSERT(videoType);
- QWindowsIUPointer<IMFMediaType> videoType;
- HRESULT hr = sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
- formatIndex, videoType.address());
- if (SUCCEEDED(hr)) {
- hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
- nullptr, videoType.get());
- if (SUCCEEDED(hr)) {
- return true;
- } else {
- qWarning() << "Failed to set video format" << hr << std::system_category().message(hr).c_str();
- }
- } else {
- qWarning() << "Failed to select video format at index" << formatIndex << hr << std::system_category().message(hr).c_str();
- }
+ HRESULT hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr,
+ videoType);
+ if (FAILED(hr))
+ qWarning() << "Failed to set video format" << errorString(hr);
- return false;
+ return SUCCEEDED(hr);
+}
+
+static ComPtr<IMFMediaType> findVideoType(IMFSourceReader *reader,
+ const QCameraFormat &format)
+{
+ for (DWORD i = 0;; ++i) {
+ ComPtr<IMFMediaType> candidate;
+ HRESULT hr = reader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
+ candidate.GetAddressOf());
+ if (FAILED(hr))
+ break;
+
+ GUID subtype = GUID_NULL;
+ if (FAILED(candidate->GetGUID(MF_MT_SUBTYPE, &subtype)))
+ continue;
+
+ if (format.pixelFormat() != pixelFormatFromMediaSubtype(subtype))
+ continue;
+
+ UINT32 width = 0u;
+ UINT32 height = 0u;
+ if (FAILED(MFGetAttributeSize(candidate.Get(), MF_MT_FRAME_SIZE, &width, &height)))
+ continue;
+
+ if (format.resolution() != QSize{ int(width), int(height) })
+ continue;
+
+ return candidate;
+ }
+ return {};
}
class ActiveCamera {
public:
- ActiveCamera() = delete;
-
static std::unique_ptr<ActiveCamera> create(QWindowsCamera &wc, const QCameraDevice &device, const QCameraFormat &format)
{
auto ac = std::unique_ptr<ActiveCamera>(new ActiveCamera(wc));
@@ -201,48 +152,47 @@ public:
if (!ac->m_source)
return {};
- ac->m_readerCallback = QWindowsIUPointer<CameraReaderCallback>(new CameraReaderCallback);
+ ac->m_readerCallback = makeComObject<CameraReaderCallback>();
ac->m_readerCallback->setActiveCamera(ac.get());
- ac->m_reader = createCameraReader(ac->m_source.get(), ac->m_readerCallback);
+ ac->m_reader = createCameraReader(ac->m_source.Get(), ac->m_readerCallback);
if (!ac->m_reader)
return {};
- qsizetype index = device.videoFormats().indexOf(format);
- if (index < 0)
- return {};
-
- if (!ac->setFormat(format, index))
+ if (!ac->setFormat(format))
return {};
return ac;
}
- bool setFormat(const QCameraFormat &format, int formatIndex)
+ bool setFormat(const QCameraFormat &format)
{
- m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM);
- m_flushWait.acquire();
-
- if (!setCameraReaderFormat(m_reader.get(), formatIndex))
- return false;
-
- m_frameFormat = { format.resolution(), format.pixelFormat() };
- m_videoFrameStride = calculateVideoFrameStride(m_reader.get(), formatIndex, format.resolution().width());
+ flush();
+
+ auto videoType = findVideoType(m_reader.Get(), format);
+ if (videoType) {
+ if (setCameraReaderFormat(m_reader.Get(), videoType.Get())) {
+ m_frameFormat = { format.resolution(), format.pixelFormat() };
+ m_videoFrameStride =
+ calculateVideoFrameStride(videoType.Get(), format.resolution().width());
+ }
+ }
- m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, nullptr,
- nullptr, nullptr, nullptr);
+ m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, nullptr, nullptr, nullptr,
+ nullptr);
return true;
}
void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
{
if (FAILED(status)) {
- emit m_windowsCamera.error(int(status), std::system_category().message(status).c_str());
+ const std::string msg{ std::system_category().message(status) };
+ emit m_windowsCamera.error(QCamera::CameraError, QString::fromStdString(msg));
return;
}
if (sample) {
- QWindowsIUPointer<IMFMediaBuffer> mediaBuffer;
- if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.address()))) {
+ ComPtr<IMFMediaBuffer> mediaBuffer;
+ if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf()))) {
DWORD bufLen = 0;
BYTE *buffer = nullptr;
@@ -274,21 +224,27 @@ public:
~ActiveCamera()
{
- m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM);
- m_flushWait.acquire();
+ flush();
m_readerCallback->setActiveCamera(nullptr);
}
private:
explicit ActiveCamera(QWindowsCamera &wc) : m_windowsCamera(wc), m_flushWait(0) {};
+ void flush()
+ {
+ if (SUCCEEDED(m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM))) {
+ m_flushWait.acquire();
+ }
+ }
+
QWindowsCamera &m_windowsCamera;
QSemaphore m_flushWait;
- QWindowsIUPointer<IMFMediaSource> m_source;
- QWindowsIUPointer<IMFSourceReader> m_reader;
- QWindowsIUPointer<CameraReaderCallback> m_readerCallback;
+ ComPtr<IMFMediaSource> m_source;
+ ComPtr<IMFSourceReader> m_reader;
+ ComPtr<CameraReaderCallback> m_readerCallback;
QVideoFrameFormat m_frameFormat;
int m_videoFrameStride = 0;
@@ -339,8 +295,8 @@ void QWindowsCamera::setActive(bool active)
activeChanged(true);
} else {
- emit activeChanged(false);
m_active.reset();
+ emit activeChanged(false);
}
}
@@ -357,11 +313,10 @@ void QWindowsCamera::setCamera(const QCameraDevice &camera)
bool QWindowsCamera::setCameraFormat(const QCameraFormat &format)
{
- qsizetype index = m_cameraDevice.videoFormats().indexOf(format);
- if (index < 0)
+ if (format.isNull())
return false;
- bool ok = m_active ? m_active->setFormat(format, index) : true;
+ bool ok = m_active ? m_active->setFormat(format) : true;
if (ok)
m_cameraFormat = format;
diff --git a/src/plugins/multimedia/ffmpeg/qwindowscamera_p.h b/src/plugins/multimedia/ffmpeg/qwindowscamera_p.h
index 22a096b81..80c05ff59 100644
--- a/src/plugins/multimedia/ffmpeg/qwindowscamera_p.h
+++ b/src/plugins/multimedia/ffmpeg/qwindowscamera_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSCAMERA_H
#define QWINDOWSCAMERA_H
@@ -52,7 +16,7 @@
//
#include <private/qplatformcamera_p.h>
-#include <private/qwindowsiupointer_p.h>
+#include <private/qcomptr_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qx11capturablewindows.cpp b/src/plugins/multimedia/ffmpeg/qx11capturablewindows.cpp
new file mode 100644
index 000000000..9e57cbc64
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qx11capturablewindows.cpp
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qx11capturablewindows_p.h"
+#include "private/qcapturablewindow_p.h"
+#include <qdebug.h>
+
+#include <X11/Xlib.h>
+
+QT_BEGIN_NAMESPACE
+
+QX11CapturableWindows::~QX11CapturableWindows()
+{
+ if (m_display)
+ XCloseDisplay(m_display);
+}
+
+QList<QCapturableWindow> QX11CapturableWindows::windows() const
+{
+ auto display = this->display();
+
+ if (!display)
+ return {};
+
+ Atom atom = XInternAtom(display, "_NET_CLIENT_LIST", true);
+ Atom actualType = 0;
+ int format = 0;
+ unsigned long windowsCount = 0;
+ unsigned long bytesAfter = 0;
+ unsigned char *data = nullptr;
+ const int status = XGetWindowProperty(display, XDefaultRootWindow(display), atom, 0L, (~0L),
+ false, AnyPropertyType, &actualType, &format,
+ &windowsCount, &bytesAfter, &data);
+
+ if (status < Success || !data)
+ return {};
+
+ QList<QCapturableWindow> result;
+
+ auto freeDataGuard = qScopeGuard([data]() { XFree(data); });
+ auto windows = reinterpret_cast<XID *>(data);
+ for (unsigned long i = 0; i < windowsCount; i++) {
+ auto windowData = std::make_unique<QCapturableWindowPrivate>();
+ windowData->id = static_cast<QCapturableWindowPrivate::Id>(windows[i]);
+
+ char *windowTitle = nullptr;
+ if (XFetchName(display, windows[i], &windowTitle) && windowTitle) {
+ windowData->description = QString::fromUtf8(windowTitle);
+ XFree(windowTitle);
+ }
+
+ if (isWindowValid(*windowData))
+ result.push_back(windowData.release()->create());
+ }
+
+ return result;
+}
+
+bool QX11CapturableWindows::isWindowValid(const QCapturableWindowPrivate &window) const
+{
+ auto display = this->display();
+ XWindowAttributes windowAttributes = {};
+ return display
+ && XGetWindowAttributes(display, static_cast<Window>(window.id), &windowAttributes) != 0
+ && windowAttributes.depth > 0;
+}
+
+Display *QX11CapturableWindows::display() const
+{
+ std::call_once(m_displayOnceFlag, [this]() { m_display = XOpenDisplay(nullptr); });
+ return m_display;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qx11capturablewindows_p.h b/src/plugins/multimedia/ffmpeg/qx11capturablewindows_p.h
new file mode 100644
index 000000000..088fe97cb
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qx11capturablewindows_p.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QX11CAPTURABLEWINDOWS_P_H
+#define QX11CAPTURABLEWINDOWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qplatformcapturablewindows_p.h"
+#include <mutex>
+
+struct _XDisplay;
+typedef struct _XDisplay Display;
+
+QT_BEGIN_NAMESPACE
+
+class QX11CapturableWindows : public QPlatformCapturableWindows
+{
+public:
+ ~QX11CapturableWindows() override;
+
+ QList<QCapturableWindow> windows() const override;
+
+ bool isWindowValid(const QCapturableWindowPrivate &window) const override;
+
+private:
+ Display *display() const;
+
+private:
+ mutable std::once_flag m_displayOnceFlag;
+ mutable Display *m_display = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QX11CAPTURABLEWINDOWS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qx11surfacecapture.cpp b/src/plugins/multimedia/ffmpeg/qx11surfacecapture.cpp
new file mode 100644
index 000000000..ab5ea3f48
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qx11surfacecapture.cpp
@@ -0,0 +1,342 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qx11surfacecapture_p.h"
+#include "qffmpegsurfacecapturegrabber_p.h"
+
+#include <qvideoframe.h>
+#include <qscreen.h>
+#include <qwindow.h>
+#include <qdebug.h>
+#include <qguiapplication.h>
+#include <qloggingcategory.h>
+
+#include "private/qabstractvideobuffer_p.h"
+#include "private/qcapturablewindow_p.h"
+#include "private/qmemoryvideobuffer_p.h"
+#include "private/qvideoframeconversionhelper_p.h"
+
+#include <X11/Xlib.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xrandr.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcX11SurfaceCapture, "qt.multimedia.ffmpeg.qx11surfacecapture");
+
+namespace {
+
+void destroyXImage(XImage* image) {
+ XDestroyImage(image); // macro
+}
+
+template <typename T, typename D>
+std::unique_ptr<T, D> makeXUptr(T* ptr, D deleter) {
+ return std::unique_ptr<T, D>(ptr, deleter);
+}
+
+int screenNumberByName(Display *display, const QString &name)
+{
+ int size = 0;
+ auto monitors = makeXUptr(
+ XRRGetMonitors(display, XDefaultRootWindow(display), true, &size),
+ &XRRFreeMonitors);
+ const auto end = monitors.get() + size;
+ auto found = std::find_if(monitors.get(), end, [&](const XRRMonitorInfo &info) {
+ auto atomName = makeXUptr(XGetAtomName(display, info.name), &XFree);
+ return atomName && name == QString::fromUtf8(atomName.get());
+ });
+
+ return found == end ? -1 : std::distance(monitors.get(), found);
+}
+
+QVideoFrameFormat::PixelFormat xImagePixelFormat(const XImage &image)
+{
+ if (image.bits_per_pixel != 32) return QVideoFrameFormat::Format_Invalid;
+
+ if (image.red_mask == 0xff0000 &&
+ image.green_mask == 0xff00 &&
+ image.blue_mask == 0xff)
+ return QVideoFrameFormat::Format_BGRX8888;
+
+ if (image.red_mask == 0xff00 &&
+ image.green_mask == 0xff0000 &&
+ image.blue_mask == 0xff000000)
+ return QVideoFrameFormat::Format_XBGR8888;
+
+ if (image.blue_mask == 0xff0000 &&
+ image.green_mask == 0xff00 &&
+ image.red_mask == 0xff)
+ return QVideoFrameFormat::Format_RGBX8888;
+
+ if (image.red_mask == 0xff00 &&
+ image.green_mask == 0xff0000 &&
+ image.blue_mask == 0xff000000)
+ return QVideoFrameFormat::Format_XRGB8888;
+
+ return QVideoFrameFormat::Format_Invalid;
+}
+
+} // namespace
+
+class QX11SurfaceCapture::Grabber : private QFFmpegSurfaceCaptureGrabber
+{
+public:
+ static std::unique_ptr<Grabber> create(QX11SurfaceCapture &capture, QScreen *screen)
+ {
+ std::unique_ptr<Grabber> result(new Grabber(capture));
+ return result->init(screen) ? std::move(result) : nullptr;
+ }
+
+ static std::unique_ptr<Grabber> create(QX11SurfaceCapture &capture, WId wid)
+ {
+ std::unique_ptr<Grabber> result(new Grabber(capture));
+ return result->init(wid) ? std::move(result) : nullptr;
+ }
+
+ ~Grabber() override
+ {
+ stop();
+
+ detachShm();
+ }
+
+ const QVideoFrameFormat &format() const { return m_format; }
+
+private:
+ Grabber(QX11SurfaceCapture &capture)
+ {
+ addFrameCallback(capture, &QX11SurfaceCapture::newVideoFrame);
+ connect(this, &Grabber::errorUpdated, &capture, &QX11SurfaceCapture::updateError);
+ }
+
+ bool createDisplay()
+ {
+ if (!m_display)
+ m_display.reset(XOpenDisplay(nullptr));
+
+ if (!m_display)
+ updateError(QPlatformSurfaceCapture::InternalError,
+ QLatin1String("Cannot open X11 display"));
+
+ return m_display != nullptr;
+ }
+
+ bool init(WId wid)
+ {
+ if (auto screen = QGuiApplication::primaryScreen())
+ setFrameRate(screen->refreshRate());
+
+ return createDisplay() && initWithXID(static_cast<XID>(wid));
+ }
+
+ bool init(QScreen *screen)
+ {
+ if (!screen) {
+ updateError(QPlatformSurfaceCapture::NotFound, QLatin1String("Screen Not Found"));
+ return false;
+ }
+
+ if (!createDisplay())
+ return false;
+
+ auto screenNumber = screenNumberByName(m_display.get(), screen->name());
+
+ if (screenNumber < 0)
+ return false;
+
+ setFrameRate(screen->refreshRate());
+
+ return initWithXID(RootWindow(m_display.get(), screenNumber));
+ }
+
+ bool initWithXID(XID xid)
+ {
+ m_xid = xid;
+
+ if (update()) {
+ start();
+ return true;
+ }
+
+ return false;
+ }
+
+ void detachShm()
+ {
+ if (std::exchange(m_attached, false)) {
+ XShmDetach(m_display.get(), &m_shmInfo);
+ shmdt(m_shmInfo.shmaddr);
+ shmctl(m_shmInfo.shmid, IPC_RMID, 0);
+ }
+ }
+
+ void attachShm()
+ {
+ Q_ASSERT(!m_attached);
+
+ m_shmInfo.shmid =
+ shmget(IPC_PRIVATE, m_xImage->bytes_per_line * m_xImage->height, IPC_CREAT | 0777);
+
+ if (m_shmInfo.shmid == -1)
+ return;
+
+ m_shmInfo.readOnly = false;
+ m_shmInfo.shmaddr = m_xImage->data = (char *)shmat(m_shmInfo.shmid, 0, 0);
+
+ m_attached = XShmAttach(m_display.get(), &m_shmInfo);
+ }
+
+ bool update()
+ {
+ XWindowAttributes wndattr = {};
+ if (XGetWindowAttributes(m_display.get(), m_xid, &wndattr) == 0) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot get window attributes"));
+ return false;
+ }
+
+ // TODO: if capture windows, we should adjust offsets and size if
+ // the window is out of the screen borders
+ // m_xOffset = ...
+ // m_yOffset = ...
+
+ // check window params for the root window as well since
+ // it potentially can be changed (e.g. on VM with resizing)
+ if (!m_xImage || wndattr.width != m_xImage->width || wndattr.height != m_xImage->height
+ || wndattr.depth != m_xImage->depth || wndattr.visual->visualid != m_visualID) {
+
+ qCDebug(qLcX11SurfaceCapture) << "recreate ximage: " << wndattr.width << wndattr.height
+ << wndattr.depth << wndattr.visual->visualid;
+
+ detachShm();
+ m_xImage.reset();
+
+ m_visualID = wndattr.visual->visualid;
+ m_xImage.reset(XShmCreateImage(m_display.get(), wndattr.visual, wndattr.depth, ZPixmap,
+ nullptr, &m_shmInfo, wndattr.width, wndattr.height));
+
+ if (!m_xImage) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot create image"));
+ return false;
+ }
+
+ const auto pixelFormat = xImagePixelFormat(*m_xImage);
+
+ // TODO: probably, add a converter instead
+ if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Not handled pixel format, bpp=")
+ + QString::number(m_xImage->bits_per_pixel));
+ return false;
+ }
+
+ attachShm();
+
+ if (!m_attached) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String("Cannot attach shared memory"));
+ return false;
+ }
+
+ QVideoFrameFormat format(QSize(m_xImage->width, m_xImage->height), pixelFormat);
+ format.setStreamFrameRate(frameRate());
+ m_format = format;
+ }
+
+ return m_attached;
+ }
+
+protected:
+ QVideoFrame grabFrame() override
+ {
+ if (!update())
+ return {};
+
+ if (!XShmGetImage(m_display.get(), m_xid, m_xImage.get(), m_xOffset, m_yOffset,
+ AllPlanes)) {
+ updateError(QPlatformSurfaceCapture::CaptureFailed,
+ QLatin1String(
+ "Cannot get ximage; the window may be out of the screen borders"));
+ return {};
+ }
+
+ QByteArray data(m_xImage->bytes_per_line * m_xImage->height, Qt::Uninitialized);
+
+ const auto pixelSrc = reinterpret_cast<const uint32_t *>(m_xImage->data);
+ const auto pixelDst = reinterpret_cast<uint32_t *>(data.data());
+ const auto pixelCount = data.size() / 4;
+ const auto xImageAlphaVaries = false; // In known cases it doesn't vary - it's 0xff or 0xff
+
+ qCopyPixelsWithAlphaMask(pixelDst, pixelSrc, pixelCount, m_format.pixelFormat(),
+ xImageAlphaVaries);
+
+ auto buffer = new QMemoryVideoBuffer(data, m_xImage->bytes_per_line);
+ return QVideoFrame(buffer, m_format);
+ }
+
+private:
+ std::optional<QPlatformSurfaceCapture::Error> m_prevGrabberError;
+ XID m_xid = None;
+ int m_xOffset = 0;
+ int m_yOffset = 0;
+ std::unique_ptr<Display, decltype(&XCloseDisplay)> m_display{ nullptr, &XCloseDisplay };
+ std::unique_ptr<XImage, decltype(&destroyXImage)> m_xImage{ nullptr, &destroyXImage };
+ XShmSegmentInfo m_shmInfo;
+ bool m_attached = false;
+ VisualID m_visualID = None;
+ QVideoFrameFormat m_format;
+};
+
+QX11SurfaceCapture::QX11SurfaceCapture(Source initialSource)
+ : QPlatformSurfaceCapture(initialSource)
+{
+ // For debug
+ // XSetErrorHandler([](Display *, XErrorEvent * e) {
+ // qDebug() << "error handler" << e->error_code;
+ // return 0;
+ // });
+}
+
+QX11SurfaceCapture::~QX11SurfaceCapture() = default;
+
+QVideoFrameFormat QX11SurfaceCapture::frameFormat() const
+{
+ return m_grabber ? m_grabber->format() : QVideoFrameFormat{};
+}
+
+bool QX11SurfaceCapture::setActiveInternal(bool active)
+{
+ qCDebug(qLcX11SurfaceCapture) << "set active" << active;
+
+ if (m_grabber)
+ m_grabber.reset();
+ else
+ std::visit([this](auto source) { activate(source); }, source());
+
+ return static_cast<bool>(m_grabber) == active;
+}
+
+void QX11SurfaceCapture::activate(ScreenSource screen)
+{
+ if (checkScreenWithError(screen))
+ m_grabber = Grabber::create(*this, screen);
+}
+
+void QX11SurfaceCapture::activate(WindowSource window)
+{
+ auto handle = QCapturableWindowPrivate::handle(window);
+ m_grabber = Grabber::create(*this, handle ? handle->id : 0);
+}
+
+bool QX11SurfaceCapture::isSupported()
+{
+ return qgetenv("XDG_SESSION_TYPE").compare(QLatin1String("x11"), Qt::CaseInsensitive) == 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/qx11surfacecapture_p.h b/src/plugins/multimedia/ffmpeg/qx11surfacecapture_p.h
new file mode 100644
index 000000000..7f794fd8b
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/qx11surfacecapture_p.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef X11SURFACECAPTURE_P_H
+#define X11SURFACECAPTURE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qplatformsurfacecapture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QX11SurfaceCapture : public QPlatformSurfaceCapture
+{
+ class Grabber;
+
+public:
+ explicit QX11SurfaceCapture(Source initialSource);
+ ~QX11SurfaceCapture() override;
+
+ QVideoFrameFormat frameFormat() const override;
+
+ static bool isSupported();
+
+protected:
+ bool setActiveInternal(bool active) override;
+
+private:
+ void activate(ScreenSource);
+
+ void activate(WindowSource);
+
+private:
+ std::unique_ptr<Grabber> m_grabber;
+};
+
+QT_END_NAMESPACE
+
+#endif // X11SURFACECAPTURE_P_H
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp
new file mode 100644
index 000000000..9948952e8
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp
@@ -0,0 +1,220 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qffmpegaudioencoder_p.h"
+#include "qffmpegaudioencoderutils_p.h"
+#include "qffmpegaudioinput_p.h"
+#include "qffmpegencoderoptions_p.h"
+#include "qffmpegmuxer_p.h"
+#include "qffmpegrecordingengine_p.h"
+#include "qffmpegmediaformatinfo_p.h"
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+static Q_LOGGING_CATEGORY(qLcFFmpegAudioEncoder, "qt.multimedia.ffmpeg.audioencoder");
+
+AudioEncoder::AudioEncoder(RecordingEngine &recordingEngine, QFFmpegAudioInput *input,
+ const QMediaEncoderSettings &settings)
+ : EncoderThread(recordingEngine), m_input(input), m_settings(settings)
+{
+ setObjectName(QLatin1String("AudioEncoder"));
+ qCDebug(qLcFFmpegAudioEncoder) << "AudioEncoder" << settings.audioCodec();
+
+ m_format = input->device.preferredFormat();
+ auto codecID = QFFmpegMediaFormatInfo::codecIdForAudioCodec(settings.audioCodec());
+ Q_ASSERT(avformat_query_codec(recordingEngine.avFormatContext()->oformat, codecID,
+ FF_COMPLIANCE_NORMAL));
+
+ const AVAudioFormat requestedAudioFormat(m_format);
+
+ m_avCodec = QFFmpeg::findAVEncoder(codecID, {}, requestedAudioFormat.sampleFormat);
+
+ if (!m_avCodec)
+ m_avCodec = QFFmpeg::findAVEncoder(codecID);
+
+ qCDebug(qLcFFmpegAudioEncoder) << "found audio codec" << m_avCodec->name;
+
+ Q_ASSERT(m_avCodec);
+
+ m_stream = avformat_new_stream(recordingEngine.avFormatContext(), nullptr);
+ m_stream->id = recordingEngine.avFormatContext()->nb_streams - 1;
+ m_stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ m_stream->codecpar->codec_id = codecID;
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ m_stream->codecpar->channel_layout =
+ adjustChannelLayout(m_avCodec->channel_layouts, requestedAudioFormat.channelLayoutMask);
+ m_stream->codecpar->channels = qPopulationCount(m_stream->codecpar->channel_layout);
+#else
+ m_stream->codecpar->ch_layout =
+ adjustChannelLayout(m_avCodec->ch_layouts, requestedAudioFormat.channelLayout);
+#endif
+ const auto sampleRate =
+ adjustSampleRate(m_avCodec->supported_samplerates, requestedAudioFormat.sampleRate);
+
+ m_stream->codecpar->sample_rate = sampleRate;
+ m_stream->codecpar->frame_size = 1024;
+ m_stream->codecpar->format =
+ adjustSampleFormat(m_avCodec->sample_fmts, requestedAudioFormat.sampleFormat);
+
+ m_stream->time_base = AVRational{ 1, sampleRate };
+
+ qCDebug(qLcFFmpegAudioEncoder) << "set stream time_base" << m_stream->time_base.num << "/"
+ << m_stream->time_base.den;
+}
+
+void AudioEncoder::open()
+{
+ m_codecContext.reset(avcodec_alloc_context3(m_avCodec));
+
+ if (m_stream->time_base.num != 1 || m_stream->time_base.den != m_format.sampleRate()) {
+ qCDebug(qLcFFmpegAudioEncoder) << "Most likely, av_format_write_header changed time base from"
+ << 1 << "/" << m_format.sampleRate() << "to"
+ << m_stream->time_base;
+ }
+
+ m_codecContext->time_base = m_stream->time_base;
+
+ avcodec_parameters_to_context(m_codecContext.get(), m_stream->codecpar);
+
+ AVDictionaryHolder opts;
+ applyAudioEncoderOptions(m_settings, m_avCodec->name, m_codecContext.get(), opts);
+ applyExperimentalCodecOptions(m_avCodec, opts);
+
+ int res = avcodec_open2(m_codecContext.get(), m_avCodec, opts);
+ qCDebug(qLcFFmpegAudioEncoder) << "audio codec opened" << res;
+ qCDebug(qLcFFmpegAudioEncoder) << "audio codec params: fmt=" << m_codecContext->sample_fmt
+ << "rate=" << m_codecContext->sample_rate;
+
+ const AVAudioFormat requestedAudioFormat(m_format);
+ const AVAudioFormat codecAudioFormat(m_codecContext.get());
+
+ if (requestedAudioFormat != codecAudioFormat)
+ m_resampler = createResampleContext(requestedAudioFormat, codecAudioFormat);
+}
+
+void AudioEncoder::addBuffer(const QAudioBuffer &buffer)
+{
+ QMutexLocker locker = lockLoopData();
+ if (!m_paused.loadRelaxed()) {
+ m_audioBufferQueue.push(buffer);
+ locker.unlock();
+ dataReady();
+ }
+}
+
+QAudioBuffer AudioEncoder::takeBuffer()
+{
+ QMutexLocker locker = lockLoopData();
+ return dequeueIfPossible(m_audioBufferQueue);
+}
+
+void AudioEncoder::init()
+{
+ open();
+ if (m_input) {
+ m_input->setFrameSize(m_codecContext->frame_size);
+ }
+ qCDebug(qLcFFmpegAudioEncoder) << "AudioEncoder::init started audio device thread.";
+}
+
+void AudioEncoder::cleanup()
+{
+ while (!m_audioBufferQueue.empty())
+ processOne();
+ while (avcodec_send_frame(m_codecContext.get(), nullptr) == AVERROR(EAGAIN))
+ retrievePackets();
+ retrievePackets();
+}
+
+bool AudioEncoder::hasData() const
+{
+ return !m_audioBufferQueue.empty();
+}
+
+void AudioEncoder::retrievePackets()
+{
+ while (1) {
+ AVPacketUPtr packet(av_packet_alloc());
+ int ret = avcodec_receive_packet(m_codecContext.get(), packet.get());
+ if (ret < 0) {
+ if (ret != AVERROR(EOF))
+ break;
+ if (ret != AVERROR(EAGAIN)) {
+ char errStr[1024];
+ av_strerror(ret, errStr, 1024);
+ qCDebug(qLcFFmpegAudioEncoder) << "receive packet" << ret << errStr;
+ }
+ break;
+ }
+
+ // qCDebug(qLcFFmpegEncoder) << "writing audio packet" << packet->size << packet->pts <<
+ // packet->dts;
+ packet->stream_index = m_stream->id;
+ m_recordingEngine.getMuxer()->addPacket(std::move(packet));
+ }
+}
+
+void AudioEncoder::processOne()
+{
+ QAudioBuffer buffer = takeBuffer();
+ if (!buffer.isValid())
+ return;
+
+ if (buffer.format() != m_format) {
+ // should we recreate recreate resampler here?
+ qWarning() << "Get invalid audio format:" << buffer.format() << ", expected:" << m_format;
+ return;
+ }
+
+ // qCDebug(qLcFFmpegEncoder) << "new audio buffer" << buffer.byteCount() << buffer.format()
+ // << buffer.frameCount() << codec->frame_size;
+ retrievePackets();
+
+ auto frame = makeAVFrame();
+ frame->format = m_codecContext->sample_fmt;
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+ frame->channel_layout = m_codecContext->channel_layout;
+ frame->channels = m_codecContext->channels;
+#else
+ frame->ch_layout = m_codecContext->ch_layout;
+#endif
+ frame->sample_rate = m_codecContext->sample_rate;
+ frame->nb_samples = buffer.frameCount();
+ if (frame->nb_samples)
+ av_frame_get_buffer(frame.get(), 0);
+
+ if (m_resampler) {
+ const uint8_t *data = buffer.constData<uint8_t>();
+ swr_convert(m_resampler.get(), frame->extended_data, frame->nb_samples, &data,
+ frame->nb_samples);
+ } else {
+ memcpy(frame->buf[0]->data, buffer.constData<uint8_t>(), buffer.byteCount());
+ }
+
+ const auto &timeBase = m_stream->time_base;
+ const auto pts = timeBase.den && timeBase.num
+ ? timeBase.den * m_samplesWritten / (m_codecContext->sample_rate * timeBase.num)
+ : m_samplesWritten;
+ setAVFrameTime(*frame, pts, timeBase);
+ m_samplesWritten += buffer.frameCount();
+
+ qint64 time = m_format.durationForFrames(m_samplesWritten);
+ m_recordingEngine.newTimeStamp(time / 1000);
+
+ // qCDebug(qLcFFmpegEncoder) << "sending audio frame" << buffer.byteCount() << frame->pts <<
+ // ((double)buffer.frameCount()/frame->sample_rate);
+
+ int ret = avcodec_send_frame(m_codecContext.get(), frame.get());
+ if (ret < 0) {
+ char errStr[1024];
+ av_strerror(ret, errStr, 1024);
+ // qCDebug(qLcFFmpegEncoder) << "error sending frame" << ret << errStr;
+ }
+}
+
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h
new file mode 100644
index 000000000..16d8d81a1
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGAUDIOENCODER_P_H
+#define QFFMPEGAUDIOENCODER_P_H
+
+#include "qffmpeg_p.h"
+#include "qffmpegencoderthread_p.h"
+#include "private/qplatformmediarecorder_p.h"
+#include <qaudiobuffer.h>
+#include <queue>
+
+QT_BEGIN_NAMESPACE
+
+class QMediaEncoderSettings;
+class QFFmpegAudioInput;
+
+namespace QFFmpeg {
+
+class AudioEncoder : public EncoderThread
+{
+public:
+ AudioEncoder(RecordingEngine &recordingEngine, QFFmpegAudioInput *input,
+ const QMediaEncoderSettings &settings);
+
+ void open();
+ void addBuffer(const QAudioBuffer &buffer);
+
+ QFFmpegAudioInput *audioInput() const { return m_input; }
+
+private:
+ QAudioBuffer takeBuffer();
+ void retrievePackets();
+
+ void init() override;
+ void cleanup() override;
+ bool hasData() const override;
+ void processOne() override;
+
+private:
+ std::queue<QAudioBuffer> m_audioBufferQueue;
+
+ AVStream *m_stream = nullptr;
+ AVCodecContextUPtr m_codecContext;
+ QFFmpegAudioInput *m_input = nullptr;
+ QAudioFormat m_format;
+
+ SwrContextUPtr m_resampler;
+ qint64 m_samplesWritten = 0;
+ const AVCodec *m_avCodec = nullptr;
+ QMediaEncoderSettings m_settings;
+};
+
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoderutils.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoderutils.cpp
new file mode 100644
index 000000000..ea36a8138
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoderutils.cpp
@@ -0,0 +1,97 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegaudioencoderutils_p.h"
+#include "qalgorithms.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+AVSampleFormat adjustSampleFormat(const AVSampleFormat *supportedFormats, AVSampleFormat requested)
+{
+ auto calcScore = [requested](AVSampleFormat format) {
+ return format == requested ? BestAVScore
+ : format == av_get_planar_sample_fmt(requested) ? BestAVScore - 1
+ : 0;
+ };
+
+ const auto result = findBestAVFormat(supportedFormats, calcScore).first;
+ return result == AV_SAMPLE_FMT_NONE ? requested : result;
+}
+
+int adjustSampleRate(const int *supportedRates, int requested)
+{
+ auto calcScore = [requested](int rate) {
+ return requested == rate ? BestAVScore
+ : requested <= rate ? rate - requested
+ : requested - rate - 1000000;
+ };
+
+ const auto result = findBestAVValue(supportedRates, calcScore).first;
+ return result == 0 ? requested : result;
+}
+
+static AVScore calculateScoreByChannelsCount(int supportedChannelsNumber,
+ int requestedChannelsNumber)
+{
+ if (supportedChannelsNumber >= requestedChannelsNumber)
+ return requestedChannelsNumber - supportedChannelsNumber;
+
+ return supportedChannelsNumber - requestedChannelsNumber - 10000;
+}
+
+static AVScore calculateScoreByChannelsMask(int supportedChannelsNumber, uint64_t supportedMask,
+ int requestedChannelsNumber, uint64_t requestedMask)
+{
+ if ((supportedMask & requestedMask) == requestedMask)
+ return BestAVScore - qPopulationCount(supportedMask & ~requestedMask);
+
+ return calculateScoreByChannelsCount(supportedChannelsNumber, requestedChannelsNumber);
+}
+
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+
+uint64_t adjustChannelLayout(const uint64_t *supportedMasks, uint64_t requested)
+{
+ auto calcScore = [requested](uint64_t mask) {
+ return calculateScoreByChannelsMask(qPopulationCount(mask), mask,
+ qPopulationCount(requested), requested);
+ };
+
+ const auto result = findBestAVValue(supportedMasks, calcScore).first;
+ return result == 0 ? requested : result;
+}
+
+#else
+
+AVChannelLayout adjustChannelLayout(const AVChannelLayout *supportedLayouts,
+ const AVChannelLayout &requested)
+{
+ auto calcScore = [&requested](const AVChannelLayout &layout) {
+ if (layout == requested)
+ return BestAVScore;
+
+ // The only realistic case for now:
+ // layout.order == requested.order == AV_CHANNEL_ORDER_NATIVE
+ // Let's consider other orders to make safe code
+
+ if (layout.order == AV_CHANNEL_ORDER_CUSTOM || requested.order == AV_CHANNEL_ORDER_CUSTOM)
+ return calculateScoreByChannelsCount(layout.nb_channels, requested.nb_channels) - 1000;
+
+ const auto offset = layout.order == requested.order ? 1 : 100;
+
+ return calculateScoreByChannelsMask(layout.nb_channels, layout.u.mask,
+ requested.nb_channels, requested.u.mask)
+ - offset;
+ };
+
+ const auto result = findBestAVValue(supportedLayouts, calcScore);
+ return result.second == NotSuitableAVScore ? requested : result.first;
+}
+
+#endif
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoderutils_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoderutils_p.h
new file mode 100644
index 000000000..8a7c184ec
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoderutils_p.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFFMPEGAUDIOENCODERUTILS_P_H
+#define QFFMPEGAUDIOENCODERUTILS_P_H
+
+#include "qffmpeg_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+AVSampleFormat adjustSampleFormat(const AVSampleFormat *supportedFormats, AVSampleFormat requested);
+
+int adjustSampleRate(const int *supportedRates, int requested);
+
+#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
+uint64_t adjustChannelLayout(const uint64_t *supportedLayouts, uint64_t requested);
+#else
+AVChannelLayout adjustChannelLayout(const AVChannelLayout *supportedLayouts,
+ const AVChannelLayout &requested);
+#endif
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGAUDIOENCODERUTILS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions.cpp
index fad90b3c1..bd6a8e09b 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegencoderoptions.cpp
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qffmpegencoderoptions_p.h"
#if QT_CONFIG(vaapi)
@@ -93,6 +57,20 @@ static int bitrateForSettings(const QMediaEncoderSettings &settings, bool hdr =
return bitrate;
}
+static void apply_openh264(const QMediaEncoderSettings &settings, AVCodecContext *codec,
+ AVDictionary **opts)
+{
+ if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding
+ || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
+ codec->bit_rate = settings.videoBitRate();
+ av_dict_set(opts, "rc_mode", "bitrate", 0);
+ } else {
+ av_dict_set(opts, "rc_mode", "quality", 0);
+ static const int q[] = { 51, 48, 38, 25, 5 };
+ codec->qmax = codec->qmin = q[settings.quality()];
+ }
+}
+
static void apply_x264(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
{
if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
@@ -132,7 +110,7 @@ static void apply_libvpx(const QMediaEncoderSettings &settings, AVCodecContext *
}
#ifdef Q_OS_DARWIN
-static void apply_videotoolbox(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **)
+static void apply_videotoolbox(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
{
if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
codec->bit_rate = settings.videoBitRate();
@@ -152,6 +130,13 @@ static void apply_videotoolbox(const QMediaEncoderSettings &settings, AVCodecCon
codec->bit_rate = bitrateForSettings(settings);
#endif
}
+
+ // Videotooldox hw acceleration fails of some hardwares,
+ // allow_sw makes sw encoding available if hw encoding failed.
+ // Under the hood, ffmpeg sets
+ // kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder instead of
+ // kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder
+ av_dict_set(opts, "allow_sw", "1", 0);
}
#endif
@@ -206,14 +191,34 @@ static void apply_vaapi(const QMediaEncoderSettings &settings, AVCodecContext *c
break;
}
- if (quality) {
- qDebug() << "using quality" << settings.quality() << quality[settings.quality()];
+ if (quality)
codec->global_quality = quality[settings.quality()];
- }
}
}
#endif
+static void apply_nvenc(const QMediaEncoderSettings &settings, AVCodecContext *codec,
+ AVDictionary **opts)
+{
+ switch (settings.encodingMode()) {
+ case QMediaRecorder::EncodingMode::AverageBitRateEncoding:
+ av_dict_set(opts, "vbr", "1", 0);
+ codec->bit_rate = settings.videoBitRate();
+ break;
+ case QMediaRecorder::EncodingMode::ConstantBitRateEncoding:
+ av_dict_set(opts, "cbr", "1", 0);
+ codec->bit_rate = settings.videoBitRate();
+ codec->rc_max_rate = codec->rc_min_rate = codec->bit_rate;
+ break;
+ case QMediaRecorder::EncodingMode::ConstantQualityEncoding: {
+ static const char *q[] = { "51", "48", "35", "15", "1" };
+ av_dict_set(opts, "cq", q[settings.quality()], 0);
+ } break;
+ default:
+ break;
+ }
+}
+
#ifdef Q_OS_WINDOWS
static void apply_mf(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
{
@@ -230,6 +235,49 @@ static void apply_mf(const QMediaEncoderSettings &settings, AVCodecContext *code
}
#endif
+#ifdef Q_OS_ANDROID
+static void apply_mediacodec(const QMediaEncoderSettings &settings, AVCodecContext *codec,
+ AVDictionary **opts)
+{
+ codec->bit_rate = settings.videoBitRate();
+
+ const int quality[] = { 25, 50, 75, 90, 100 };
+ codec->global_quality = quality[settings.quality()];
+
+ switch (settings.encodingMode()) {
+ case QMediaRecorder::EncodingMode::AverageBitRateEncoding:
+ av_dict_set(opts, "bitrate_mode", "vbr", 1);
+ break;
+ case QMediaRecorder::EncodingMode::ConstantBitRateEncoding:
+ av_dict_set(opts, "bitrate_mode", "cbr", 1);
+ break;
+ case QMediaRecorder::EncodingMode::ConstantQualityEncoding:
+ // av_dict_set(opts, "bitrate_mode", "cq", 1);
+ av_dict_set(opts, "bitrate_mode", "cbr", 1);
+ break;
+ default:
+ break;
+ }
+
+ switch (settings.videoCodec()) {
+ case QMediaFormat::VideoCodec::H264: {
+ const char *levels[] = { "2.2", "3.2", "4.2", "5.2", "6.2" };
+ av_dict_set(opts, "level", levels[settings.quality()], 1);
+ codec->profile = FF_PROFILE_H264_HIGH;
+ break;
+ }
+ case QMediaFormat::VideoCodec::H265: {
+ const char *levels[] = { "h2.1", "h3.1", "h4.1", "h5.1", "h6.1" };
+ av_dict_set(opts, "level", levels[settings.quality()], 1);
+ codec->profile = FF_PROFILE_HEVC_MAIN;
+ break;
+ }
+ default:
+ break;
+ }
+}
+#endif
+
namespace QFFmpeg {
using ApplyOptions = void (*)(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts);
@@ -237,31 +285,37 @@ using ApplyOptions = void (*)(const QMediaEncoderSettings &settings, AVCodecCont
const struct {
const char *name;
ApplyOptions apply;
-} videoCodecOptionTable[] = {
- { "libx264", apply_x264 },
- { "libx265xx", apply_x265 },
- { "libvpx", apply_libvpx },
- { "libvpx_vp9", apply_libvpx },
+} videoCodecOptionTable[] = { { "libx264", apply_x264 },
+ { "libx265xx", apply_x265 },
+ { "libvpx", apply_libvpx },
+ { "libvpx_vp9", apply_libvpx },
+ { "libopenh264", apply_openh264 },
+ { "h264_nvenc", apply_nvenc },
+ { "hevc_nvenc", apply_nvenc },
+ { "av1_nvenc", apply_nvenc },
#ifdef Q_OS_DARWIN
- { "h264_videotoolbox", apply_videotoolbox },
- { "hevc_videotoolbox", apply_videotoolbox },
- { "prores_videotoolbox", apply_videotoolbox },
- { "vp9_videotoolbox", apply_videotoolbox },
+ { "h264_videotoolbox", apply_videotoolbox },
+ { "hevc_videotoolbox", apply_videotoolbox },
+ { "prores_videotoolbox", apply_videotoolbox },
+ { "vp9_videotoolbox", apply_videotoolbox },
#endif
#if QT_CONFIG(vaapi)
- { "mpeg2_vaapi", apply_vaapi },
- { "mjpeg_vaapi", apply_vaapi },
- { "h264_vaapi", apply_vaapi },
- { "hevc_vaapi", apply_vaapi },
- { "vp8_vaapi", apply_vaapi },
- { "vp9_vaapi", apply_vaapi },
+ { "mpeg2_vaapi", apply_vaapi },
+ { "mjpeg_vaapi", apply_vaapi },
+ { "h264_vaapi", apply_vaapi },
+ { "hevc_vaapi", apply_vaapi },
+ { "vp8_vaapi", apply_vaapi },
+ { "vp9_vaapi", apply_vaapi },
#endif
#ifdef Q_OS_WINDOWS
- { "hevc_mf", apply_mf },
- { "h264_mf", apply_mf },
+ { "hevc_mf", apply_mf },
+ { "h264_mf", apply_mf },
#endif
- { nullptr, nullptr }
-};
+#ifdef Q_OS_ANDROID
+ { "hevc_mediacodec", apply_mediacodec },
+ { "h264_mediacodec", apply_mediacodec },
+#endif
+ { nullptr, nullptr } };
const struct {
const char *name;
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions_p.h
new file mode 100644
index 000000000..005ad7652
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderoptions_p.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGENCODEROPTIONS_P_H
+#define QFFMPEGENCODEROPTIONS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpeghwaccel_p.h"
+#include "qvideoframeformat.h"
+#include "private/qplatformmediarecorder_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts);
+void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts);
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp
new file mode 100644
index 000000000..b673af450
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qffmpegencoderthread_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+EncoderThread::EncoderThread(RecordingEngine &recordingEngine) : m_recordingEngine(recordingEngine)
+{
+}
+
+void EncoderThread::setPaused(bool b)
+{
+ m_paused.storeRelease(b);
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h
new file mode 100644
index 000000000..1fe35303b
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGENCODERTHREAD_P_H
+#define QFFMPEGENCODERTHREAD_P_H
+
+#include "qffmpegthread_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+class RecordingEngine;
+
+class EncoderThread : public ConsumerThread
+{
+public:
+ EncoderThread(RecordingEngine &recordingEngine);
+ virtual void setPaused(bool b);
+
+protected:
+ QAtomicInteger<bool> m_paused = false;
+ RecordingEngine &m_recordingEngine;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp
new file mode 100644
index 000000000..2df594017
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp
@@ -0,0 +1,63 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qffmpegmuxer_p.h"
+#include "qffmpegrecordingengine_p.h"
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+static Q_LOGGING_CATEGORY(qLcFFmpegMuxer, "qt.multimedia.ffmpeg.muxer");
+
+Muxer::Muxer(RecordingEngine *encoder) : m_encoder(encoder)
+{
+ setObjectName(QLatin1String("Muxer"));
+}
+
+void Muxer::addPacket(AVPacketUPtr packet)
+{
+ {
+ QMutexLocker locker = lockLoopData();
+ m_packetQueue.push(std::move(packet));
+ }
+
+ // qCDebug(qLcFFmpegEncoder) << "Muxer::addPacket" << packet->pts << packet->stream_index;
+ dataReady();
+}
+
+AVPacketUPtr Muxer::takePacket()
+{
+ QMutexLocker locker = lockLoopData();
+ return dequeueIfPossible(m_packetQueue);
+}
+
+void Muxer::init()
+{
+ qCDebug(qLcFFmpegMuxer) << "Muxer::init started thread.";
+}
+
+void Muxer::cleanup()
+{
+ while (!m_packetQueue.empty())
+ processOne();
+}
+
+bool QFFmpeg::Muxer::hasData() const
+{
+ return !m_packetQueue.empty();
+}
+
+void Muxer::processOne()
+{
+ auto packet = takePacket();
+ // qCDebug(qLcFFmpegEncoder) << "writing packet to file" << packet->pts << packet->duration <<
+ // packet->stream_index;
+
+ // the function takes ownership for the packet
+ av_interleaved_write_frame(m_encoder->avFormatContext(), packet.release());
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h
new file mode 100644
index 000000000..4f8f4d27a
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGMUXER_P_H
+#define QFFMPEGMUXER_P_H
+
+#include "qffmpegthread_p.h"
+#include "qffmpeg_p.h"
+#include <queue>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+class RecordingEngine;
+
+class Muxer : public ConsumerThread
+{
+public:
+ Muxer(RecordingEngine *encoder);
+
+ void addPacket(AVPacketUPtr packet);
+
+private:
+ AVPacketUPtr takePacket();
+
+ void init() override;
+ void cleanup() override;
+ bool hasData() const override;
+ void processOne() override;
+
+private:
+ std::queue<AVPacketUPtr> m_packetQueue;
+
+ RecordingEngine *m_encoder;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp
new file mode 100644
index 000000000..23bd4c02d
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp
@@ -0,0 +1,182 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qffmpegrecordingengine_p.h"
+#include "qffmpegmediaformatinfo_p.h"
+#include "qffmpegvideoframeencoder_p.h"
+#include "private/qmultimediautils_p.h"
+
+#include <qdebug.h>
+#include "qffmpegaudioencoder_p.h"
+#include "qffmpegaudioinput_p.h"
+#include <private/qplatformcamera_p.h>
+#include "qffmpegvideobuffer_p.h"
+#include "qffmpegvideoencoder_p.h"
+#include "qffmpegmediametadata_p.h"
+#include "qffmpegmuxer_p.h"
+#include <qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcFFmpegEncoder, "qt.multimedia.ffmpeg.encoder");
+
+namespace QFFmpeg
+{
+
+RecordingEngine::RecordingEngine(const QMediaEncoderSettings &settings,
+ std::unique_ptr<EncodingFormatContext> context)
+ : m_settings(settings), m_formatContext(std::move(context)), m_muxer(new Muxer(this))
+{
+ Q_ASSERT(m_formatContext);
+ Q_ASSERT(m_formatContext->isAVIOOpen());
+}
+
+RecordingEngine::~RecordingEngine()
+{
+}
+
+void RecordingEngine::addAudioInput(QFFmpegAudioInput *input)
+{
+ m_audioEncoder = new AudioEncoder(*this, input, m_settings);
+ addMediaFrameHandler(input, &QFFmpegAudioInput::newAudioBuffer, m_audioEncoder,
+ &AudioEncoder::addBuffer);
+ input->setRunning(true);
+}
+
+void RecordingEngine::addVideoSource(QPlatformVideoSource * source)
+{
+ auto frameFormat = source->frameFormat();
+
+ if (!frameFormat.isValid()) {
+ qCWarning(qLcFFmpegEncoder) << "Cannot add source; invalid vide frame format";
+ emit error(QMediaRecorder::ResourceError,
+ QLatin1StringView("Cannot get video source format"));
+ return;
+ }
+
+ std::optional<AVPixelFormat> hwPixelFormat = source->ffmpegHWPixelFormat()
+ ? AVPixelFormat(*source->ffmpegHWPixelFormat())
+ : std::optional<AVPixelFormat>{};
+
+ qCDebug(qLcFFmpegEncoder) << "adding video source" << source->metaObject()->className() << ":"
+ << "pixelFormat=" << frameFormat.pixelFormat()
+ << "frameSize=" << frameFormat.frameSize()
+ << "frameRate=" << frameFormat.streamFrameRate()
+ << "ffmpegHWPixelFormat=" << (hwPixelFormat ? *hwPixelFormat : AV_PIX_FMT_NONE);
+
+ auto veUPtr = std::make_unique<VideoEncoder>(*this, m_settings, frameFormat, hwPixelFormat);
+ if (!veUPtr->isValid()) {
+ emit error(QMediaRecorder::FormatError, QLatin1StringView("Cannot initialize encoder"));
+ return;
+ }
+
+ auto ve = veUPtr.release();
+ addMediaFrameHandler(source, &QPlatformVideoSource::newVideoFrame, ve, &VideoEncoder::addFrame);
+ m_videoEncoders.append(ve);
+}
+
+void RecordingEngine::start()
+{
+ qCDebug(qLcFFmpegEncoder) << "RecordingEngine::start!";
+
+ avFormatContext()->metadata = QFFmpegMetaData::toAVMetaData(m_metaData);
+
+ Q_ASSERT(!m_isHeaderWritten);
+
+ int res = avformat_write_header(avFormatContext(), nullptr);
+ if (res < 0) {
+ qWarning() << "could not write header, error:" << res << err2str(res);
+ emit error(QMediaRecorder::ResourceError, "Cannot start writing the stream");
+ return;
+ }
+
+ m_isHeaderWritten = true;
+
+ qCDebug(qLcFFmpegEncoder) << "stream header is successfully written";
+
+ m_muxer->start();
+ if (m_audioEncoder)
+ m_audioEncoder->start();
+ for (auto *videoEncoder : m_videoEncoders)
+ if (videoEncoder->isValid())
+ videoEncoder->start();
+}
+
+RecordingEngine::EncodingFinalizer::EncodingFinalizer(RecordingEngine &recordingEngine)
+ : m_recordingEngine(recordingEngine)
+{
+ connect(this, &QThread::finished, this, &QObject::deleteLater);
+}
+
+void RecordingEngine::EncodingFinalizer::run()
+{
+ if (m_recordingEngine.m_audioEncoder)
+ m_recordingEngine.m_audioEncoder->stopAndDelete();
+ for (auto &videoEncoder : m_recordingEngine.m_videoEncoders)
+ videoEncoder->stopAndDelete();
+ m_recordingEngine.m_muxer->stopAndDelete();
+
+ if (m_recordingEngine.m_isHeaderWritten) {
+ const int res = av_write_trailer(m_recordingEngine.avFormatContext());
+ if (res < 0) {
+ const auto errorDescription = err2str(res);
+ qCWarning(qLcFFmpegEncoder) << "could not write trailer" << res << errorDescription;
+ emit m_recordingEngine.error(QMediaRecorder::FormatError,
+ QLatin1String("Cannot write trailer: ")
+ + errorDescription);
+ }
+ }
+ // else ffmpeg might crash
+
+ // close AVIO before emitting finalizationDone.
+ m_recordingEngine.m_formatContext->closeAVIO();
+
+ qCDebug(qLcFFmpegEncoder) << " done finalizing.";
+ emit m_recordingEngine.finalizationDone();
+ auto recordingEnginePtr = &m_recordingEngine;
+ delete recordingEnginePtr;
+}
+
+void RecordingEngine::finalize()
+{
+ qCDebug(qLcFFmpegEncoder) << ">>>>>>>>>>>>>>> finalize";
+
+ for (auto &conn : m_connections)
+ disconnect(conn);
+
+ auto *finalizer = new EncodingFinalizer(*this);
+ finalizer->start();
+}
+
+void RecordingEngine::setPaused(bool p)
+{
+ if (m_audioEncoder)
+ m_audioEncoder->setPaused(p);
+ for (auto &videoEncoder : m_videoEncoders)
+ videoEncoder->setPaused(p);
+}
+
+void RecordingEngine::setMetaData(const QMediaMetaData &metaData)
+{
+ m_metaData = metaData;
+}
+
+void RecordingEngine::newTimeStamp(qint64 time)
+{
+ QMutexLocker locker(&m_timeMutex);
+ if (time > m_timeRecorded) {
+ m_timeRecorded = time;
+ emit durationChanged(time);
+ }
+}
+
+template<typename... Args>
+void RecordingEngine::addMediaFrameHandler(Args &&...args)
+{
+ auto connection = connect(std::forward<Args>(args)..., Qt::DirectConnection);
+ m_connections.append(connection);
+}
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qffmpegrecordingengine_p.cpp"
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h
new file mode 100644
index 000000000..b74fbba9f
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGENCODER_P_H
+#define QFFMPEGENCODER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpegthread_p.h"
+#include "qffmpegencodingformatcontext_p.h"
+
+#include <private/qplatformmediarecorder_p.h>
+#include <qmediarecorder.h>
+
+#include <queue>
+
+QT_BEGIN_NAMESPACE
+
+class QFFmpegAudioInput;
+class QVideoFrame;
+class QPlatformVideoSource;
+
+namespace QFFmpeg
+{
+
+class RecordingEngine;
+class Muxer;
+class AudioEncoder;
+class VideoEncoder;
+class VideoFrameEncoder;
+
+template <typename T>
+T dequeueIfPossible(std::queue<T> &queue)
+{
+ if (queue.empty())
+ return T{};
+
+ auto result = std::move(queue.front());
+ queue.pop();
+ return result;
+}
+
+class RecordingEngine : public QObject
+{
+ Q_OBJECT
+public:
+ RecordingEngine(const QMediaEncoderSettings &settings, std::unique_ptr<EncodingFormatContext> context);
+ ~RecordingEngine();
+
+ void addAudioInput(QFFmpegAudioInput *input);
+ void addVideoSource(QPlatformVideoSource *source);
+
+ void start();
+ void finalize();
+
+ void setPaused(bool p);
+
+ void setMetaData(const QMediaMetaData &metaData);
+ AVFormatContext *avFormatContext() { return m_formatContext->avFormatContext(); }
+ Muxer *getMuxer() { return m_muxer; }
+
+public Q_SLOTS:
+ void newTimeStamp(qint64 time);
+
+Q_SIGNALS:
+ void durationChanged(qint64 duration);
+ void error(QMediaRecorder::Error code, const QString &description);
+ void finalizationDone();
+
+private:
+ template<typename... Args>
+ void addMediaFrameHandler(Args &&...args);
+
+ class EncodingFinalizer : public QThread
+ {
+ public:
+ EncodingFinalizer(RecordingEngine &recordingEngine);
+
+ void run() override;
+
+ private:
+ RecordingEngine &m_recordingEngine;
+ };
+
+private:
+ QMediaEncoderSettings m_settings;
+ QMediaMetaData m_metaData;
+ std::unique_ptr<EncodingFormatContext> m_formatContext;
+ Muxer *m_muxer = nullptr;
+
+ AudioEncoder *m_audioEncoder = nullptr;
+ QList<VideoEncoder *> m_videoEncoders;
+ QList<QMetaObject::Connection> m_connections;
+
+ QMutex m_timeMutex;
+ qint64 m_timeRecorded = 0;
+
+ bool m_isHeaderWritten = false;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp
new file mode 100644
index 000000000..5f9dd83c4
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp
@@ -0,0 +1,191 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qffmpegvideoencoder_p.h"
+#include "qffmpegmuxer_p.h"
+#include "qffmpegvideobuffer_p.h"
+#include "qffmpegrecordingengine_p.h"
+#include "qffmpegvideoframeencoder_p.h"
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+static Q_LOGGING_CATEGORY(qLcFFmpegVideoEncoder, "qt.multimedia.ffmpeg.videoencoder");
+
+VideoEncoder::VideoEncoder(RecordingEngine &recordingEngine, const QMediaEncoderSettings &settings,
+ const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat)
+ : EncoderThread(recordingEngine)
+{
+ setObjectName(QLatin1String("VideoEncoder"));
+
+ AVPixelFormat swFormat = QFFmpegVideoBuffer::toAVPixelFormat(format.pixelFormat());
+ AVPixelFormat ffmpegPixelFormat =
+ hwFormat && *hwFormat != AV_PIX_FMT_NONE ? *hwFormat : swFormat;
+ auto frameRate = format.streamFrameRate();
+ if (frameRate <= 0.) {
+ qWarning() << "Invalid frameRate" << frameRate << "; Using the default instead";
+
+ // set some default frame rate since ffmpeg has UB if it's 0.
+ frameRate = 30.;
+ }
+
+ m_frameEncoder = VideoFrameEncoder::create(settings,
+ format.frameSize(),
+ format.rotation(),
+ frameRate,
+ ffmpegPixelFormat,
+ swFormat,
+ recordingEngine.avFormatContext());
+}
+
+VideoEncoder::~VideoEncoder() = default;
+
+bool VideoEncoder::isValid() const
+{
+ return m_frameEncoder != nullptr;
+}
+
+void VideoEncoder::addFrame(const QVideoFrame &frame)
+{
+ QMutexLocker locker = lockLoopData();
+
+ // Drop frames if encoder can not keep up with the video source data rate
+ const bool queueFull = m_videoFrameQueue.size() >= m_maxQueueSize;
+
+ if (queueFull) {
+ qCDebug(qLcFFmpegVideoEncoder) << "RecordingEngine frame queue full. Frame lost.";
+ } else if (!m_paused.loadRelaxed()) {
+ m_videoFrameQueue.push(frame);
+
+ locker.unlock(); // Avoid context switch on wake wake-up
+
+ dataReady();
+ }
+}
+
+QVideoFrame VideoEncoder::takeFrame()
+{
+ QMutexLocker locker = lockLoopData();
+ return dequeueIfPossible(m_videoFrameQueue);
+}
+
+void VideoEncoder::retrievePackets()
+{
+ if (!m_frameEncoder)
+ return;
+ while (auto packet = m_frameEncoder->retrievePacket())
+ m_recordingEngine.getMuxer()->addPacket(std::move(packet));
+}
+
+void VideoEncoder::init()
+{
+ qCDebug(qLcFFmpegVideoEncoder) << "VideoEncoder::init started video device thread.";
+ bool ok = m_frameEncoder->open();
+ if (!ok)
+ emit m_recordingEngine.error(QMediaRecorder::ResourceError, "Could not initialize encoder");
+}
+
+void VideoEncoder::cleanup()
+{
+ while (!m_videoFrameQueue.empty())
+ processOne();
+ if (m_frameEncoder) {
+ while (m_frameEncoder->sendFrame(nullptr) == AVERROR(EAGAIN))
+ retrievePackets();
+ retrievePackets();
+ }
+}
+
+bool VideoEncoder::hasData() const
+{
+ return !m_videoFrameQueue.empty();
+}
+
+struct QVideoFrameHolder
+{
+ QVideoFrame f;
+ QImage i;
+};
+
+static void freeQVideoFrame(void *opaque, uint8_t *)
+{
+ delete reinterpret_cast<QVideoFrameHolder *>(opaque);
+}
+
+void VideoEncoder::processOne()
+{
+ retrievePackets();
+
+ auto frame = takeFrame();
+ if (!frame.isValid())
+ return;
+
+ if (!isValid())
+ return;
+
+ // qCDebug(qLcFFmpegEncoder) << "new video buffer" << frame.startTime();
+
+ AVFrameUPtr avFrame;
+
+ auto *videoBuffer = dynamic_cast<QFFmpegVideoBuffer *>(frame.videoBuffer());
+ if (videoBuffer) {
+ // ffmpeg video buffer, let's use the native AVFrame stored in there
+ auto *hwFrame = videoBuffer->getHWFrame();
+ if (hwFrame && hwFrame->format == m_frameEncoder->sourceFormat())
+ avFrame.reset(av_frame_clone(hwFrame));
+ }
+
+ if (!avFrame) {
+ frame.map(QVideoFrame::ReadOnly);
+ auto size = frame.size();
+ avFrame = makeAVFrame();
+ avFrame->format = m_frameEncoder->sourceFormat();
+ avFrame->width = size.width();
+ avFrame->height = size.height();
+
+ for (int i = 0; i < 4; ++i) {
+ avFrame->data[i] = const_cast<uint8_t *>(frame.bits(i));
+ avFrame->linesize[i] = frame.bytesPerLine(i);
+ }
+
+ QImage img;
+ if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
+ // the QImage is cached inside the video frame, so we can take the pointer to the image
+ // data here
+ img = frame.toImage();
+ avFrame->data[0] = (uint8_t *)img.bits();
+ avFrame->linesize[0] = img.bytesPerLine();
+ }
+
+ Q_ASSERT(avFrame->data[0]);
+ // ensure the video frame and it's data is alive as long as it's being used in the encoder
+ avFrame->opaque_ref = av_buffer_create(nullptr, 0, freeQVideoFrame,
+ new QVideoFrameHolder{ frame, img }, 0);
+ }
+
+ if (m_baseTime.loadAcquire() == std::numeric_limits<qint64>::min()) {
+ m_baseTime.storeRelease(frame.startTime() - m_lastFrameTime);
+ qCDebug(qLcFFmpegVideoEncoder) << ">>>> adjusting base time to" << m_baseTime.loadAcquire()
+ << frame.startTime() << m_lastFrameTime;
+ }
+
+ qint64 time = frame.startTime() - m_baseTime.loadAcquire();
+ m_lastFrameTime = frame.endTime() - m_baseTime.loadAcquire();
+
+ setAVFrameTime(*avFrame, m_frameEncoder->getPts(time), m_frameEncoder->getTimeBase());
+
+ m_recordingEngine.newTimeStamp(time / 1000);
+
+ qCDebug(qLcFFmpegVideoEncoder)
+ << ">>> sending frame" << avFrame->pts << time << m_lastFrameTime;
+ int ret = m_frameEncoder->sendFrame(std::move(avFrame));
+ if (ret < 0) {
+ qCDebug(qLcFFmpegVideoEncoder) << "error sending frame" << ret << err2str(ret);
+ emit m_recordingEngine.error(QMediaRecorder::ResourceError, err2str(ret));
+ }
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h
new file mode 100644
index 000000000..8f9a943de
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGVIDEOENCODER_P_H
+#define QFFMPEGVIDEOENCODER_P_H
+
+#include "qffmpegencoderthread_p.h"
+#include "qffmpeg_p.h"
+#include <qvideoframe.h>
+#include <queue>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoFrameFormat;
+class QMediaEncoderSettings;
+
+namespace QFFmpeg {
+class VideoFrameEncoder;
+
+class VideoEncoder : public EncoderThread
+{
+public:
+ VideoEncoder(RecordingEngine &recordingEngine, const QMediaEncoderSettings &settings,
+ const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat);
+ ~VideoEncoder() override;
+
+ bool isValid() const;
+
+ void addFrame(const QVideoFrame &frame);
+
+ void setPaused(bool b) override
+ {
+ EncoderThread::setPaused(b);
+ if (b)
+ m_baseTime.storeRelease(-1);
+ }
+
+private:
+ QVideoFrame takeFrame();
+ void retrievePackets();
+
+ void init() override;
+ void cleanup() override;
+ bool hasData() const override;
+ void processOne() override;
+
+private:
+ std::queue<QVideoFrame> m_videoFrameQueue;
+ const size_t m_maxQueueSize = 10; // Arbitrarily chosen to limit memory usage (332 MB @ 4K)
+
+ std::unique_ptr<VideoFrameEncoder> m_frameEncoder;
+ QAtomicInteger<qint64> m_baseTime = std::numeric_limits<qint64>::min();
+ qint64 m_lastFrameTime = 0;
+};
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils.cpp
new file mode 100644
index 000000000..83b9575b4
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils.cpp
@@ -0,0 +1,213 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegvideoencoderutils_p.h"
+#include "private/qmultimediautils_p.h"
+
+extern "C" {
+#include <libavutil/pixdesc.h>
+}
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+static AVScore calculateTargetSwFormatScore(const AVPixFmtDescriptor *sourceSwFormatDesc,
+ AVPixelFormat fmt)
+{
+ // determine the format used by the encoder.
+ // We prefer YUV422 based formats such as NV12 or P010. Selection trues to find the best
+ // matching format for the encoder depending on the bit depth of the source format
+
+ const auto *desc = av_pix_fmt_desc_get(fmt);
+ if (!desc)
+ return NotSuitableAVScore;
+
+ const int sourceDepth = sourceSwFormatDesc ? sourceSwFormatDesc->comp[0].depth : 0;
+
+ if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
+ // we really don't want HW accelerated formats here
+ return NotSuitableAVScore;
+
+ auto score = DefaultAVScore;
+
+ if (desc == sourceSwFormatDesc)
+ // prefer exact matches
+ score += 10;
+ if (desc->comp[0].depth == sourceDepth)
+ score += 100;
+ else if (desc->comp[0].depth < sourceDepth)
+ score -= 100 + (sourceDepth - desc->comp[0].depth);
+ if (desc->log2_chroma_h == 1)
+ score += 1;
+ if (desc->log2_chroma_w == 1)
+ score += 1;
+ if (desc->flags & AV_PIX_FMT_FLAG_BE)
+ score -= 10;
+ if (desc->flags & AV_PIX_FMT_FLAG_PAL)
+ // we don't want paletted formats
+ score -= 10000;
+ if (desc->flags & AV_PIX_FMT_FLAG_RGB)
+ // we don't want RGB formats
+ score -= 1000;
+
+ // qCDebug(qLcVideoFrameEncoder)
+ // << "checking format" << fmt << Qt::hex << desc->flags << desc->comp[0].depth
+ // << desc->log2_chroma_h << desc->log2_chroma_w << "score:" << score;
+
+ return score;
+}
+
+static auto targetSwFormatScoreCalculator(AVPixelFormat sourceFormat)
+{
+ const auto sourceSwFormatDesc = av_pix_fmt_desc_get(sourceFormat);
+ return [=](AVPixelFormat fmt) { return calculateTargetSwFormatScore(sourceSwFormatDesc, fmt); };
+}
+
+static bool isHwFormatAcceptedByCodec(AVPixelFormat pixFormat)
+{
+ switch (pixFormat) {
+ case AV_PIX_FMT_MEDIACODEC:
+ // Mediacodec doesn't accept AV_PIX_FMT_MEDIACODEC (QTBUG-116836)
+ return false;
+ default:
+ return true;
+ }
+}
+
+AVPixelFormat findTargetSWFormat(AVPixelFormat sourceSWFormat, const AVCodec *codec,
+ const HWAccel &accel)
+{
+ auto scoreCalculator = targetSwFormatScoreCalculator(sourceSWFormat);
+
+ const auto constraints = accel.constraints();
+ if (constraints && constraints->valid_sw_formats)
+ return findBestAVFormat(constraints->valid_sw_formats, scoreCalculator).first;
+
+ // Some codecs, e.g. mediacodec, don't expose constraints, let's find the format in
+ // codec->pix_fmts
+ if (codec->pix_fmts)
+ return findBestAVFormat(codec->pix_fmts, scoreCalculator).first;
+
+ return AV_PIX_FMT_NONE;
+}
+
+AVPixelFormat findTargetFormat(AVPixelFormat sourceFormat, AVPixelFormat sourceSWFormat,
+ const AVCodec *codec, const HWAccel *accel)
+{
+ Q_UNUSED(sourceFormat);
+
+ if (accel) {
+ const auto hwFormat = accel->hwFormat();
+
+ // TODO: handle codec->capabilities & AV_CODEC_CAP_HARDWARE here
+ if (!isHwFormatAcceptedByCodec(hwFormat))
+ return findTargetSWFormat(sourceSWFormat, codec, *accel);
+
+ const auto constraints = accel->constraints();
+ if (constraints && hasAVFormat(constraints->valid_hw_formats, hwFormat))
+ return hwFormat;
+
+ // Some codecs, don't expose constraints, let's find the format in codec->pix_fmts
+ if (hasAVFormat(codec->pix_fmts, hwFormat))
+ return hwFormat;
+ }
+
+ if (!codec->pix_fmts) {
+ qWarning() << "Codec pix formats are undefined, it's likely to behave incorrectly";
+
+ return sourceSWFormat;
+ }
+
+ auto swScoreCalculator = targetSwFormatScoreCalculator(sourceSWFormat);
+ return findBestAVFormat(codec->pix_fmts, swScoreCalculator).first;
+}
+
+std::pair<const AVCodec *, std::unique_ptr<HWAccel>> findHwEncoder(AVCodecID codecID,
+ const QSize &resolution)
+{
+ auto matchesSizeConstraints = [&resolution](const HWAccel &accel) {
+ const auto constraints = accel.constraints();
+ if (!constraints)
+ return true;
+
+ return resolution.width() >= constraints->min_width
+ && resolution.height() >= constraints->min_height
+ && resolution.width() <= constraints->max_width
+ && resolution.height() <= constraints->max_height;
+ };
+
+ // 1st - attempt to find hw accelerated encoder
+ auto result = HWAccel::findEncoderWithHwAccel(codecID, matchesSizeConstraints);
+ Q_ASSERT(!!result.first == !!result.second);
+
+ return result;
+}
+
+const AVCodec *findSwEncoder(AVCodecID codecID, AVPixelFormat sourceSWFormat)
+{
+ auto formatScoreCalculator = targetSwFormatScoreCalculator(sourceSWFormat);
+
+ return findAVEncoder(codecID, [&formatScoreCalculator](const AVCodec *codec) {
+ if (!codec->pix_fmts)
+ // codecs without pix_fmts are suspicious
+ return MinAVScore;
+
+ return findBestAVFormat(codec->pix_fmts, formatScoreCalculator).second;
+ });
+}
+
+AVRational adjustFrameRate(const AVRational *supportedRates, qreal requestedRate)
+{
+ auto calcScore = [requestedRate](const AVRational &rate) {
+ // relative comparison
+ return qMin(requestedRate * rate.den, qreal(rate.num))
+ / qMax(requestedRate * rate.den, qreal(rate.num));
+ };
+
+ const auto result = findBestAVValue(supportedRates, calcScore).first;
+ if (result.num && result.den)
+ return result;
+
+ const auto [num, den] = qRealToFraction(requestedRate);
+ return { num, den };
+}
+
+AVRational adjustFrameTimeBase(const AVRational *supportedRates, AVRational frameRate)
+{
+ // TODO: user-specified frame rate might be required.
+ if (supportedRates) {
+ auto hasFrameRate = [&]() {
+ for (auto rate = supportedRates; rate->num && rate->den; ++rate)
+ if (rate->den == frameRate.den && rate->num == frameRate.num)
+ return true;
+
+ return false;
+ };
+
+ Q_ASSERT(hasFrameRate());
+
+ return { frameRate.den, frameRate.num };
+ }
+
+ constexpr int TimeScaleFactor = 1000; // Allows not to follow fixed rate
+ return { frameRate.den, frameRate.num * TimeScaleFactor };
+}
+
+QSize adjustVideoResolution(const AVCodec *codec, QSize requestedResolution)
+{
+#ifdef Q_OS_WINDOWS
+ // TODO: investigate, there might be more encoders not supporting odd resolution
+ if (strcmp(codec->name, "h264_mf") == 0) {
+ auto makeEven = [](int size) { return size & ~1; };
+ return QSize(makeEven(requestedResolution.width()), makeEven(requestedResolution.height()));
+ }
+#else
+ Q_UNUSED(codec);
+#endif
+ return requestedResolution;
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils_p.h
new file mode 100644
index 000000000..3a16a7de3
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoderutils_p.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGVIDEOENCODERUTILS_P_H
+#define QFFMPEGVIDEOENCODERUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpeg_p.h"
+#include "qffmpeghwaccel_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QFFmpeg {
+
+AVPixelFormat findTargetSWFormat(AVPixelFormat sourceSWFormat, const AVCodec *codec,
+ const HWAccel &accel);
+
+AVPixelFormat findTargetFormat(AVPixelFormat sourceFormat, AVPixelFormat sourceSWFormat,
+ const AVCodec *codec, const HWAccel *accel);
+
+std::pair<const AVCodec *, std::unique_ptr<HWAccel>> findHwEncoder(AVCodecID codecID,
+ const QSize &sourceSize);
+
+const AVCodec *findSwEncoder(AVCodecID codecID, AVPixelFormat sourceSWFormat);
+
+/**
+ * @brief adjustFrameRate get a rational frame rate be requested qreal rate.
+ * If the codec supports fixed frame rate (non-null supportedRates),
+ * the function selects the most suitable one,
+ * otherwise just makes AVRational from qreal.
+ */
+AVRational adjustFrameRate(const AVRational *supportedRates, qreal requestedRate);
+
+/**
+ * @brief adjustFrameTimeBase gets adjusted timebase by a list of supported frame rates
+ * and an already adjusted frame rate.
+ *
+ * Timebase is the fundamental unit of time (in seconds) in terms
+ * of which frame timestamps are represented.
+ * For fixed-fps content (non-null supportedRates),
+ * timebase should be 1/framerate.
+ *
+ * For more information, see AVStream::time_base and AVCodecContext::time_base.
+ *
+ * The adjusted time base is supposed to be set to stream and codec context.
+ */
+AVRational adjustFrameTimeBase(const AVRational *supportedRates, AVRational frameRate);
+
+QSize adjustVideoResolution(const AVCodec *codec, QSize requestedResolution);
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
+
+#endif // QFFMPEGVIDEOENCODERUTILS_P_H
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder.cpp
new file mode 100644
index 000000000..18d9c1401
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder.cpp
@@ -0,0 +1,423 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qffmpegvideoframeencoder_p.h"
+#include "qffmpegmediaformatinfo_p.h"
+#include "qffmpegencoderoptions_p.h"
+#include "qffmpegvideoencoderutils_p.h"
+#include <qloggingcategory.h>
+
+extern "C" {
+#include "libavutil/display.h"
+}
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qLcVideoFrameEncoder, "qt.multimedia.ffmpeg.videoencoder");
+
+namespace QFFmpeg {
+
+std::unique_ptr<VideoFrameEncoder>
+VideoFrameEncoder::create(const QMediaEncoderSettings &encoderSettings,
+ const QSize &sourceSize,
+ QtVideo::Rotation sourceRotation,
+ qreal sourceFrameRate,
+ AVPixelFormat sourceFormat,
+ AVPixelFormat sourceSWFormat,
+ AVFormatContext *formatContext)
+{
+ Q_ASSERT(isSwPixelFormat(sourceSWFormat));
+ Q_ASSERT(isHwPixelFormat(sourceFormat) || sourceSWFormat == sourceFormat);
+
+ std::unique_ptr<VideoFrameEncoder> result(new VideoFrameEncoder);
+
+ result->m_settings = encoderSettings;
+ result->m_sourceSize = sourceSize;
+ result->m_sourceFormat = sourceFormat;
+ result->m_sourceRotation = sourceRotation;
+
+ // Temporary: check isSwPixelFormat because of android issue (QTBUG-116836)
+ result->m_sourceSWFormat = isSwPixelFormat(sourceFormat) ? sourceFormat : sourceSWFormat;
+
+ if (!result->m_settings.videoResolution().isValid())
+ result->m_settings.setVideoResolution(sourceSize);
+
+ if (result->m_settings.videoFrameRate() <= 0.)
+ result->m_settings.setVideoFrameRate(sourceFrameRate);
+
+ if (!result->initCodec() || !result->initTargetFormats()
+ || !result->initCodecContext(formatContext)) {
+ return nullptr;
+ }
+
+ // TODO: make VideoFrameEncoder::private and do openning here
+ // if (!open()) {
+ // m_error = QMediaRecorder::FormatError;
+ // m_errorStr = QLatin1StringView("Cannot open codec");
+ // return;
+ // }
+
+ result->updateConversions();
+
+ return result;
+}
+
+bool VideoFrameEncoder::initCodec()
+{
+ const auto qVideoCodec = m_settings.videoCodec();
+ const auto codecID = QFFmpegMediaFormatInfo::codecIdForVideoCodec(qVideoCodec);
+ const auto resolution = m_settings.videoResolution();
+
+ std::tie(m_codec, m_accel) = findHwEncoder(codecID, resolution);
+
+ if (!m_codec)
+ m_codec = findSwEncoder(codecID, m_sourceSWFormat);
+
+ if (!m_codec) {
+ qWarning() << "Could not find encoder for codecId" << codecID;
+ return false;
+ }
+
+ qCDebug(qLcVideoFrameEncoder) << "found encoder" << m_codec->name << "for id" << m_codec->id;
+
+#ifdef Q_OS_WINDOWS
+ // TODO: investigate, there might be more encoders not supporting odd resolution
+ if (strcmp(m_codec->name, "h264_mf") == 0) {
+ auto makeEven = [](int size) { return size & ~1; };
+ const QSize fixedResolution(makeEven(resolution.width()), makeEven(resolution.height()));
+ if (fixedResolution != resolution) {
+ qCDebug(qLcVideoFrameEncoder) << "Fix odd video resolution for codec" << m_codec->name
+ << ":" << resolution << "->" << fixedResolution;
+ m_settings.setVideoResolution(fixedResolution);
+ }
+ }
+#endif
+
+ auto fixedResolution = adjustVideoResolution(m_codec, m_settings.videoResolution());
+ if (resolution != fixedResolution) {
+ qCDebug(qLcVideoFrameEncoder) << "Fix odd video resolution for codec" << m_codec->name
+ << ":" << resolution << "->" << fixedResolution;
+
+ m_settings.setVideoResolution(fixedResolution);
+ }
+
+ if (m_codec->supported_framerates && qLcVideoFrameEncoder().isEnabled(QtDebugMsg))
+ for (auto rate = m_codec->supported_framerates; rate->num && rate->den; ++rate)
+ qCDebug(qLcVideoFrameEncoder) << "supported frame rate:" << *rate;
+
+ m_codecFrameRate = adjustFrameRate(m_codec->supported_framerates, m_settings.videoFrameRate());
+ qCDebug(qLcVideoFrameEncoder) << "Adjusted frame rate:" << m_codecFrameRate;
+
+ return true;
+}
+
+bool VideoFrameEncoder::initTargetFormats()
+{
+ m_targetFormat = findTargetFormat(m_sourceFormat, m_sourceSWFormat, m_codec, m_accel.get());
+
+ if (m_targetFormat == AV_PIX_FMT_NONE) {
+ qWarning() << "Could not find target format for codecId" << m_codec->id;
+ return false;
+ }
+
+ if (isHwPixelFormat(m_targetFormat)) {
+ Q_ASSERT(m_accel);
+
+ m_targetSWFormat = findTargetSWFormat(m_sourceSWFormat, m_codec, *m_accel);
+
+ if (m_targetSWFormat == AV_PIX_FMT_NONE) {
+ qWarning() << "Cannot find software target format. sourceSWFormat:" << m_sourceSWFormat
+ << "targetFormat:" << m_targetFormat;
+ return false;
+ }
+
+ m_accel->createFramesContext(m_targetSWFormat, m_settings.videoResolution());
+ if (!m_accel->hwFramesContextAsBuffer())
+ return false;
+ } else {
+ m_targetSWFormat = m_targetFormat;
+ }
+
+ return true;
+}
+
+VideoFrameEncoder::~VideoFrameEncoder() = default;
+
+bool QFFmpeg::VideoFrameEncoder::initCodecContext(AVFormatContext *formatContext)
+{
+ m_stream = avformat_new_stream(formatContext, nullptr);
+ m_stream->id = formatContext->nb_streams - 1;
+ //qCDebug(qLcVideoFrameEncoder) << "Video stream: index" << d->stream->id;
+ m_stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ m_stream->codecpar->codec_id = m_codec->id;
+
+ // Apples HEVC decoders don't like the hev1 tag ffmpeg uses by default, use hvc1 as the more commonly accepted tag
+ if (m_codec->id == AV_CODEC_ID_HEVC)
+ m_stream->codecpar->codec_tag = MKTAG('h', 'v', 'c', '1');
+
+ const auto resolution = m_settings.videoResolution();
+
+ // ### Fix hardcoded values
+ m_stream->codecpar->format = m_targetFormat;
+ m_stream->codecpar->width = resolution.width();
+ m_stream->codecpar->height = resolution.height();
+ m_stream->codecpar->sample_aspect_ratio = AVRational{ 1, 1 };
+
+ if (m_sourceRotation != QtVideo::Rotation::None) {
+ constexpr auto displayMatrixSize = sizeof(int32_t) * 9;
+ AVPacketSideData sideData = { reinterpret_cast<uint8_t *>(av_malloc(displayMatrixSize)),
+ displayMatrixSize, AV_PKT_DATA_DISPLAYMATRIX };
+ av_display_rotation_set(reinterpret_cast<int32_t *>(sideData.data),
+ static_cast<double>(m_sourceRotation));
+ addStreamSideData(m_stream, sideData);
+ }
+
+ Q_ASSERT(m_codec);
+
+ m_stream->time_base = adjustFrameTimeBase(m_codec->supported_framerates, m_codecFrameRate);
+ m_codecContext.reset(avcodec_alloc_context3(m_codec));
+ if (!m_codecContext) {
+ qWarning() << "Could not allocate codec context";
+ return false;
+ }
+
+ avcodec_parameters_to_context(m_codecContext.get(), m_stream->codecpar);
+ m_codecContext->time_base = m_stream->time_base;
+ qCDebug(qLcVideoFrameEncoder) << "codecContext time base" << m_codecContext->time_base.num
+ << m_codecContext->time_base.den;
+
+ m_codecContext->framerate = m_codecFrameRate;
+ m_codecContext->pix_fmt = m_targetFormat;
+ m_codecContext->width = resolution.width();
+ m_codecContext->height = resolution.height();
+
+ if (m_accel) {
+ auto deviceContext = m_accel->hwDeviceContextAsBuffer();
+ Q_ASSERT(deviceContext);
+ m_codecContext->hw_device_ctx = av_buffer_ref(deviceContext);
+
+ if (auto framesContext = m_accel->hwFramesContextAsBuffer())
+ m_codecContext->hw_frames_ctx = av_buffer_ref(framesContext);
+ }
+
+ return true;
+}
+
+bool VideoFrameEncoder::open()
+{
+ if (!m_codecContext)
+ return false;
+
+ AVDictionaryHolder opts;
+ applyVideoEncoderOptions(m_settings, m_codec->name, m_codecContext.get(), opts);
+ applyExperimentalCodecOptions(m_codec, opts);
+
+ int res = avcodec_open2(m_codecContext.get(), m_codec, opts);
+ if (res < 0) {
+ m_codecContext.reset();
+ qWarning() << "Couldn't open codec for writing" << err2str(res);
+ return false;
+ }
+ qCDebug(qLcVideoFrameEncoder) << "video codec opened" << res << "time base"
+ << m_codecContext->time_base;
+ return true;
+}
+
+qint64 VideoFrameEncoder::getPts(qint64 us) const
+{
+ qint64 div = 1'000'000 * m_stream->time_base.num;
+ return div != 0 ? (us * m_stream->time_base.den + div / 2) / div : 0;
+}
+
+const AVRational &VideoFrameEncoder::getTimeBase() const
+{
+ return m_stream->time_base;
+}
+
+int VideoFrameEncoder::sendFrame(AVFrameUPtr frame)
+{
+ if (!m_codecContext) {
+ qWarning() << "codec context is not initialized!";
+ return AVERROR(EINVAL);
+ }
+
+ if (!frame)
+ return avcodec_send_frame(m_codecContext.get(), frame.get());
+
+ if (frame->format != m_sourceFormat) {
+ qWarning() << "Frame format has changed:" << m_sourceFormat << "->" << frame->format;
+ return AVERROR(EINVAL);
+ }
+
+ const QSize frameSize(frame->width, frame->height);
+ if (frameSize != m_sourceSize) {
+ qCDebug(qLcVideoFrameEncoder) << "Update conversions on the fly. Source size"
+ << m_sourceSize << "->" << frameSize;
+ m_sourceSize = frameSize;
+ updateConversions();
+ }
+
+ int64_t pts = 0;
+ AVRational timeBase = {};
+ getAVFrameTime(*frame, pts, timeBase);
+
+ if (m_downloadFromHW) {
+ auto f = makeAVFrame();
+
+ int err = av_hwframe_transfer_data(f.get(), frame.get(), 0);
+ if (err < 0) {
+ qCDebug(qLcVideoFrameEncoder) << "Error transferring frame data to surface." << err2str(err);
+ return err;
+ }
+
+ frame = std::move(f);
+ }
+
+ if (m_converter) {
+ auto f = makeAVFrame();
+
+ f->format = m_targetSWFormat;
+ f->width = m_settings.videoResolution().width();
+ f->height = m_settings.videoResolution().height();
+
+ av_frame_get_buffer(f.get(), 0);
+ const auto scaledHeight = sws_scale(m_converter.get(), frame->data, frame->linesize, 0,
+ frame->height, f->data, f->linesize);
+
+ if (scaledHeight != f->height)
+ qCWarning(qLcVideoFrameEncoder) << "Scaled height" << scaledHeight << "!=" << f->height;
+
+ frame = std::move(f);
+ }
+
+ if (m_uploadToHW) {
+ auto *hwFramesContext = m_accel->hwFramesContextAsBuffer();
+ Q_ASSERT(hwFramesContext);
+ auto f = makeAVFrame();
+
+ if (!f)
+ return AVERROR(ENOMEM);
+ int err = av_hwframe_get_buffer(hwFramesContext, f.get(), 0);
+ if (err < 0) {
+ qCDebug(qLcVideoFrameEncoder) << "Error getting HW buffer" << err2str(err);
+ return err;
+ } else {
+ qCDebug(qLcVideoFrameEncoder) << "got HW buffer";
+ }
+ if (!f->hw_frames_ctx) {
+ qCDebug(qLcVideoFrameEncoder) << "no hw frames context";
+ return AVERROR(ENOMEM);
+ }
+ err = av_hwframe_transfer_data(f.get(), frame.get(), 0);
+ if (err < 0) {
+ qCDebug(qLcVideoFrameEncoder) << "Error transferring frame data to surface." << err2str(err);
+ return err;
+ }
+ frame = std::move(f);
+ }
+
+ qCDebug(qLcVideoFrameEncoder) << "sending frame" << pts << "*" << timeBase;
+
+ setAVFrameTime(*frame, pts, timeBase);
+ return avcodec_send_frame(m_codecContext.get(), frame.get());
+}
+
+AVPacketUPtr VideoFrameEncoder::retrievePacket()
+{
+ if (!m_codecContext)
+ return nullptr;
+
+ auto getPacket = [&]() {
+ AVPacketUPtr packet(av_packet_alloc());
+ const int ret = avcodec_receive_packet(m_codecContext.get(), packet.get());
+ if (ret < 0) {
+ if (ret != AVERROR(EOF) && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
+ qCDebug(qLcVideoFrameEncoder) << "Error receiving packet" << ret << err2str(ret);
+ return AVPacketUPtr{};
+ }
+ auto ts = timeStampMs(packet->pts, m_stream->time_base);
+
+ qCDebug(qLcVideoFrameEncoder)
+ << "got a packet" << packet->pts << packet->dts << (ts ? *ts : 0);
+
+ packet->stream_index = m_stream->id;
+ return packet;
+ };
+
+ auto fixPacketDts = [&](AVPacket &packet) {
+ // Workaround for some ffmpeg codecs bugs (e.g. nvenc)
+ // Ideally, packet->pts < packet->dts is not expected
+
+ if (packet.dts == AV_NOPTS_VALUE)
+ return true;
+
+ packet.dts -= m_packetDtsOffset;
+
+ if (packet.pts != AV_NOPTS_VALUE && packet.pts < packet.dts) {
+ m_packetDtsOffset += packet.dts - packet.pts;
+ packet.dts = packet.pts;
+
+ if (m_prevPacketDts != AV_NOPTS_VALUE && packet.dts < m_prevPacketDts) {
+ qCWarning(qLcVideoFrameEncoder)
+ << "Skip packet; failed to fix dts:" << packet.dts << m_prevPacketDts;
+ return false;
+ }
+ }
+
+ m_prevPacketDts = packet.dts;
+
+ return true;
+ };
+
+ while (auto packet = getPacket()) {
+ if (fixPacketDts(*packet))
+ return packet;
+ }
+
+ return nullptr;
+}
+
+void VideoFrameEncoder::updateConversions()
+{
+ const bool needToScale = m_sourceSize != m_settings.videoResolution();
+ const bool zeroCopy = m_sourceFormat == m_targetFormat && !needToScale;
+
+ m_converter.reset();
+
+ if (zeroCopy) {
+ m_downloadFromHW = false;
+ m_uploadToHW = false;
+
+ qCDebug(qLcVideoFrameEncoder) << "zero copy encoding, format" << m_targetFormat;
+ // no need to initialize any converters
+ return;
+ }
+
+ m_downloadFromHW = m_sourceFormat != m_sourceSWFormat;
+ m_uploadToHW = m_targetFormat != m_targetSWFormat;
+
+ if (m_sourceSWFormat != m_targetSWFormat || needToScale) {
+ const auto targetSize = m_settings.videoResolution();
+ qCDebug(qLcVideoFrameEncoder)
+ << "video source and encoder use different formats:" << m_sourceSWFormat
+ << m_targetSWFormat << "or sizes:" << m_sourceSize << targetSize;
+
+ m_converter.reset(sws_getContext(m_sourceSize.width(), m_sourceSize.height(),
+ m_sourceSWFormat, targetSize.width(), targetSize.height(),
+ m_targetSWFormat, SWS_FAST_BILINEAR, nullptr, nullptr,
+ nullptr));
+ }
+
+ qCDebug(qLcVideoFrameEncoder) << "VideoFrameEncoder conversions initialized:"
+ << "sourceFormat:" << m_sourceFormat
+ << (isHwPixelFormat(m_sourceFormat) ? "(hw)" : "(sw)")
+ << "targetFormat:" << m_targetFormat
+ << (isHwPixelFormat(m_targetFormat) ? "(hw)" : "(sw)")
+ << "sourceSWFormat:" << m_sourceSWFormat
+ << "targetSWFormat:" << m_targetSWFormat
+ << "converter:" << m_converter.get();
+}
+
+} // namespace QFFmpeg
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder_p.h
new file mode 100644
index 000000000..b8e51e6c1
--- /dev/null
+++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoframeencoder_p.h
@@ -0,0 +1,89 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QFFMPEGVIDEOFRAMEENCODER_P_H
+#define QFFMPEGVIDEOFRAMEENCODER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qffmpeghwaccel_p.h"
+#include "private/qplatformmediarecorder_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QMediaEncoderSettings;
+
+namespace QFFmpeg {
+
+class VideoFrameEncoder
+{
+public:
+ static std::unique_ptr<VideoFrameEncoder> create(const QMediaEncoderSettings &encoderSettings,
+ const QSize &sourceSize,
+ QtVideo::Rotation sourceRotation,
+ qreal sourceFrameRate,
+ AVPixelFormat sourceFormat,
+ AVPixelFormat sourceSWFormat,
+ AVFormatContext *formatContext);
+
+ ~VideoFrameEncoder();
+
+ bool open();
+
+ AVPixelFormat sourceFormat() const { return m_sourceFormat; }
+ AVPixelFormat targetFormat() const { return m_targetFormat; }
+
+ qint64 getPts(qint64 ms) const;
+
+ const AVRational &getTimeBase() const;
+
+ int sendFrame(AVFrameUPtr frame);
+ AVPacketUPtr retrievePacket();
+
+private:
+ VideoFrameEncoder() = default;
+
+ void updateConversions();
+
+ bool initCodec();
+
+ bool initTargetFormats();
+
+ bool initCodecContext(AVFormatContext *formatContext);
+
+private:
+ QMediaEncoderSettings m_settings;
+ QSize m_sourceSize;
+ QtVideo::Rotation m_sourceRotation = QtVideo::Rotation::None;
+
+ std::unique_ptr<HWAccel> m_accel;
+ const AVCodec *m_codec = nullptr;
+ AVStream *m_stream = nullptr;
+ AVCodecContextUPtr m_codecContext;
+ std::unique_ptr<SwsContext, decltype(&sws_freeContext)> m_converter = { nullptr,
+ &sws_freeContext };
+ AVPixelFormat m_sourceFormat = AV_PIX_FMT_NONE;
+ AVPixelFormat m_sourceSWFormat = AV_PIX_FMT_NONE;
+ AVPixelFormat m_targetFormat = AV_PIX_FMT_NONE;
+ AVPixelFormat m_targetSWFormat = AV_PIX_FMT_NONE;
+ bool m_downloadFromHW = false;
+ bool m_uploadToHW = false;
+
+ AVRational m_codecFrameRate = { 0, 1 };
+
+ int64_t m_prevPacketDts = AV_NOPTS_VALUE;
+ int64_t m_packetDtsOffset = 0;
+};
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/CMakeLists.txt b/src/plugins/multimedia/gstreamer/CMakeLists.txt
index 3bce143f6..80fc1fce0 100644
--- a/src/plugins/multimedia/gstreamer/CMakeLists.txt
+++ b/src/plugins/multimedia/gstreamer/CMakeLists.txt
@@ -1,20 +1,26 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_find_package(EGL)
-qt_internal_add_plugin(QGstreamerMediaPlugin
- OUTPUT_NAME gstreamermediaplugin
- PLUGIN_TYPE multimedia
+qt_internal_add_module(QGstreamerMediaPluginPrivate
+ STATIC
+ INTERNAL_MODULE
SOURCES
audio/qgstreameraudiodevice.cpp audio/qgstreameraudiodevice_p.h
audio/qgstreameraudiosource.cpp audio/qgstreameraudiosource_p.h
audio/qgstreameraudiosink.cpp audio/qgstreameraudiosink_p.h
audio/qgstreameraudiodecoder.cpp audio/qgstreameraudiodecoder_p.h
- common/qgst_p.h
- common/qgstappsrc.cpp common/qgstappsrc_p.h
+ common/qglist_helper_p.h
+ common/qgst.cpp common/qgst_p.h
+ common/qgst_debug.cpp common/qgst_debug_p.h
+ common/qgst_handle_types_p.h
+ common/qgstappsource.cpp common/qgstappsource_p.h
common/qgstreameraudioinput.cpp common/qgstreameraudioinput_p.h
common/qgstreameraudiooutput.cpp common/qgstreameraudiooutput_p.h
common/qgstreamerbufferprobe.cpp common/qgstreamerbufferprobe_p.h
common/qgstreamermetadata.cpp common/qgstreamermetadata_p.h
- common/qgstreamermessage.cpp common/qgstreamermessage_p.h
+ common/qgstreamermessage_p.h
common/qgstreamermediaplayer.cpp common/qgstreamermediaplayer_p.h
common/qgstreamervideooutput.cpp common/qgstreamervideooutput_p.h
common/qgstreamervideooverlay.cpp common/qgstreamervideooverlay_p.h
@@ -24,33 +30,46 @@ qt_internal_add_plugin(QGstreamerMediaPlugin
common/qgstvideobuffer.cpp common/qgstvideobuffer_p.h
common/qgstvideorenderersink.cpp common/qgstvideorenderersink_p.h
common/qgstsubtitlesink.cpp common/qgstsubtitlesink_p.h
- qgstreamervideodevices.cpp qgstreamervideodevices_p.h
- qgstreamerformatinfo.cpp qgstreamerformatinfo_p.h
qgstreamerintegration.cpp qgstreamerintegration_p.h
+ qgstreamerformatinfo.cpp qgstreamerformatinfo_p.h
+ qgstreamervideodevices.cpp qgstreamervideodevices_p.h
mediacapture/qgstreamercamera.cpp mediacapture/qgstreamercamera_p.h
mediacapture/qgstreamerimagecapture.cpp mediacapture/qgstreamerimagecapture_p.h
mediacapture/qgstreamermediacapture.cpp mediacapture/qgstreamermediacapture_p.h
mediacapture/qgstreamermediaencoder.cpp mediacapture/qgstreamermediaencoder_p.h
+ NO_UNITY_BUILD_SOURCES
+ # Conflicts with macros defined in X11.h, and Xlib.h
+ common/qgstvideobuffer.cpp
+ common/qgstreamervideosink.cpp
+ NO_GENERATE_CPP_EXPORTS
DEFINES
GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26
- INCLUDE_DIRECTORIES
- audio
- common
- mediacapture
- LIBRARIES
+ PUBLIC_LIBRARIES
Qt::MultimediaPrivate
Qt::CorePrivate
GStreamer::GStreamer
GStreamer::App
)
-qt_internal_extend_target(QGstreamerMediaPlugin CONDITION QT_FEATURE_gstreamer_photography
- LIBRARIES
- -lgstphotography-1.0
+qt_internal_extend_target(QGstreamerMediaPluginPrivate CONDITION QT_FEATURE_gstreamer_photography
+ PUBLIC_LIBRARIES
+ GStreamer::Photography
)
-qt_internal_extend_target(QGstreamerMediaPlugin CONDITION QT_FEATURE_gstreamer_gl
- LIBRARIES
+qt_internal_extend_target(QGstreamerMediaPluginPrivate CONDITION QT_FEATURE_gstreamer_gl
+ PUBLIC_LIBRARIES
GStreamer::Gl
+ LIBRARIES
EGL::EGL
)
+
+qt_internal_add_plugin(QGstreamerMediaPlugin
+ OUTPUT_NAME gstreamermediaplugin
+ PLUGIN_TYPE multimedia
+ SOURCES
+ qgstreamerplugin.cpp
+ gstreamer.json
+ LIBRARIES
+ Qt::QGstreamerMediaPluginPrivate
+ Qt::MultimediaPrivate
+)
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp
index 779084c7f..0cfa28169 100644
--- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp
@@ -1,47 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define DEBUG_DECODER
-#include "qgstreameraudiodecoder_p.h"
-#include "qgstreamermessage_p.h"
+#include <audio/qgstreameraudiodecoder_p.h>
-#include <qgstutils_p.h>
+#include <common/qgstreamermessage_p.h>
+#include <common/qgst_debug_p.h>
+#include <common/qgstutils_p.h>
#include <gst/gstvalue.h>
#include <gst/base/gstbasesrc.h>
@@ -54,11 +19,14 @@
#include <QtCore/qdir.h>
#include <QtCore/qstandardpaths.h>
#include <QtCore/qurl.h>
+#include <QtCore/qloggingcategory.h>
#define MAX_BUFFERS_IN_QUEUE 4
QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(qLcGstreamerAudioDecoder, "qt.multimedia.gstreameraudiodecoder");
+
typedef enum {
GST_PLAY_FLAG_VIDEO = 0x00000001,
GST_PLAY_FLAG_AUDIO = 0x00000002,
@@ -72,32 +40,42 @@ typedef enum {
} GstPlayFlags;
+QMaybe<QPlatformAudioDecoder *> QGstreamerAudioDecoder::create(QAudioDecoder *parent)
+{
+ QGstElement audioconvert = QGstElement::createFromFactory("audioconvert", "audioconvert");
+ if (!audioconvert)
+ return errorMessageCannotFindElement("audioconvert");
+
+ QGstPipeline playbin = QGstPipeline::adopt(
+ GST_PIPELINE_CAST(QGstElement::createFromFactory("playbin", "playbin").element()));
+ if (!playbin)
+ return errorMessageCannotFindElement("playbin");
+
+ return new QGstreamerAudioDecoder(playbin, audioconvert, parent);
+}
-QGstreamerAudioDecoder::QGstreamerAudioDecoder(QAudioDecoder *parent)
+QGstreamerAudioDecoder::QGstreamerAudioDecoder(QGstPipeline playbin, QGstElement audioconvert,
+ QAudioDecoder *parent)
: QPlatformAudioDecoder(parent),
- m_playbin(GST_PIPELINE_CAST(QGstElement("playbin", "playbin").element()))
+ m_playbin(std::move(playbin)),
+ m_audioConvert(std::move(audioconvert))
{
- if (m_playbin.isNull()) {
- // ### set error
- return;
- }
-
// Sort out messages
m_playbin.installMessageFilter(this);
// Set the rest of the pipeline up
setAudioFlags(true);
- m_audioConvert = QGstElement("audioconvert", "audioconvert");
-
- m_outputBin = QGstBin("audio-output-bin");
+ m_outputBin = QGstBin::create("audio-output-bin");
m_outputBin.add(m_audioConvert);
// add ghostpad
m_outputBin.addGhostPad(m_audioConvert, "sink");
g_object_set(m_playbin.object(), "audio-sink", m_outputBin.element(), NULL);
- g_signal_connect(m_playbin.object(), "deep-notify::source", (GCallback) &QGstreamerAudioDecoder::configureAppSrcElement, (gpointer)this);
+
+ m_deepNotifySourceConnection = m_playbin.connect(
+ "deep-notify::source", (GCallback)&configureAppSrcElement, (gpointer)this);
// Set volume to 100%
gdouble volume = 1.0;
@@ -106,165 +84,160 @@ QGstreamerAudioDecoder::QGstreamerAudioDecoder(QAudioDecoder *parent)
QGstreamerAudioDecoder::~QGstreamerAudioDecoder()
{
- if (m_playbin.isNull())
- return;
-
stop();
+ m_playbin.removeMessageFilter(this);
+
#if QT_CONFIG(gstreamer_app)
delete m_appSrc;
#endif
}
#if QT_CONFIG(gstreamer_app)
-void QGstreamerAudioDecoder::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerAudioDecoder *self)
+void QGstreamerAudioDecoder::configureAppSrcElement([[maybe_unused]] GObject *object, GObject *orig,
+ [[maybe_unused]] GParamSpec *pspec,
+ QGstreamerAudioDecoder *self)
{
- Q_UNUSED(object);
- Q_UNUSED(pspec);
-
// In case we switch from appsrc to not
- if (!self->appsrc())
+ if (!self->m_appSrc)
return;
- GstElement *appsrc;
+ QGstElementHandle appsrc;
g_object_get(orig, "source", &appsrc, NULL);
- auto *qAppSrc = self->appsrc();
- qAppSrc->setExternalAppSrc(appsrc);
+ auto *qAppSrc = self->m_appSrc;
+ qAppSrc->setExternalAppSrc(QGstAppSrc{
+ qGstSafeCast<GstAppSrc>(appsrc.get()),
+ QGstAppSrc::NeedsRef, // CHECK: can we `release()`?
+ });
qAppSrc->setup(self->mDevice);
-
- g_object_unref(G_OBJECT(appsrc));
}
#endif
bool QGstreamerAudioDecoder::processBusMessage(const QGstreamerMessage &message)
{
- GstMessage* gm = message.rawMessage();
- if (gm) {
- if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_DURATION) {
- updateDuration();
- } else if (GST_MESSAGE_SRC(gm) == m_playbin.object()) {
- switch (GST_MESSAGE_TYPE(gm)) {
- case GST_MESSAGE_STATE_CHANGED:
- {
- GstState oldState;
- GstState newState;
- GstState pending;
-
- gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
-
-#ifdef DEBUG_DECODER
- QStringList states;
- states << "GST_STATE_VOID_PENDING" << "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING";
-
- qDebug() << QString("state changed: old: %1 new: %2 pending: %3") \
- .arg(states[oldState]) \
- .arg(states[newState]) \
- .arg(states[pending]) << "internal" << m_state;
-#endif
+ qCDebug(qLcGstreamerAudioDecoder) << "received bus message:" << message;
- bool isDecoding = false;
- switch (newState) {
- case GST_STATE_VOID_PENDING:
- case GST_STATE_NULL:
- case GST_STATE_READY:
- break;
- case GST_STATE_PLAYING:
- isDecoding = true;
- break;
- case GST_STATE_PAUSED:
- isDecoding = true;
-
- //gstreamer doesn't give a reliable indication the duration
- //information is ready, GST_MESSAGE_DURATION is not sent by most elements
- //the duration is queried up to 5 times with increasing delay
- m_durationQueries = 5;
- updateDuration();
- break;
- }
-
- setIsDecoding(isDecoding);
- }
- break;
-
- case GST_MESSAGE_EOS:
- finished();
- break;
-
- case GST_MESSAGE_ERROR: {
- GError *err;
- gchar *debug;
- gst_message_parse_error(gm, &err, &debug);
- if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND)
- processInvalidMedia(QAudioDecoder::FormatError, tr("Cannot play stream of type: <unknown>"));
- else
- processInvalidMedia(QAudioDecoder::ResourceError, QString::fromUtf8(err->message));
- qWarning() << "Error:" << QString::fromUtf8(err->message);
- g_error_free(err);
- g_free(debug);
- }
- break;
- case GST_MESSAGE_WARNING:
- {
- GError *err;
- gchar *debug;
- gst_message_parse_warning (gm, &err, &debug);
- qWarning() << "Warning:" << QString::fromUtf8(err->message);
- g_error_free (err);
- g_free (debug);
- }
- break;
-#ifdef DEBUG_DECODER
- case GST_MESSAGE_INFO:
- {
- GError *err;
- gchar *debug;
- gst_message_parse_info (gm, &err, &debug);
- qDebug() << "Info:" << QString::fromUtf8(err->message);
- g_error_free (err);
- g_free (debug);
- }
- break;
-#endif
- default:
- break;
- }
- } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) {
- GError *err;
- gchar *debug;
- gst_message_parse_error(gm, &err, &debug);
+ GstMessage *gm = message.message();
+
+ switch (message.type()) {
+ case GST_MESSAGE_DURATION: {
+ updateDuration();
+ return false;
+ }
+
+ case GST_MESSAGE_ERROR: {
+ qCDebug(qLcGstreamerAudioDecoder) << " error" << QCompactGstMessageAdaptor(message);
+
+ QUniqueGErrorHandle err;
+ QGString debug;
+ gst_message_parse_error(gm, &err, &debug);
+
+ if (message.source() == m_playbin) {
+ if (err.get()->domain == GST_STREAM_ERROR
+ && err.get()->code == GST_STREAM_ERROR_CODEC_NOT_FOUND)
+ processInvalidMedia(QAudioDecoder::FormatError,
+ tr("Cannot play stream of type: <unknown>"));
+ else
+ processInvalidMedia(QAudioDecoder::ResourceError,
+ QString::fromUtf8(err.get()->message));
+ } else {
QAudioDecoder::Error qerror = QAudioDecoder::ResourceError;
- if (err->domain == GST_STREAM_ERROR) {
- switch (err->code) {
- case GST_STREAM_ERROR_DECRYPT:
- case GST_STREAM_ERROR_DECRYPT_NOKEY:
- qerror = QAudioDecoder::AccessDeniedError;
- break;
- case GST_STREAM_ERROR_FORMAT:
- case GST_STREAM_ERROR_DEMUX:
- case GST_STREAM_ERROR_DECODE:
- case GST_STREAM_ERROR_WRONG_TYPE:
- case GST_STREAM_ERROR_TYPE_NOT_FOUND:
- case GST_STREAM_ERROR_CODEC_NOT_FOUND:
- qerror = QAudioDecoder::FormatError;
- break;
- default:
- break;
+ if (err.get()->domain == GST_STREAM_ERROR) {
+ switch (err.get()->code) {
+ case GST_STREAM_ERROR_DECRYPT:
+ case GST_STREAM_ERROR_DECRYPT_NOKEY:
+ qerror = QAudioDecoder::AccessDeniedError;
+ break;
+ case GST_STREAM_ERROR_FORMAT:
+ case GST_STREAM_ERROR_DEMUX:
+ case GST_STREAM_ERROR_DECODE:
+ case GST_STREAM_ERROR_WRONG_TYPE:
+ case GST_STREAM_ERROR_TYPE_NOT_FOUND:
+ case GST_STREAM_ERROR_CODEC_NOT_FOUND:
+ qerror = QAudioDecoder::FormatError;
+ break;
+ default:
+ break;
}
- } else if (err->domain == GST_CORE_ERROR) {
- switch (err->code) {
- case GST_CORE_ERROR_MISSING_PLUGIN:
- qerror = QAudioDecoder::FormatError;
- break;
- default:
- break;
+ } else if (err.get()->domain == GST_CORE_ERROR) {
+ switch (err.get()->code) {
+ case GST_CORE_ERROR_MISSING_PLUGIN:
+ qerror = QAudioDecoder::FormatError;
+ break;
+ default:
+ break;
}
}
- processInvalidMedia(qerror, QString::fromUtf8(err->message));
- g_error_free(err);
- g_free(debug);
+ processInvalidMedia(qerror, QString::fromUtf8(err.get()->message));
+ }
+ break;
+ }
+
+ default:
+ if (message.source() == m_playbin)
+ return handlePlaybinMessage(message);
+ }
+
+ return false;
+}
+
+bool QGstreamerAudioDecoder::handlePlaybinMessage(const QGstreamerMessage &message)
+{
+ GstMessage *gm = message.message();
+
+ switch (GST_MESSAGE_TYPE(gm)) {
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState oldState;
+ GstState newState;
+ GstState pending;
+
+ gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
+
+ bool isDecoding = false;
+ switch (newState) {
+ case GST_STATE_VOID_PENDING:
+ case GST_STATE_NULL:
+ case GST_STATE_READY:
+ break;
+ case GST_STATE_PLAYING:
+ isDecoding = true;
+ break;
+ case GST_STATE_PAUSED:
+ isDecoding = true;
+
+ // gstreamer doesn't give a reliable indication the duration
+ // information is ready, GST_MESSAGE_DURATION is not sent by most elements
+ // the duration is queried up to 5 times with increasing delay
+ m_durationQueries = 5;
+ updateDuration();
+ break;
}
+
+ setIsDecoding(isDecoding);
+ break;
+ };
+
+ case GST_MESSAGE_EOS:
+ m_playbin.setState(GST_STATE_NULL);
+ finished();
+ break;
+
+ case GST_MESSAGE_ERROR:
+ Q_UNREACHABLE_RETURN(false); // handled in processBusMessage
+
+ case GST_MESSAGE_WARNING:
+ qCWarning(qLcGstreamerAudioDecoder) << "Warning:" << QCompactGstMessageAdaptor(message);
+ break;
+
+ case GST_MESSAGE_INFO: {
+ if (qLcGstreamerAudioDecoder().isDebugEnabled())
+ qCWarning(qLcGstreamerAudioDecoder) << "Info:" << QCompactGstMessageAdaptor(message);
+ break;
+ }
+ default:
+ break;
}
return false;
@@ -285,7 +258,7 @@ void QGstreamerAudioDecoder::setSource(const QUrl &fileName)
bool isSignalRequired = (mSource != fileName);
mSource = fileName;
if (isSignalRequired)
- emit sourceChanged();
+ sourceChanged();
}
QIODevice *QGstreamerAudioDecoder::sourceDevice() const
@@ -300,16 +273,11 @@ void QGstreamerAudioDecoder::setSourceDevice(QIODevice *device)
bool isSignalRequired = (mDevice != device);
mDevice = device;
if (isSignalRequired)
- emit sourceChanged();
+ sourceChanged();
}
void QGstreamerAudioDecoder::start()
{
- if (m_playbin.isNull()) {
- processInvalidMedia(QAudioDecoder::ResourceError, QLatin1String("Playbin element is not valid"));
- return;
- }
-
addAppSink();
if (!mSource.isEmpty()) {
@@ -321,8 +289,15 @@ void QGstreamerAudioDecoder::start()
return;
}
- if (!m_appSrc)
- m_appSrc = new QGstAppSrc(this);
+ if (!m_appSrc) {
+ auto maybeAppSrc = QGstAppSource::create(this);
+ if (maybeAppSrc) {
+ m_appSrc = maybeAppSrc.value();
+ } else {
+ processInvalidMedia(QAudioDecoder::ResourceError, maybeAppSrc.error());
+ return;
+ }
+ }
m_playbin.set("uri", "appsrc://");
} else {
@@ -333,12 +308,12 @@ void QGstreamerAudioDecoder::start()
if (m_appSink) {
if (mFormat.isValid()) {
setAudioFlags(false);
- QGstMutableCaps caps = QGstUtils::capsForAudioFormat(mFormat);
- gst_app_sink_set_caps(m_appSink, caps.get());
+ auto caps = QGstUtils::capsForAudioFormat(mFormat);
+ m_appSink.setCaps(caps);
} else {
// We want whatever the native audio format is
setAudioFlags(true);
- gst_app_sink_set_caps(m_appSink, nullptr);
+ m_appSink.setCaps({});
}
}
@@ -351,26 +326,24 @@ void QGstreamerAudioDecoder::start()
void QGstreamerAudioDecoder::stop()
{
- if (m_playbin.isNull())
- return;
-
m_playbin.setState(GST_STATE_NULL);
+ m_currentSessionId += 1;
removeAppSink();
// GStreamer thread is stopped. Can safely access m_buffersAvailable
if (m_buffersAvailable != 0) {
m_buffersAvailable = 0;
- emit bufferAvailableChanged(false);
+ bufferAvailableChanged(false);
}
if (m_position != -1) {
m_position = -1;
- emit positionChanged(m_position);
+ positionChanged(m_position);
}
if (m_duration != -1) {
m_duration = -1;
- emit durationChanged(m_duration);
+ durationChanged(m_duration);
}
setIsDecoding(false);
@@ -385,7 +358,7 @@ void QGstreamerAudioDecoder::setAudioFormat(const QAudioFormat &format)
{
if (mFormat != format) {
mFormat = format;
- emit formatChanged(mFormat);
+ formatChanged(mFormat);
}
}
@@ -393,56 +366,38 @@ QAudioBuffer QGstreamerAudioDecoder::read()
{
QAudioBuffer audioBuffer;
- int buffersAvailable;
- {
- QMutexLocker locker(&m_buffersMutex);
- buffersAvailable = m_buffersAvailable;
-
- // need to decrement before pulling a buffer
- // to make sure assert in QGstreamerAudioDecoderControl::new_buffer works
- m_buffersAvailable--;
- }
-
-
- if (buffersAvailable) {
- if (buffersAvailable == 1)
- emit bufferAvailableChanged(false);
-
- const char* bufferData = nullptr;
- int bufferSize = 0;
-
- GstSample *sample = gst_app_sink_pull_sample(m_appSink);
- GstBuffer *buffer = gst_sample_get_buffer(sample);
- GstMapInfo mapInfo;
- gst_buffer_map(buffer, &mapInfo, GST_MAP_READ);
- bufferData = (const char*)mapInfo.data;
- bufferSize = mapInfo.size;
- QAudioFormat format = QGstUtils::audioFormatForSample(sample);
-
- if (format.isValid()) {
- // XXX At the moment we have to copy data from GstBuffer into QAudioBuffer.
- // We could improve performance by implementing QAbstractAudioBuffer for GstBuffer.
- qint64 position = getPositionFromBuffer(buffer);
- audioBuffer = QAudioBuffer(QByteArray((const char*)bufferData, bufferSize), format, position);
- position /= 1000; // convert to milliseconds
- if (position != m_position) {
- m_position = position;
- emit positionChanged(m_position);
- }
+ if (m_buffersAvailable == 0)
+ return audioBuffer;
+
+ m_buffersAvailable -= 1;
+
+ if (m_buffersAvailable == 0)
+ bufferAvailableChanged(false);
+
+ QGstSampleHandle sample = m_appSink.pullSample();
+ GstBuffer *buffer = gst_sample_get_buffer(sample.get());
+ GstMapInfo mapInfo;
+ gst_buffer_map(buffer, &mapInfo, GST_MAP_READ);
+ const char *bufferData = (const char *)mapInfo.data;
+ int bufferSize = mapInfo.size;
+ QAudioFormat format = QGstUtils::audioFormatForSample(sample.get());
+
+ if (format.isValid()) {
+ // XXX At the moment we have to copy data from GstBuffer into QAudioBuffer.
+ // We could improve performance by implementing QAbstractAudioBuffer for GstBuffer.
+ qint64 position = getPositionFromBuffer(buffer);
+ audioBuffer = QAudioBuffer(QByteArray(bufferData, bufferSize), format, position);
+ position /= 1000; // convert to milliseconds
+ if (position != m_position) {
+ m_position = position;
+ positionChanged(m_position);
}
- gst_buffer_unmap(buffer, &mapInfo);
- gst_sample_unref(sample);
}
+ gst_buffer_unmap(buffer, &mapInfo);
return audioBuffer;
}
-bool QGstreamerAudioDecoder::bufferAvailable() const
-{
- QMutexLocker locker(&m_buffersMutex);
- return m_buffersAvailable > 0;
-}
-
qint64 QGstreamerAudioDecoder::position() const
{
return m_position;
@@ -456,33 +411,35 @@ qint64 QGstreamerAudioDecoder::duration() const
void QGstreamerAudioDecoder::processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString)
{
stop();
- emit error(int(errorCode), errorString);
+ error(int(errorCode), errorString);
}
-GstFlowReturn QGstreamerAudioDecoder::new_sample(GstAppSink *, gpointer user_data)
+GstFlowReturn QGstreamerAudioDecoder::newSample(GstAppSink *)
{
- // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()."
- QGstreamerAudioDecoder *decoder = reinterpret_cast<QGstreamerAudioDecoder*>(user_data);
-
- int buffersAvailable;
- {
- QMutexLocker locker(&decoder->m_buffersMutex);
- buffersAvailable = decoder->m_buffersAvailable;
- decoder->m_buffersAvailable++;
- Q_ASSERT(decoder->m_buffersAvailable <= MAX_BUFFERS_IN_QUEUE);
- }
+ // "Note that the preroll buffer will also be returned as the first buffer when calling
+ // gst_app_sink_pull_buffer()."
+
+ QMetaObject::invokeMethod(this, [this, sessionId = m_currentSessionId] {
+ if (sessionId != m_currentSessionId)
+ return; // stop()ed before message is executed
+
+ m_buffersAvailable += 1;
+ bufferAvailableChanged(true);
+ bufferReady();
+ });
- if (!buffersAvailable)
- decoder->bufferAvailableChanged(true);
- decoder->bufferReady();
return GST_FLOW_OK;
}
-void QGstreamerAudioDecoder::setAudioFlags(bool wantNativeAudio)
+GstFlowReturn QGstreamerAudioDecoder::new_sample(GstAppSink *sink, gpointer user_data)
{
- if (m_playbin.isNull())
- return;
+ QGstreamerAudioDecoder *decoder = reinterpret_cast<QGstreamerAudioDecoder *>(user_data);
+ qCDebug(qLcGstreamerAudioDecoder) << "QGstreamerAudioDecoder::new_sample";
+ return decoder->newSample(sink);
+}
+void QGstreamerAudioDecoder::setAudioFlags(bool wantNativeAudio)
+{
int flags = m_playbin.getInt("flags");
// make sure not to use GST_PLAY_FLAG_NATIVE_AUDIO unless desired
// it prevents audio format conversion
@@ -498,17 +455,18 @@ void QGstreamerAudioDecoder::addAppSink()
if (m_appSink)
return;
- m_appSink = (GstAppSink*)gst_element_factory_make("appsink", nullptr);
-
- GstAppSinkCallbacks callbacks;
- memset(&callbacks, 0, sizeof(callbacks));
- callbacks.new_sample = &new_sample;
- gst_app_sink_set_callbacks(m_appSink, &callbacks, this, nullptr);
- gst_app_sink_set_max_buffers(m_appSink, MAX_BUFFERS_IN_QUEUE);
- gst_base_sink_set_sync(GST_BASE_SINK(m_appSink), FALSE);
-
- gst_bin_add(m_outputBin.bin(), GST_ELEMENT(m_appSink));
- gst_element_link(m_audioConvert.element(), GST_ELEMENT(m_appSink));
+ qCDebug(qLcGstreamerAudioDecoder) << "QGstreamerAudioDecoder::addAppSink";
+ m_appSink = QGstAppSink::create("decoderAppSink");
+ GstAppSinkCallbacks callbacks{};
+ callbacks.new_sample = new_sample;
+ m_appSink.setCallbacks(callbacks, this, nullptr);
+ gst_app_sink_set_max_buffers(m_appSink.appSink(), MAX_BUFFERS_IN_QUEUE);
+ gst_base_sink_set_sync(m_appSink.baseSink(), FALSE);
+
+ QGstPipeline::modifyPipelineWhileNotRunning(m_playbin.getPipeline(), [&] {
+ m_outputBin.add(m_appSink);
+ qLinkGstElements(m_audioConvert, m_appSink);
+ });
}
void QGstreamerAudioDecoder::removeAppSink()
@@ -516,22 +474,22 @@ void QGstreamerAudioDecoder::removeAppSink()
if (!m_appSink)
return;
- gst_element_unlink(m_audioConvert.element(), GST_ELEMENT(m_appSink));
- gst_bin_remove(m_outputBin.bin(), GST_ELEMENT(m_appSink));
+ qCDebug(qLcGstreamerAudioDecoder) << "QGstreamerAudioDecoder::removeAppSink";
- m_appSink = nullptr;
+ QGstPipeline::modifyPipelineWhileNotRunning(m_playbin.getPipeline(), [&] {
+ qUnlinkGstElements(m_audioConvert, m_appSink);
+ m_outputBin.stopAndRemoveElements(m_appSink);
+ });
+ m_appSink = {};
}
void QGstreamerAudioDecoder::updateDuration()
{
- int duration = -1;
-
- if (!m_playbin.isNull())
- duration = m_playbin.duration() / 1000000;
+ int duration = m_playbin.duration() / 1000000;
if (m_duration != duration) {
m_duration = duration;
- emit durationChanged(m_duration);
+ durationChanged(m_duration);
}
if (m_duration > 0)
@@ -540,7 +498,7 @@ void QGstreamerAudioDecoder::updateDuration()
if (m_durationQueries > 0) {
//increase delay between duration requests
int delay = 25 << (5 - m_durationQueries);
- QTimer::singleShot(delay, this, SLOT(updateDuration()));
+ QTimer::singleShot(delay, this, &QGstreamerAudioDecoder::updateDuration);
m_durationQueries--;
}
}
@@ -556,3 +514,5 @@ qint64 QGstreamerAudioDecoder::getPositionFromBuffer(GstBuffer* buffer)
}
QT_END_NAMESPACE
+
+#include "moc_qgstreameraudiodecoder_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h
index 85ff53041..eba1025fa 100644
--- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERAUDIODECODERCONTROL_H
#define QGSTREAMERAUDIODECODERCONTROL_H
@@ -51,34 +15,33 @@
// We mean it.
//
+#include <QtMultimedia/private/qmultimediautils_p.h>
+#include <QtMultimedia/private/qplatformaudiodecoder_p.h>
#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <QObject>
+#include <QtMultimedia/qaudiodecoder.h>
+#include <QtCore/qobject.h>
#include <QtCore/qmutex.h>
#include <QtCore/qurl.h>
-#include "private/qplatformaudiodecoder_p.h"
-#include <qgstpipeline_p.h>
-#include "qaudiodecoder.h"
+#include <common/qgstpipeline_p.h>
+#include <common/qgst_p.h>
#if QT_CONFIG(gstreamer_app)
-#include <qgstappsrc_p.h>
+# include <common/qgstappsource_p.h>
#endif
-#include <qgst_p.h>
#include <gst/app/gstappsink.h>
QT_BEGIN_NAMESPACE
class QGstreamerMessage;
-class QGstreamerAudioDecoder
- : public QPlatformAudioDecoder,
- public QGstreamerBusMessageFilter
+class QGstreamerAudioDecoder final : public QPlatformAudioDecoder, public QGstreamerBusMessageFilter
{
Q_OBJECT
public:
- QGstreamerAudioDecoder(QAudioDecoder *parent);
+ static QMaybe<QPlatformAudioDecoder *> create(QAudioDecoder *parent);
virtual ~QGstreamerAudioDecoder();
QUrl source() const override;
@@ -94,7 +57,6 @@ public:
void setAudioFormat(const QAudioFormat &format) override;
QAudioBuffer read() override;
- bool bufferAvailable() const override;
qint64 position() const override;
qint64 duration() const override;
@@ -102,41 +64,48 @@ public:
// GStreamerBusMessageFilter interface
bool processBusMessage(const QGstreamerMessage &message) override;
-#if QT_CONFIG(gstreamer_app)
- QGstAppSrc *appsrc() const { return m_appSrc; }
- static void configureAppSrcElement(GObject*, GObject*, GParamSpec*, QGstreamerAudioDecoder *_this);
-#endif
-
- static GstFlowReturn new_sample(GstAppSink *sink, gpointer user_data);
-
private slots:
void updateDuration();
private:
+ QGstreamerAudioDecoder(QGstPipeline playbin, QGstElement audioconvert, QAudioDecoder *parent);
+
+#if QT_CONFIG(gstreamer_app)
+ static GstFlowReturn new_sample(GstAppSink *sink, gpointer user_data);
+ GstFlowReturn newSample(GstAppSink *sink);
+
+ static void configureAppSrcElement(GObject *, GObject *, GParamSpec *,
+ QGstreamerAudioDecoder *_this);
+#endif
+
void setAudioFlags(bool wantNativeAudio);
void addAppSink();
void removeAppSink();
- void processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString);
+ bool handlePlaybinMessage(const QGstreamerMessage &);
+
+ void processInvalidMedia(QAudioDecoder::Error errorCode, const QString &errorString);
static qint64 getPositionFromBuffer(GstBuffer* buffer);
QGstPipeline m_playbin;
QGstBin m_outputBin;
QGstElement m_audioConvert;
- GstAppSink *m_appSink = nullptr;
- QGstAppSrc *m_appSrc = nullptr;
+ QGstAppSink m_appSink;
+ QGstAppSource *m_appSrc = nullptr;
QUrl mSource;
QIODevice *mDevice = nullptr;
QAudioFormat mFormat;
- mutable QMutex m_buffersMutex;
int m_buffersAvailable = 0;
-
qint64 m_position = -1;
qint64 m_duration = -1;
int m_durationQueries = 0;
+
+ qint32 m_currentSessionId{};
+
+ QGObjectHandlerScopedConnection m_deepNotifySourceConnection;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp
index aaac46efd..2c6b57e55 100644
--- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice.cpp
@@ -1,61 +1,28 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgstreameraudiodevice_p.h"
-#include <qgstutils_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstutils_p.h>
#include <private/qplatformmediaintegration_p.h>
QT_BEGIN_NAMESPACE
-QGStreamerAudioDeviceInfo::QGStreamerAudioDeviceInfo(GstDevice *d, const QByteArray &device, QAudioDevice::Mode mode)
+QGStreamerAudioDeviceInfo::QGStreamerAudioDeviceInfo(GstDevice *d, const QByteArray &device,
+ QAudioDevice::Mode mode)
: QAudioDevicePrivate(device, mode),
- gstDevice(d)
+ gstDevice{
+ d,
+ QGstDeviceHandle::NeedsRef,
+ }
{
- Q_ASSERT(gstDevice);
- gst_object_ref(gstDevice);
+ QGString name{
+ gst_device_get_display_name(gstDevice.get()),
+ };
+ description = name.toQString();
- auto *n = gst_device_get_display_name(gstDevice);
- description = QString::fromUtf8(n);
- g_free(n);
-
- QGstCaps caps = gst_device_get_caps(gstDevice);
+ auto caps = QGstCaps(gst_device_get_caps(gstDevice.get()), QGstCaps::HasRef);
int size = caps.size();
for (int i = 0; i < size; ++i) {
auto c = caps.at(i);
@@ -82,10 +49,6 @@ QGStreamerAudioDeviceInfo::QGStreamerAudioDeviceInfo(GstDevice *d, const QByteAr
preferredFormat.setSampleFormat(f);
}
-QGStreamerAudioDeviceInfo::~QGStreamerAudioDeviceInfo()
-{
- if (gstDevice)
- gst_object_unref(gstDevice);
-}
+QGStreamerAudioDeviceInfo::~QGStreamerAudioDeviceInfo() = default;
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h
index 9da109e38..dee0c40bc 100644
--- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodevice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERAUDIODEVICEINFO_H
#define QGSTREAMERAUDIODEVICEINFO_H
@@ -59,6 +23,8 @@
#include "qaudiodevice.h"
#include <private/qaudiodevice_p.h>
+#include <common/qgst_handle_types_p.h>
+
#include <gst/gst.h>
QT_BEGIN_NAMESPACE
@@ -69,7 +35,7 @@ public:
QGStreamerAudioDeviceInfo(GstDevice *gstDevice, const QByteArray &device, QAudioDevice::Mode mode);
~QGStreamerAudioDeviceInfo();
- GstDevice *gstDevice = nullptr;
+ QGstDeviceHandle gstDevice;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp
index f3602186c..6fd972524 100644
--- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink.cpp
@@ -1,75 +1,60 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmath.h>
-#include <private/qaudiohelpers_p.h>
+#include <QtMultimedia/private/qaudiohelpers_p.h>
-#include "qgstreameraudiosink_p.h"
-#include "qgstreameraudiodevice_p.h"
#include <sys/types.h>
#include <unistd.h>
-#include <qgstpipeline_p.h>
-#include <qgstappsrc_p.h>
+#include <audio/qgstreameraudiosink_p.h>
+#include <audio/qgstreameraudiodevice_p.h>
+#include <common/qgst_debug_p.h>
+#include <common/qgstappsource_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreamermessage_p.h>
+#include <common/qgstutils_p.h>
-#include <qgstutils_p.h>
-#include <qgstreamermessage_p.h>
+#include <utility>
QT_BEGIN_NAMESPACE
-QGStreamerAudioSink::QGStreamerAudioSink(const QAudioDevice &device)
- : m_device(device.id()),
- gstPipeline("pipeline")
+QMaybe<QPlatformAudioSink *> QGStreamerAudioSink::create(const QAudioDevice &device, QObject *parent)
+{
+ auto maybeAppSrc = QGstAppSource::create();
+ if (!maybeAppSrc)
+ return maybeAppSrc.error();
+
+ QGstElement audioconvert = QGstElement::createFromFactory("audioconvert", "conv");
+ if (!audioconvert)
+ return errorMessageCannotFindElement("audioconvert");
+
+ QGstElement volume = QGstElement::createFromFactory("volume", "volume");
+ if (!volume)
+ return errorMessageCannotFindElement("volume");
+
+ return new QGStreamerAudioSink(device, maybeAppSrc.value(), audioconvert, volume, parent);
+}
+
+QGStreamerAudioSink::QGStreamerAudioSink(const QAudioDevice &device, QGstAppSource *appsrc,
+ QGstElement audioconvert, QGstElement volume,
+ QObject *parent)
+ : QPlatformAudioSink(parent),
+ m_device(device.id()),
+ gstPipeline(QGstPipeline::create("audioSinkPipeline")),
+ gstVolume(std::move(volume)),
+ m_appSrc(appsrc)
{
gstPipeline.installMessageFilter(this);
- m_appSrc = new QGstAppSrc;
- connect(m_appSrc, &QGstAppSrc::bytesProcessed, this, &QGStreamerAudioSink::bytesProcessedByAppSrc);
- connect(m_appSrc, &QGstAppSrc::noMoreData, this, &QGStreamerAudioSink::needData);
+ connect(m_appSrc, &QGstAppSource::bytesProcessed, this, &QGStreamerAudioSink::bytesProcessedByAppSrc);
+ connect(m_appSrc, &QGstAppSource::noMoreData, this, &QGStreamerAudioSink::needData);
gstAppSrc = m_appSrc->element();
- // gstDecodeBin = gst_element_factory_make ("decodebin", "dec");
- QGstElement queue("queue", "queue");
- QGstElement conv("audioconvert", "conv");
- gstVolume = QGstElement("volume", "volume");
+ QGstElement queue = QGstElement::createFromFactory("queue", "audioSinkQueue");
+
if (m_volume != 1.)
gstVolume.set("volume", m_volume);
@@ -77,15 +62,17 @@ QGStreamerAudioSink::QGStreamerAudioSink(const QAudioDevice &device)
// g_signal_connect (gstDecodeBin, "pad-added", (GCallback) padAdded, conv);
const auto *audioInfo = static_cast<const QGStreamerAudioDeviceInfo *>(device.handle());
- gstOutput = gst_device_create_element(audioInfo->gstDevice, nullptr);
+ gstOutput = QGstElement::createFromDevice(audioInfo->gstDevice, nullptr);
- gstPipeline.add(gstAppSrc, queue, /*gstDecodeBin, */ conv, gstVolume, gstOutput);
- gstAppSrc.link(queue, conv, gstVolume, gstOutput);
+ gstPipeline.add(gstAppSrc, queue, /*gstDecodeBin, */ audioconvert, gstVolume, gstOutput);
+ qLinkGstElements(gstAppSrc, queue, audioconvert, gstVolume, gstOutput);
}
QGStreamerAudioSink::~QGStreamerAudioSink()
{
close();
+ gstPipeline.removeMessageFilter(this);
+
gstPipeline = {};
gstVolume = {};
gstAppSrc = {};
@@ -175,9 +162,8 @@ static void padAdded(GstElement *element, GstPad *pad, gpointer data)
{
GstElement *other = static_cast<GstElement *>(data);
- gchar *name = gst_pad_get_name(pad);
+ QGString name { gst_pad_get_name(pad)};
qDebug("A new pad %s was created for %s\n", name, gst_element_get_name(element));
- g_free(name);
qDebug("element %s will be linked to %s\n",
gst_element_get_name(element),
@@ -188,22 +174,14 @@ static void padAdded(GstElement *element, GstPad *pad, gpointer data)
bool QGStreamerAudioSink::processBusMessage(const QGstreamerMessage &message)
{
- auto *msg = message.rawMessage();
+ auto *msg = message.message();
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_EOS:
setState(QAudio::IdleState);
break;
case GST_MESSAGE_ERROR: {
setError(QAudio::IOError);
- gchar *debug;
- GError *error;
-
- gst_message_parse_error (msg, &error, &debug);
- g_free (debug);
-
- qDebug("Error: %s\n", error->message);
- g_error_free (error);
-
+ qDebug() << "Error:" << QCompactGstMessageAdaptor(message);
break;
}
default:
@@ -310,7 +288,7 @@ void QGStreamerAudioSink::resume()
m_appSrc->resume();
gstPipeline.setState(GST_STATE_PLAYING);
- setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState);
+ setState(m_suspendedInState);
setError(QAudio::NoError);
}
}
@@ -328,6 +306,7 @@ QAudioFormat QGStreamerAudioSink::format() const
void QGStreamerAudioSink::suspend()
{
if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) {
+ m_suspendedInState = m_deviceState;
setError(QAudio::NoError);
setState(QAudio::SuspendedState);
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h
index 080fe1cbf..1aadb2290 100644
--- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAUDIOOUTPUTGSTREAMER_H
#define QAUDIOOUTPUTGSTREAMER_H
@@ -62,13 +26,14 @@
#include "qaudio.h"
#include "qaudiodevice.h"
#include <private/qaudiosystem_p.h>
+#include <private/qmultimediautils_p.h>
-#include <qgst_p.h>
-#include <qgstpipeline_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
QT_BEGIN_NAMESPACE
-class QGstAppSrc;
+class QGstAppSource;
class QGStreamerAudioSink
: public QPlatformAudioSink,
@@ -78,7 +43,7 @@ class QGStreamerAudioSink
Q_OBJECT
public:
- QGStreamerAudioSink(const QAudioDevice &device);
+ static QMaybe<QPlatformAudioSink *> create(const QAudioDevice &device, QObject *parent);
~QGStreamerAudioSink();
void start(QIODevice *device) override;
@@ -104,6 +69,9 @@ private Q_SLOTS:
void needData();
private:
+ QGStreamerAudioSink(const QAudioDevice &device, QGstAppSource *appsrc, QGstElement audioconvert,
+ QGstElement volume, QObject *parent);
+
void setState(QAudio::State state);
void setError(QAudio::Error error);
@@ -118,10 +86,10 @@ private:
QAudioFormat m_format;
QAudio::Error m_errorState = QAudio::NoError;
QAudio::State m_deviceState = QAudio::StoppedState;
+ QAudio::State m_suspendedInState = QAudio::SuspendedState;
bool m_pullMode = true;
bool m_opened = false;
QIODevice *m_audioSource = nullptr;
- QTimer m_periodTimer;
int m_bufferSize = 0;
qint64 m_bytesProcessed = 0;
QElapsedTimer m_timeStamp;
@@ -132,7 +100,7 @@ private:
QGstElement gstOutput;
QGstElement gstVolume;
QGstElement gstAppSrc;
- QGstAppSrc *m_appSrc = nullptr;
+ QGstAppSource *m_appSrc = nullptr;
};
class GStreamerOutputPrivate : public QIODevice
@@ -141,8 +109,7 @@ class GStreamerOutputPrivate : public QIODevice
Q_OBJECT
public:
- GStreamerOutputPrivate(QGStreamerAudioSink *audio);
- virtual ~GStreamerOutputPrivate() {}
+ explicit GStreamerOutputPrivate(QGStreamerAudioSink *audio);
protected:
qint64 readData(char *data, qint64 len) override;
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp
index ffa402627..829d116a2 100644
--- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource.cpp
@@ -1,61 +1,27 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmath.h>
-#include <private/qaudiohelpers_p.h>
+#include <QtMultimedia/private/qaudiohelpers_p.h>
#include "qgstreameraudiosource_p.h"
#include "qgstreameraudiodevice_p.h"
+#include "common/qgst_p.h"
+#include "common/qgst_debug_p.h"
+
#include <sys/types.h>
#include <unistd.h>
-#include <gst/gst.h>
Q_DECLARE_OPAQUE_POINTER(GstSample *);
Q_DECLARE_METATYPE(GstSample *);
QT_BEGIN_NAMESPACE
-
-QGStreamerAudioSource::QGStreamerAudioSource(const QAudioDevice &device)
- : m_info(device),
+QGStreamerAudioSource::QGStreamerAudioSource(const QAudioDevice &device, QObject *parent)
+ : QPlatformAudioSource(parent),
+ m_info(device),
m_device(device.id())
{
qRegisterMetaType<GstSample *>();
@@ -163,7 +129,7 @@ bool QGStreamerAudioSource::open()
return false;
}
- gstInput = QGstElement(gst_device_create_element(deviceInfo->gstDevice, nullptr));
+ gstInput = QGstElement::createFromDevice(deviceInfo->gstDevice);
if (gstInput.isNull()) {
setError(QAudio::OpenError);
setState(QAudio::StoppedState);
@@ -184,7 +150,7 @@ bool QGStreamerAudioSource::open()
qDebug() << "Caps: " << gst_caps_to_string(gstCaps);
#endif
- gstPipeline = QGstPipeline("pipeline");
+ gstPipeline = QGstPipeline::create("audioSourcePipeline");
auto *gstBus = gst_pipeline_get_bus(gstPipeline.pipeline());
gst_bus_add_watch(gstBus, &QGStreamerAudioSource::busMessage, this);
@@ -193,13 +159,14 @@ bool QGStreamerAudioSource::open()
gstAppSink = createAppSink();
gstAppSink.set("caps", gstCaps);
- QGstElement conv("audioconvert", "conv");
- gstVolume = QGstElement("volume", "volume");
+ QGstElement conv = QGstElement::createFromFactory("audioconvert", "conv");
+ gstVolume = QGstElement::createFromFactory("volume", "volume");
+ Q_ASSERT(gstVolume);
if (m_volume != 1.)
gstVolume.set("volume", m_volume);
gstPipeline.add(gstInput, gstVolume, conv, gstAppSink);
- gstInput.link(gstVolume, conv, gstAppSink);
+ qLinkGstElements(gstInput, gstVolume, conv, gstAppSink);
gstPipeline.setState(GST_STATE_PLAYING);
@@ -239,15 +206,7 @@ gboolean QGStreamerAudioSource::busMessage(GstBus *, GstMessage *msg, gpointer u
break;
case GST_MESSAGE_ERROR: {
input->setError(QAudio::IOError);
- gchar *debug;
- GError *error;
-
- gst_message_parse_error (msg, &error, &debug);
- g_free (debug);
-
- qDebug("Error: %s\n", error->message);
- g_error_free (error);
-
+ qDebug() << "Error:" << QCompactGstMessageAdaptor(msg);
break;
}
default:
@@ -318,26 +277,24 @@ void QGStreamerAudioSource::reset()
//#define MAX_BUFFERS_IN_QUEUE 4
-QGstElement QGStreamerAudioSource::createAppSink()
+QGstAppSink QGStreamerAudioSource::createAppSink()
{
- QGstElement sink("appsink", "appsink");
- GstAppSink *appSink = reinterpret_cast<GstAppSink *>(sink.element());
+ QGstAppSink sink = QGstAppSink::create("appsink");
- GstAppSinkCallbacks callbacks;
- memset(&callbacks, 0, sizeof(callbacks));
- callbacks.eos = &eos;
- callbacks.new_sample = &new_sample;
- gst_app_sink_set_callbacks(appSink, &callbacks, this, nullptr);
-// gst_app_sink_set_max_buffers(appSink, MAX_BUFFERS_IN_QUEUE);
- gst_base_sink_set_sync(GST_BASE_SINK(appSink), FALSE);
+ GstAppSinkCallbacks callbacks{};
+ callbacks.eos = eos;
+ callbacks.new_sample = new_sample;
+ sink.setCallbacks(callbacks, this, nullptr);
+ // gst_app_sink_set_max_buffers(sink.appSink(), MAX_BUFFERS_IN_QUEUE);
+ gst_base_sink_set_sync(sink.baseSink(), FALSE);
return sink;
}
-void QGStreamerAudioSource::newDataAvailable(GstSample *sample)
+void QGStreamerAudioSource::newDataAvailable(QGstSampleHandle sample)
{
if (m_audioSink) {
- GstBuffer *buffer = gst_sample_get_buffer(sample);
+ GstBuffer *buffer = gst_sample_get_buffer(sample.get());
GstMapInfo mapInfo;
gst_buffer_map(buffer, &mapInfo, GST_MAP_READ);
const char *bufferData = (const char*)mapInfo.data;
@@ -354,8 +311,6 @@ void QGStreamerAudioSource::newDataAvailable(GstSample *sample)
gst_buffer_unmap(buffer, &mapInfo);
}
-
- gst_sample_unref(sample);
}
GstFlowReturn QGStreamerAudioSource::new_sample(GstAppSink *sink, gpointer user_data)
@@ -363,8 +318,14 @@ GstFlowReturn QGStreamerAudioSource::new_sample(GstAppSink *sink, gpointer user_
// "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()."
QGStreamerAudioSource *control = static_cast<QGStreamerAudioSource*>(user_data);
- GstSample *sample = gst_app_sink_pull_sample(sink);
- QMetaObject::invokeMethod(control, "newDataAvailable", Qt::AutoConnection, Q_ARG(GstSample *, sample));
+ QGstSampleHandle sample{
+ gst_app_sink_pull_sample(sink),
+ QGstSampleHandle::HasRef,
+ };
+
+ QMetaObject::invokeMethod(control, [control, sample = std::move(sample)]() mutable {
+ control->newDataAvailable(std::move(sample));
+ });
return GST_FLOW_OK;
}
@@ -377,7 +338,7 @@ void QGStreamerAudioSource::eos(GstAppSink *, gpointer user_data)
GStreamerInputPrivate::GStreamerInputPrivate(QGStreamerAudioSource *audio)
{
- m_audioDevice = qobject_cast<QGStreamerAudioSource*>(audio);
+ m_audioDevice = audio;
}
qint64 GStreamerInputPrivate::readData(char *data, qint64 len)
@@ -403,5 +364,3 @@ qint64 GStreamerInputPrivate::bytesAvailable() const
QT_END_NAMESPACE
-
-#include "moc_qgstreameraudiosource_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource_p.h b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource_p.h
index 5a04702ca..9021f1ddd 100644
--- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource_p.h
+++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiosource_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
@@ -65,8 +29,9 @@
#include "qaudiodevice.h"
#include <private/qaudiosystem_p.h>
-#include <qgstutils_p.h>
-#include <qgstpipeline_p.h>
+#include <common/qgstutils_p.h>
+#include <common/qgstpipeline_p.h>
+
#include <gst/app/gstappsink.h>
QT_BEGIN_NAMESPACE
@@ -76,10 +41,9 @@ class GStreamerInputPrivate;
class QGStreamerAudioSource
: public QPlatformAudioSource
{
- Q_OBJECT
friend class GStreamerInputPrivate;
public:
- QGStreamerAudioSource(const QAudioDevice &device);
+ QGStreamerAudioSource(const QAudioDevice &device, QObject *parent);
~QGStreamerAudioSource();
void start(QIODevice *device) override;
@@ -100,14 +64,11 @@ public:
void setVolume(qreal volume) override;
qreal volume() const override;
-private Q_SLOTS:
- void newDataAvailable(GstSample *sample);
-
private:
void setState(QAudio::State state);
void setError(QAudio::Error error);
- QGstElement createAppSink();
+ QGstAppSink createAppSink();
static GstFlowReturn new_sample(GstAppSink *, gpointer user_data);
static void eos(GstAppSink *, gpointer user_data);
@@ -116,6 +77,8 @@ private:
static gboolean busMessage(GstBus *bus, GstMessage *msg, gpointer user_data);
+ void newDataAvailable(QGstSampleHandle sample);
+
QAudioDevice m_info;
qint64 m_bytesWritten = 0;
QIODevice *m_audioSink = nullptr;
@@ -136,15 +99,13 @@ private:
QGstElement gstInput;
QGstPipeline gstPipeline;
QGstElement gstVolume;
- QGstElement gstAppSink;
+ QGstAppSink gstAppSink;
};
class GStreamerInputPrivate : public QIODevice
{
- Q_OBJECT
public:
- GStreamerInputPrivate(QGStreamerAudioSource *audio);
- ~GStreamerInputPrivate() {};
+ explicit GStreamerInputPrivate(QGStreamerAudioSource *audio);
qint64 readData(char *data, qint64 len) override;
qint64 writeData(const char *data, qint64 len) override;
diff --git a/src/plugins/multimedia/gstreamer/common/qglist_helper_p.h b/src/plugins/multimedia/gstreamer/common/qglist_helper_p.h
new file mode 100644
index 000000000..54108e1c3
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qglist_helper_p.h
@@ -0,0 +1,82 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGLIST_HELPER_P_H
+#define QGLIST_HELPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qtconfigmacros.h>
+
+#include <glib.h>
+#include <iterator>
+
+QT_BEGIN_NAMESPACE
+
+namespace QGstUtils {
+
+template <typename ListType>
+struct GListIterator
+{
+ explicit GListIterator(const GList *element = nullptr) : element(element) { }
+
+ const ListType &operator*() const noexcept { return *operator->(); }
+ const ListType *operator->() const noexcept
+ {
+ return reinterpret_cast<const ListType *>(&element->data);
+ }
+
+ GListIterator &operator++() noexcept
+ {
+ if (element)
+ element = element->next;
+
+ return *this;
+ }
+ GListIterator operator++(int n) noexcept
+ {
+ for (int i = 0; i != n; ++i)
+ operator++();
+
+ return *this;
+ }
+
+ bool operator==(const GListIterator &r) const noexcept { return element == r.element; }
+ bool operator!=(const GListIterator &r) const noexcept { return element != r.element; }
+
+ using difference_type = std::ptrdiff_t;
+ using value_type = ListType;
+ using pointer = value_type *;
+ using reference = value_type &;
+ using iterator_category = std::input_iterator_tag;
+
+ const GList *element = nullptr;
+};
+
+template <typename ListType>
+struct GListRangeAdaptor
+{
+ static_assert(std::is_pointer_v<ListType>);
+
+ explicit GListRangeAdaptor(const GList *list) : head(list) { }
+
+ auto begin() { return GListIterator<ListType>(head); }
+ auto end() { return GListIterator<ListType>(nullptr); }
+
+ const GList *head;
+};
+
+} // namespace QGstUtils
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgst.cpp b/src/plugins/multimedia/gstreamer/common/qgst.cpp
new file mode 100644
index 000000000..9e0a63488
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst.cpp
@@ -0,0 +1,1238 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <common/qgst_p.h>
+#include <common/qgst_debug_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreamermessage_p.h>
+
+#include <QtCore/qdebug.h>
+#include <QtMultimedia/qcameradevice.h>
+
+#include <array>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+struct VideoFormat
+{
+ QVideoFrameFormat::PixelFormat pixelFormat;
+ GstVideoFormat gstFormat;
+};
+
+constexpr std::array<VideoFormat, 19> qt_videoFormatLookup{ {
+ { QVideoFrameFormat::Format_YUV420P, GST_VIDEO_FORMAT_I420 },
+ { QVideoFrameFormat::Format_YUV422P, GST_VIDEO_FORMAT_Y42B },
+ { QVideoFrameFormat::Format_YV12, GST_VIDEO_FORMAT_YV12 },
+ { QVideoFrameFormat::Format_UYVY, GST_VIDEO_FORMAT_UYVY },
+ { QVideoFrameFormat::Format_YUYV, GST_VIDEO_FORMAT_YUY2 },
+ { QVideoFrameFormat::Format_NV12, GST_VIDEO_FORMAT_NV12 },
+ { QVideoFrameFormat::Format_NV21, GST_VIDEO_FORMAT_NV21 },
+ { QVideoFrameFormat::Format_AYUV, GST_VIDEO_FORMAT_AYUV },
+ { QVideoFrameFormat::Format_Y8, GST_VIDEO_FORMAT_GRAY8 },
+ { QVideoFrameFormat::Format_XRGB8888, GST_VIDEO_FORMAT_xRGB },
+ { QVideoFrameFormat::Format_XBGR8888, GST_VIDEO_FORMAT_xBGR },
+ { QVideoFrameFormat::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx },
+ { QVideoFrameFormat::Format_BGRX8888, GST_VIDEO_FORMAT_BGRx },
+ { QVideoFrameFormat::Format_ARGB8888, GST_VIDEO_FORMAT_ARGB },
+ { QVideoFrameFormat::Format_ABGR8888, GST_VIDEO_FORMAT_ABGR },
+ { QVideoFrameFormat::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA },
+ { QVideoFrameFormat::Format_BGRA8888, GST_VIDEO_FORMAT_BGRA },
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ { QVideoFrameFormat::Format_Y16, GST_VIDEO_FORMAT_GRAY16_LE },
+ { QVideoFrameFormat::Format_P010, GST_VIDEO_FORMAT_P010_10LE },
+#else
+ { QVideoFrameFormat::Format_Y16, GST_VIDEO_FORMAT_GRAY16_BE },
+ { QVideoFrameFormat::Format_P010, GST_VIDEO_FORMAT_P010_10BE },
+#endif
+} };
+
+int indexOfVideoFormat(QVideoFrameFormat::PixelFormat format)
+{
+ for (size_t i = 0; i < qt_videoFormatLookup.size(); ++i)
+ if (qt_videoFormatLookup[i].pixelFormat == format)
+ return int(i);
+
+ return -1;
+}
+
+int indexOfVideoFormat(GstVideoFormat format)
+{
+ for (size_t i = 0; i < qt_videoFormatLookup.size(); ++i)
+ if (qt_videoFormatLookup[i].gstFormat == format)
+ return int(i);
+
+ return -1;
+}
+
+} // namespace
+
+// QGValue
+
+QGValue::QGValue(const GValue *v) : value(v) { }
+
+bool QGValue::isNull() const
+{
+ return !value;
+}
+
+std::optional<bool> QGValue::toBool() const
+{
+ if (!G_VALUE_HOLDS_BOOLEAN(value))
+ return std::nullopt;
+ return g_value_get_boolean(value);
+}
+
+std::optional<int> QGValue::toInt() const
+{
+ if (!G_VALUE_HOLDS_INT(value))
+ return std::nullopt;
+ return g_value_get_int(value);
+}
+
+std::optional<int> QGValue::toInt64() const
+{
+ if (!G_VALUE_HOLDS_INT64(value))
+ return std::nullopt;
+ return g_value_get_int64(value);
+}
+
+const char *QGValue::toString() const
+{
+ return value ? g_value_get_string(value) : nullptr;
+}
+
+std::optional<float> QGValue::getFraction() const
+{
+ if (!GST_VALUE_HOLDS_FRACTION(value))
+ return std::nullopt;
+ return (float)gst_value_get_fraction_numerator(value)
+ / (float)gst_value_get_fraction_denominator(value);
+}
+
+std::optional<QGRange<float>> QGValue::getFractionRange() const
+{
+ if (!GST_VALUE_HOLDS_FRACTION_RANGE(value))
+ return std::nullopt;
+ QGValue min = QGValue{ gst_value_get_fraction_range_min(value) };
+ QGValue max = QGValue{ gst_value_get_fraction_range_max(value) };
+ return QGRange<float>{ *min.getFraction(), *max.getFraction() };
+}
+
+std::optional<QGRange<int>> QGValue::toIntRange() const
+{
+ if (!GST_VALUE_HOLDS_INT_RANGE(value))
+ return std::nullopt;
+ return QGRange<int>{ gst_value_get_int_range_min(value), gst_value_get_int_range_max(value) };
+}
+
+QGstStructure QGValue::toStructure() const
+{
+ if (!value || !GST_VALUE_HOLDS_STRUCTURE(value))
+ return QGstStructure();
+ return QGstStructure(gst_value_get_structure(value));
+}
+
+QGstCaps QGValue::toCaps() const
+{
+ if (!value || !GST_VALUE_HOLDS_CAPS(value))
+ return {};
+ return QGstCaps(gst_caps_copy(gst_value_get_caps(value)), QGstCaps::HasRef);
+}
+
+bool QGValue::isList() const
+{
+ return value && GST_VALUE_HOLDS_LIST(value);
+}
+
+int QGValue::listSize() const
+{
+ return gst_value_list_get_size(value);
+}
+
+QGValue QGValue::at(int index) const
+{
+ return QGValue{ gst_value_list_get_value(value, index) };
+}
+
+// QGstStructure
+
+QGstStructure::QGstStructure(const GstStructure *s) : structure(s) { }
+
+void QGstStructure::free()
+{
+ if (structure)
+ gst_structure_free(const_cast<GstStructure *>(structure));
+ structure = nullptr;
+}
+
+bool QGstStructure::isNull() const
+{
+ return !structure;
+}
+
+QByteArrayView QGstStructure::name() const
+{
+ return gst_structure_get_name(structure);
+}
+
+QGValue QGstStructure::operator[](const char *name) const
+{
+ return QGValue{ gst_structure_get_value(structure, name) };
+}
+
+QGstStructure QGstStructure::copy() const
+{
+ return gst_structure_copy(structure);
+}
+
+QSize QGstStructure::resolution() const
+{
+ QSize size;
+
+ int w, h;
+ if (structure && gst_structure_get_int(structure, "width", &w)
+ && gst_structure_get_int(structure, "height", &h)) {
+ size.rwidth() = w;
+ size.rheight() = h;
+ }
+
+ return size;
+}
+
+QVideoFrameFormat::PixelFormat QGstStructure::pixelFormat() const
+{
+ QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid;
+
+ if (!structure)
+ return pixelFormat;
+
+ if (gst_structure_has_name(structure, "video/x-raw")) {
+ const gchar *s = gst_structure_get_string(structure, "format");
+ if (s) {
+ GstVideoFormat format = gst_video_format_from_string(s);
+ int index = indexOfVideoFormat(format);
+
+ if (index != -1)
+ pixelFormat = qt_videoFormatLookup[index].pixelFormat;
+ }
+ } else if (gst_structure_has_name(structure, "image/jpeg")) {
+ pixelFormat = QVideoFrameFormat::Format_Jpeg;
+ }
+
+ return pixelFormat;
+}
+
+QGRange<float> QGstStructure::frameRateRange() const
+{
+ float minRate = 0.;
+ float maxRate = 0.;
+
+ if (!structure)
+ return { 0.f, 0.f };
+
+ auto extractFraction = [](const GValue *v) -> float {
+ return (float)gst_value_get_fraction_numerator(v)
+ / (float)gst_value_get_fraction_denominator(v);
+ };
+ auto extractFrameRate = [&](const GValue *v) {
+ auto insert = [&](float min, float max) {
+ if (max > maxRate)
+ maxRate = max;
+ if (min < minRate)
+ minRate = min;
+ };
+
+ if (GST_VALUE_HOLDS_FRACTION(v)) {
+ float rate = extractFraction(v);
+ insert(rate, rate);
+ } else if (GST_VALUE_HOLDS_FRACTION_RANGE(v)) {
+ auto *min = gst_value_get_fraction_range_max(v);
+ auto *max = gst_value_get_fraction_range_max(v);
+ insert(extractFraction(min), extractFraction(max));
+ }
+ };
+
+ const GValue *gstFrameRates = gst_structure_get_value(structure, "framerate");
+ if (gstFrameRates) {
+ if (GST_VALUE_HOLDS_LIST(gstFrameRates)) {
+ guint nFrameRates = gst_value_list_get_size(gstFrameRates);
+ for (guint f = 0; f < nFrameRates; ++f) {
+ extractFrameRate(gst_value_list_get_value(gstFrameRates, f));
+ }
+ } else {
+ extractFrameRate(gstFrameRates);
+ }
+ } else {
+ const GValue *min = gst_structure_get_value(structure, "min-framerate");
+ const GValue *max = gst_structure_get_value(structure, "max-framerate");
+ if (min && max) {
+ minRate = extractFraction(min);
+ maxRate = extractFraction(max);
+ }
+ }
+
+ return { minRate, maxRate };
+}
+
+QGstreamerMessage QGstStructure::getMessage()
+{
+ GstMessage *message = nullptr;
+ gst_structure_get(structure, "message", GST_TYPE_MESSAGE, &message, nullptr);
+ return QGstreamerMessage(message, QGstreamerMessage::HasRef);
+}
+
+std::optional<Fraction> QGstStructure::pixelAspectRatio() const
+{
+ gint numerator;
+ gint denominator;
+ if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &numerator, &denominator)) {
+ return Fraction{
+ numerator,
+ denominator,
+ };
+ }
+
+ return std::nullopt;
+}
+
+QSize QGstStructure::nativeSize() const
+{
+ QSize size = resolution();
+ if (!size.isValid()) {
+ qWarning() << Q_FUNC_INFO << "invalid resolution when querying nativeSize";
+ return size;
+ }
+
+ std::optional<Fraction> par = pixelAspectRatio();
+ if (par)
+ size = qCalculateFrameSize(size, *par);
+ return size;
+}
+
+// QGstCaps
+
+std::optional<std::pair<QVideoFrameFormat, GstVideoInfo>> QGstCaps::formatAndVideoInfo() const
+{
+ GstVideoInfo vidInfo;
+
+ bool success = gst_video_info_from_caps(&vidInfo, get());
+ if (!success)
+ return std::nullopt;
+
+ int index = indexOfVideoFormat(vidInfo.finfo->format);
+ if (index == -1)
+ return std::nullopt;
+
+ QVideoFrameFormat format(QSize(vidInfo.width, vidInfo.height),
+ qt_videoFormatLookup[index].pixelFormat);
+
+ if (vidInfo.fps_d > 0)
+ format.setStreamFrameRate(qreal(vidInfo.fps_n) / vidInfo.fps_d);
+
+ QVideoFrameFormat::ColorRange range = QVideoFrameFormat::ColorRange_Unknown;
+ switch (vidInfo.colorimetry.range) {
+ case GST_VIDEO_COLOR_RANGE_UNKNOWN:
+ break;
+ case GST_VIDEO_COLOR_RANGE_0_255:
+ range = QVideoFrameFormat::ColorRange_Full;
+ break;
+ case GST_VIDEO_COLOR_RANGE_16_235:
+ range = QVideoFrameFormat::ColorRange_Video;
+ break;
+ }
+ format.setColorRange(range);
+
+ QVideoFrameFormat::ColorSpace colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
+ switch (vidInfo.colorimetry.matrix) {
+ case GST_VIDEO_COLOR_MATRIX_UNKNOWN:
+ case GST_VIDEO_COLOR_MATRIX_RGB:
+ case GST_VIDEO_COLOR_MATRIX_FCC:
+ break;
+ case GST_VIDEO_COLOR_MATRIX_BT709:
+ colorSpace = QVideoFrameFormat::ColorSpace_BT709;
+ break;
+ case GST_VIDEO_COLOR_MATRIX_BT601:
+ colorSpace = QVideoFrameFormat::ColorSpace_BT601;
+ break;
+ case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
+ colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
+ break;
+ case GST_VIDEO_COLOR_MATRIX_BT2020:
+ colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
+ break;
+ }
+ format.setColorSpace(colorSpace);
+
+ QVideoFrameFormat::ColorTransfer transfer = QVideoFrameFormat::ColorTransfer_Unknown;
+ switch (vidInfo.colorimetry.transfer) {
+ case GST_VIDEO_TRANSFER_UNKNOWN:
+ break;
+ case GST_VIDEO_TRANSFER_GAMMA10:
+ transfer = QVideoFrameFormat::ColorTransfer_Linear;
+ break;
+ case GST_VIDEO_TRANSFER_GAMMA22:
+ case GST_VIDEO_TRANSFER_SMPTE240M:
+ case GST_VIDEO_TRANSFER_SRGB:
+ case GST_VIDEO_TRANSFER_ADOBERGB:
+ transfer = QVideoFrameFormat::ColorTransfer_Gamma22;
+ break;
+ case GST_VIDEO_TRANSFER_GAMMA18:
+ case GST_VIDEO_TRANSFER_GAMMA20:
+ // not quite, but best fit
+ case GST_VIDEO_TRANSFER_BT709:
+ case GST_VIDEO_TRANSFER_BT2020_12:
+ transfer = QVideoFrameFormat::ColorTransfer_BT709;
+ break;
+ case GST_VIDEO_TRANSFER_GAMMA28:
+ transfer = QVideoFrameFormat::ColorTransfer_Gamma28;
+ break;
+ case GST_VIDEO_TRANSFER_LOG100:
+ case GST_VIDEO_TRANSFER_LOG316:
+ break;
+#if GST_CHECK_VERSION(1, 18, 0)
+ case GST_VIDEO_TRANSFER_SMPTE2084:
+ transfer = QVideoFrameFormat::ColorTransfer_ST2084;
+ break;
+ case GST_VIDEO_TRANSFER_ARIB_STD_B67:
+ transfer = QVideoFrameFormat::ColorTransfer_STD_B67;
+ break;
+ case GST_VIDEO_TRANSFER_BT2020_10:
+ transfer = QVideoFrameFormat::ColorTransfer_BT709;
+ break;
+ case GST_VIDEO_TRANSFER_BT601:
+ transfer = QVideoFrameFormat::ColorTransfer_BT601;
+ break;
+#endif
+ }
+ format.setColorTransfer(transfer);
+
+ return std::pair{
+ std::move(format),
+ vidInfo,
+ };
+}
+
+void QGstCaps::addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats,
+ const char *modifier)
+{
+ if (!gst_caps_is_writable(get()))
+ *this = QGstCaps(gst_caps_make_writable(release()), QGstCaps::RefMode::HasRef);
+
+ GValue list = {};
+ g_value_init(&list, GST_TYPE_LIST);
+
+ for (QVideoFrameFormat::PixelFormat format : formats) {
+ int index = indexOfVideoFormat(format);
+ if (index == -1)
+ continue;
+ GValue item = {};
+
+ g_value_init(&item, G_TYPE_STRING);
+ g_value_set_string(&item,
+ gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat));
+ gst_value_list_append_value(&list, &item);
+ g_value_unset(&item);
+ }
+
+ auto *structure = gst_structure_new("video/x-raw", "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
+ INT_MAX, 1, "width", GST_TYPE_INT_RANGE, 1, INT_MAX,
+ "height", GST_TYPE_INT_RANGE, 1, INT_MAX, nullptr);
+ gst_structure_set_value(structure, "format", &list);
+ gst_caps_append_structure(get(), structure);
+ g_value_unset(&list);
+
+ if (modifier)
+ gst_caps_set_features(get(), size() - 1, gst_caps_features_from_string(modifier));
+}
+
+void QGstCaps::setResolution(QSize resolution)
+{
+ Q_ASSERT(resolution.isValid());
+ GValue width{};
+ g_value_init(&width, G_TYPE_INT);
+ g_value_set_int(&width, resolution.width());
+ GValue height{};
+ g_value_init(&height, G_TYPE_INT);
+ g_value_set_int(&height, resolution.height());
+
+ gst_caps_set_value(caps(), "width", &width);
+ gst_caps_set_value(caps(), "height", &height);
+}
+
+QGstCaps QGstCaps::fromCameraFormat(const QCameraFormat &format)
+{
+ QSize size = format.resolution();
+ GstStructure *structure = nullptr;
+ if (format.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
+ structure = gst_structure_new("image/jpeg", "width", G_TYPE_INT, size.width(), "height",
+ G_TYPE_INT, size.height(), nullptr);
+ } else {
+ int index = indexOfVideoFormat(format.pixelFormat());
+ if (index < 0)
+ return {};
+ auto gstFormat = qt_videoFormatLookup[index].gstFormat;
+ structure = gst_structure_new("video/x-raw", "format", G_TYPE_STRING,
+ gst_video_format_to_string(gstFormat), "width", G_TYPE_INT,
+ size.width(), "height", G_TYPE_INT, size.height(), nullptr);
+ }
+ auto caps = QGstCaps::create();
+ gst_caps_append_structure(caps.get(), structure);
+ return caps;
+}
+
+QGstCaps QGstCaps::copy() const
+{
+ return QGstCaps{
+ gst_caps_copy(caps()),
+ QGstCaps::HasRef,
+ };
+}
+
+QGstCaps::MemoryFormat QGstCaps::memoryFormat() const
+{
+ auto *features = gst_caps_get_features(get(), 0);
+ if (gst_caps_features_contains(features, "memory:GLMemory"))
+ return GLTexture;
+ if (gst_caps_features_contains(features, "memory:DMABuf"))
+ return DMABuf;
+ return CpuMemory;
+}
+
+int QGstCaps::size() const
+{
+ return int(gst_caps_get_size(get()));
+}
+
+QGstStructure QGstCaps::at(int index) const
+{
+ return gst_caps_get_structure(get(), index);
+}
+
+GstCaps *QGstCaps::caps() const
+{
+ return get();
+}
+
+QGstCaps QGstCaps::create()
+{
+ return QGstCaps(gst_caps_new_empty(), HasRef);
+}
+
+// QGstObject
+
+void QGstObject::set(const char *property, const char *str)
+{
+ g_object_set(get(), property, str, nullptr);
+}
+
+void QGstObject::set(const char *property, bool b)
+{
+ g_object_set(get(), property, gboolean(b), nullptr);
+}
+
+void QGstObject::set(const char *property, uint i)
+{
+ g_object_set(get(), property, guint(i), nullptr);
+}
+
+void QGstObject::set(const char *property, int i)
+{
+ g_object_set(get(), property, gint(i), nullptr);
+}
+
+void QGstObject::set(const char *property, qint64 i)
+{
+ g_object_set(get(), property, gint64(i), nullptr);
+}
+
+void QGstObject::set(const char *property, quint64 i)
+{
+ g_object_set(get(), property, guint64(i), nullptr);
+}
+
+void QGstObject::set(const char *property, double d)
+{
+ g_object_set(get(), property, gdouble(d), nullptr);
+}
+
+void QGstObject::set(const char *property, const QGstObject &o)
+{
+ g_object_set(get(), property, o.object(), nullptr);
+}
+
+void QGstObject::set(const char *property, const QGstCaps &c)
+{
+ g_object_set(get(), property, c.caps(), nullptr);
+}
+
+QGString QGstObject::getString(const char *property) const
+{
+ char *s = nullptr;
+ g_object_get(get(), property, &s, nullptr);
+ return QGString(s);
+}
+
+QGstStructure QGstObject::getStructure(const char *property) const
+{
+ GstStructure *s = nullptr;
+ g_object_get(get(), property, &s, nullptr);
+ return QGstStructure(s);
+}
+
+bool QGstObject::getBool(const char *property) const
+{
+ gboolean b = false;
+ g_object_get(get(), property, &b, nullptr);
+ return b;
+}
+
+uint QGstObject::getUInt(const char *property) const
+{
+ guint i = 0;
+ g_object_get(get(), property, &i, nullptr);
+ return i;
+}
+
+int QGstObject::getInt(const char *property) const
+{
+ gint i = 0;
+ g_object_get(get(), property, &i, nullptr);
+ return i;
+}
+
+quint64 QGstObject::getUInt64(const char *property) const
+{
+ guint64 i = 0;
+ g_object_get(get(), property, &i, nullptr);
+ return i;
+}
+
+qint64 QGstObject::getInt64(const char *property) const
+{
+ gint64 i = 0;
+ g_object_get(get(), property, &i, nullptr);
+ return i;
+}
+
+float QGstObject::getFloat(const char *property) const
+{
+ gfloat d = 0;
+ g_object_get(get(), property, &d, nullptr);
+ return d;
+}
+
+double QGstObject::getDouble(const char *property) const
+{
+ gdouble d = 0;
+ g_object_get(get(), property, &d, nullptr);
+ return d;
+}
+
+QGstObject QGstObject::getObject(const char *property) const
+{
+ GstObject *o = nullptr;
+ g_object_get(get(), property, &o, nullptr);
+ return QGstObject(o, HasRef);
+}
+
+QGObjectHandlerConnection QGstObject::connect(const char *name, GCallback callback,
+ gpointer userData)
+{
+ return QGObjectHandlerConnection{
+ *this,
+ g_signal_connect(get(), name, callback, userData),
+ };
+}
+
+void QGstObject::disconnect(gulong handlerId)
+{
+ g_signal_handler_disconnect(get(), handlerId);
+}
+
+GType QGstObject::type() const
+{
+ return G_OBJECT_TYPE(get());
+}
+
+const char *QGstObject::typeName() const
+{
+ return g_type_name(type());
+}
+
+GstObject *QGstObject::object() const
+{
+ return get();
+}
+
+const char *QGstObject::name() const
+{
+ return get() ? GST_OBJECT_NAME(get()) : "(null)";
+}
+
+// QGObjectHandlerConnection
+
+QGObjectHandlerConnection::QGObjectHandlerConnection(QGstObject object, gulong handlerId)
+ : object{ std::move(object) }, handlerId{ handlerId }
+{
+}
+
+void QGObjectHandlerConnection::disconnect()
+{
+ if (!object)
+ return;
+
+ object.disconnect(handlerId);
+ object = {};
+ handlerId = invalidHandlerId;
+}
+
+// QGObjectHandlerScopedConnection
+
+QGObjectHandlerScopedConnection::QGObjectHandlerScopedConnection(
+ QGObjectHandlerConnection connection)
+ : connection{
+ std::move(connection),
+ }
+{
+}
+
+QGObjectHandlerScopedConnection::~QGObjectHandlerScopedConnection()
+{
+ connection.disconnect();
+}
+
+void QGObjectHandlerScopedConnection::disconnect()
+{
+ connection.disconnect();
+}
+
+// QGstPad
+
+QGstPad::QGstPad(const QGstObject &o)
+ : QGstPad{
+ qGstSafeCast<GstPad>(o.object()),
+ QGstElement::NeedsRef,
+ }
+{
+}
+
+QGstPad::QGstPad(GstPad *pad, RefMode mode)
+ : QGstObject{
+ qGstCheckedCast<GstObject>(pad),
+ mode,
+ }
+{
+}
+
+QGstCaps QGstPad::currentCaps() const
+{
+ return QGstCaps(gst_pad_get_current_caps(pad()), QGstCaps::HasRef);
+}
+
+QGstCaps QGstPad::queryCaps() const
+{
+ return QGstCaps(gst_pad_query_caps(pad(), nullptr), QGstCaps::HasRef);
+}
+
+bool QGstPad::isLinked() const
+{
+ return gst_pad_is_linked(pad());
+}
+
+bool QGstPad::link(const QGstPad &sink) const
+{
+ return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK;
+}
+
+bool QGstPad::unlink(const QGstPad &sink) const
+{
+ return gst_pad_unlink(pad(), sink.pad());
+}
+
+bool QGstPad::unlinkPeer() const
+{
+ return unlink(peer());
+}
+
+QGstPad QGstPad::peer() const
+{
+ return QGstPad(gst_pad_get_peer(pad()), HasRef);
+}
+
+QGstElement QGstPad::parent() const
+{
+ return QGstElement(gst_pad_get_parent_element(pad()), HasRef);
+}
+
+GstPad *QGstPad::pad() const
+{
+ return qGstCheckedCast<GstPad>(object());
+}
+
+GstEvent *QGstPad::stickyEvent(GstEventType type)
+{
+ return gst_pad_get_sticky_event(pad(), type, 0);
+}
+
+bool QGstPad::sendEvent(GstEvent *event)
+{
+ return gst_pad_send_event(pad(), event);
+}
+
+// QGstClock
+
+QGstClock::QGstClock(const QGstObject &o)
+ : QGstClock{
+ qGstSafeCast<GstClock>(o.object()),
+ QGstElement::NeedsRef,
+ }
+{
+}
+
+QGstClock::QGstClock(GstClock *clock, RefMode mode)
+ : QGstObject{
+ qGstCheckedCast<GstObject>(clock),
+ mode,
+ }
+{
+}
+
+GstClock *QGstClock::clock() const
+{
+ return qGstCheckedCast<GstClock>(object());
+}
+
+GstClockTime QGstClock::time() const
+{
+ return gst_clock_get_time(clock());
+}
+
+// QGstElement
+
+QGstElement::QGstElement(GstElement *element, RefMode mode)
+ : QGstObject{
+ qGstCheckedCast<GstObject>(element),
+ mode,
+ }
+{
+}
+
+QGstElement QGstElement::createFromFactory(const char *factory, const char *name)
+{
+ GstElement *element = gst_element_factory_make(factory, name);
+
+#ifndef QT_NO_DEBUG
+ if (!element) {
+ qWarning() << "Failed to make element" << name << "from factory" << factory;
+ return QGstElement{};
+ }
+#endif
+
+ return QGstElement{
+ element,
+ NeedsRef,
+ };
+}
+
+QGstElement QGstElement::createFromFactory(GstElementFactory *factory, const char *name)
+{
+ return QGstElement{
+ gst_element_factory_create(factory, name),
+ NeedsRef,
+ };
+}
+
+QGstElement QGstElement::createFromFactory(const QGstElementFactoryHandle &factory,
+ const char *name)
+{
+ return createFromFactory(factory.get(), name);
+}
+
+QGstElement QGstElement::createFromDevice(const QGstDeviceHandle &device, const char *name)
+{
+ return createFromDevice(device.get(), name);
+}
+
+QGstElement QGstElement::createFromDevice(GstDevice *device, const char *name)
+{
+ return QGstElement{
+ gst_device_create_element(device, name),
+ QGstElement::NeedsRef,
+ };
+}
+
+QGstElement QGstElement::createFromPipelineDescription(const char *str)
+{
+ QUniqueGErrorHandle error;
+ QGstElement element{
+ gst_parse_launch(str, &error),
+ QGstElement::NeedsRef,
+ };
+
+ if (error) // error does not mean that the element could not be constructed
+ qWarning() << "gst_parse_launch error:" << error;
+
+ return element;
+}
+
+QGstElement QGstElement::createFromPipelineDescription(const QByteArray &str)
+{
+ return createFromPipelineDescription(str.constData());
+}
+
+QGstPad QGstElement::staticPad(const char *name) const
+{
+ return QGstPad(gst_element_get_static_pad(element(), name), HasRef);
+}
+
+QGstPad QGstElement::src() const
+{
+ return staticPad("src");
+}
+
+QGstPad QGstElement::sink() const
+{
+ return staticPad("sink");
+}
+
+QGstPad QGstElement::getRequestPad(const char *name) const
+{
+#if GST_CHECK_VERSION(1, 19, 1)
+ return QGstPad(gst_element_request_pad_simple(element(), name), HasRef);
+#else
+ return QGstPad(gst_element_get_request_pad(element(), name), HasRef);
+#endif
+}
+
+void QGstElement::releaseRequestPad(const QGstPad &pad) const
+{
+ return gst_element_release_request_pad(element(), pad.pad());
+}
+
+GstState QGstElement::state(std::chrono::nanoseconds timeout) const
+{
+ using namespace std::chrono_literals;
+
+ GstState state;
+ GstStateChangeReturn change =
+ gst_element_get_state(element(), &state, nullptr, timeout.count());
+
+ if (Q_UNLIKELY(change == GST_STATE_CHANGE_ASYNC))
+ qWarning() << "QGstElement::state detected an asynchronous state change. Return value not "
+ "reliable";
+
+ return state;
+}
+
+GstStateChangeReturn QGstElement::setState(GstState state)
+{
+ return gst_element_set_state(element(), state);
+}
+
+bool QGstElement::setStateSync(GstState state, std::chrono::nanoseconds timeout)
+{
+ GstStateChangeReturn change = gst_element_set_state(element(), state);
+ if (change == GST_STATE_CHANGE_ASYNC) {
+ change = gst_element_get_state(element(), nullptr, &state, timeout.count());
+ }
+#ifndef QT_NO_DEBUG
+ if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL)
+ qWarning() << "Could not change state of" << name() << "to" << state << change;
+#endif
+ return change == GST_STATE_CHANGE_SUCCESS;
+}
+
+bool QGstElement::syncStateWithParent()
+{
+ Q_ASSERT(element());
+ return gst_element_sync_state_with_parent(element()) == TRUE;
+}
+
+bool QGstElement::finishStateChange(std::chrono::nanoseconds timeout)
+{
+ GstState state, pending;
+ GstStateChangeReturn change =
+ gst_element_get_state(element(), &state, &pending, timeout.count());
+
+#ifndef QT_NO_DEBUG
+ if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) {
+ qWarning() << "Could not finish change state of" << name() << change << state << pending;
+
+ static const bool dumpEnabled = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR");
+ if (dumpEnabled) {
+ QGstPipeline pipeline = getPipeline();
+ if (pipeline)
+ pipeline.dumpGraph("finishStateChangeFailure");
+ }
+ }
+#endif
+ return change == GST_STATE_CHANGE_SUCCESS;
+}
+
+void QGstElement::lockState(bool locked)
+{
+ gst_element_set_locked_state(element(), locked);
+}
+
+bool QGstElement::isStateLocked() const
+{
+ return gst_element_is_locked_state(element());
+}
+
+void QGstElement::sendEvent(GstEvent *event) const
+{
+ gst_element_send_event(element(), event);
+}
+
+void QGstElement::sendEos() const
+{
+ sendEvent(gst_event_new_eos());
+}
+
+GstClockTime QGstElement::baseTime() const
+{
+ return gst_element_get_base_time(element());
+}
+
+void QGstElement::setBaseTime(GstClockTime time) const
+{
+ gst_element_set_base_time(element(), time);
+}
+
+GstElement *QGstElement::element() const
+{
+ return GST_ELEMENT_CAST(get());
+}
+
+QGstElement QGstElement::getParent() const
+{
+ return QGstElement{
+ qGstCheckedCast<GstElement>(gst_element_get_parent(object())),
+ QGstElement::HasRef,
+ };
+}
+
+QGstPipeline QGstElement::getPipeline() const
+{
+ QGstElement ancestor = *this;
+ for (;;) {
+ QGstElement greatAncestor = ancestor.getParent();
+ if (greatAncestor) {
+ ancestor = std::move(greatAncestor);
+ continue;
+ }
+
+ return QGstPipeline{
+ qGstSafeCast<GstPipeline>(ancestor.element()),
+ QGstPipeline::NeedsRef,
+ };
+ }
+}
+
+// QGstBin
+
+QGstBin QGstBin::create(const char *name)
+{
+ return QGstBin(gst_bin_new(name), NeedsRef);
+}
+
+QGstBin QGstBin::createFromFactory(const char *factory, const char *name)
+{
+ QGstElement element = QGstElement::createFromFactory(factory, name);
+ Q_ASSERT(GST_IS_BIN(element.element()));
+ return QGstBin{
+ GST_BIN(element.release()),
+ RefMode::HasRef,
+ };
+}
+
+QGstBin QGstBin::createFromPipelineDescription(const QByteArray &pipelineDescription,
+ const char *name, bool ghostUnlinkedPads)
+{
+ return createFromPipelineDescription(pipelineDescription.constData(), name, ghostUnlinkedPads);
+}
+
+QGstBin QGstBin::createFromPipelineDescription(const char *pipelineDescription, const char *name,
+ bool ghostUnlinkedPads)
+{
+ QUniqueGErrorHandle error;
+
+ GstElement *element =
+ gst_parse_bin_from_description_full(pipelineDescription, ghostUnlinkedPads,
+ /*context=*/nullptr, GST_PARSE_FLAG_NONE, &error);
+
+ if (!element) {
+ qWarning() << "Failed to make element from pipeline description" << pipelineDescription
+ << error;
+ return QGstBin{};
+ }
+
+ if (name)
+ gst_element_set_name(element, name);
+
+ return QGstBin{
+ element,
+ NeedsRef,
+ };
+}
+
+QGstBin::QGstBin(GstBin *bin, RefMode mode)
+ : QGstElement{
+ qGstCheckedCast<GstElement>(bin),
+ mode,
+ }
+{
+}
+
+GstBin *QGstBin::bin() const
+{
+ return qGstCheckedCast<GstBin>(object());
+}
+
+void QGstBin::addGhostPad(const QGstElement &child, const char *name)
+{
+ addGhostPad(name, child.staticPad(name));
+}
+
+void QGstBin::addGhostPad(const char *name, const QGstPad &pad)
+{
+ gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad()));
+}
+
+bool QGstBin::syncChildrenState()
+{
+ return gst_bin_sync_children_states(bin());
+}
+
+void QGstBin::dumpGraph(const char *fileNamePrefix)
+{
+ if (isNull())
+ return;
+
+ GST_DEBUG_BIN_TO_DOT_FILE(bin(), GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL),
+ fileNamePrefix);
+}
+
+QGstElement QGstBin::findByName(const char *name)
+{
+ return QGstElement{
+ gst_bin_get_by_name(bin(), name),
+ QGstElement::NeedsRef,
+ };
+}
+
+// QGstBaseSink
+
+QGstBaseSink::QGstBaseSink(GstBaseSink *element, RefMode mode)
+ : QGstElement{
+ qGstCheckedCast<GstElement>(element),
+ mode,
+ }
+{
+}
+
+GstBaseSink *QGstBaseSink::baseSink() const
+{
+ return qGstCheckedCast<GstBaseSink>(element());
+}
+
+// QGstBaseSrc
+
+QGstBaseSrc::QGstBaseSrc(GstBaseSrc *element, RefMode mode)
+ : QGstElement{
+ qGstCheckedCast<GstElement>(element),
+ mode,
+ }
+{
+}
+
+GstBaseSrc *QGstBaseSrc::baseSrc() const
+{
+ return qGstCheckedCast<GstBaseSrc>(element());
+}
+
+#if QT_CONFIG(gstreamer_app)
+
+// QGstAppSink
+
+QGstAppSink::QGstAppSink(GstAppSink *element, RefMode mode)
+ : QGstBaseSink{
+ qGstCheckedCast<GstBaseSink>(element),
+ mode,
+ }
+{
+}
+
+QGstAppSink QGstAppSink::create(const char *name)
+{
+ QGstElement created = QGstElement::createFromFactory("appsink", name);
+ return QGstAppSink{
+ qGstCheckedCast<GstAppSink>(created.element()),
+ QGstAppSink::NeedsRef,
+ };
+}
+
+GstAppSink *QGstAppSink::appSink() const
+{
+ return qGstCheckedCast<GstAppSink>(element());
+}
+
+void QGstAppSink::setCaps(const QGstCaps &caps)
+{
+ gst_app_sink_set_caps(appSink(), caps.caps());
+}
+
+void QGstAppSink::setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data,
+ GDestroyNotify notify)
+{
+ gst_app_sink_set_callbacks(appSink(), &callbacks, user_data, notify);
+}
+
+QGstSampleHandle QGstAppSink::pullSample()
+{
+ return QGstSampleHandle{
+ gst_app_sink_pull_sample(appSink()),
+ QGstSampleHandle::HasRef,
+ };
+}
+
+// QGstAppSrc
+
+QGstAppSrc::QGstAppSrc(GstAppSrc *element, RefMode mode)
+ : QGstBaseSrc{
+ qGstCheckedCast<GstBaseSrc>(element),
+ mode,
+ }
+{
+}
+
+QGstAppSrc QGstAppSrc::create(const char *name)
+{
+ QGstElement created = QGstElement::createFromFactory("appsrc", name);
+ return QGstAppSrc{
+ qGstCheckedCast<GstAppSrc>(created.element()),
+ QGstAppSrc::NeedsRef,
+ };
+}
+
+GstAppSrc *QGstAppSrc::appSrc() const
+{
+ return qGstCheckedCast<GstAppSrc>(element());
+}
+
+void QGstAppSrc::setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data,
+ GDestroyNotify notify)
+{
+ gst_app_src_set_callbacks(appSrc(), &callbacks, user_data, notify);
+}
+
+GstFlowReturn QGstAppSrc::pushBuffer(GstBuffer *buffer)
+{
+ return gst_app_src_push_buffer(appSrc(), buffer);
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgst_debug.cpp b/src/plugins/multimedia/gstreamer/common/qgst_debug.cpp
new file mode 100644
index 000000000..0461bfe09
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst_debug.cpp
@@ -0,0 +1,453 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qgst_debug_p.h"
+#include "qgstreamermessage_p.h"
+
+#include <gst/gstclock.h>
+
+QT_BEGIN_NAMESPACE
+
+// NOLINTBEGIN(performance-unnecessary-value-param)
+
+QDebug operator<<(QDebug dbg, const QGString &str)
+{
+ return dbg << str.get();
+}
+
+QDebug operator<<(QDebug dbg, const QGstCaps &caps)
+{
+ return dbg << caps.caps();
+}
+
+QDebug operator<<(QDebug dbg, const QGstStructure &structure)
+{
+ return dbg << structure.structure;
+}
+
+QDebug operator<<(QDebug dbg, const QGValue &value)
+{
+ return dbg << value.value;
+}
+
+QDebug operator<<(QDebug dbg, const QGstreamerMessage &msg)
+{
+ return dbg << msg.message();
+}
+
+QDebug operator<<(QDebug dbg, const QUniqueGErrorHandle &handle)
+{
+ return dbg << handle.get();
+}
+
+QDebug operator<<(QDebug dbg, const QUniqueGStringHandle &handle)
+{
+ return dbg << handle.get();
+}
+
+QDebug operator<<(QDebug dbg, const QGstElement &element)
+{
+ return dbg << element.element();
+}
+
+QDebug operator<<(QDebug dbg, const QGstPad &pad)
+{
+ return dbg << pad.pad();
+}
+
+QDebug operator<<(QDebug dbg, const GstCaps *caps)
+{
+ if (caps)
+ return dbg << QGString(gst_caps_to_string(caps));
+ else
+ return dbg << "null";
+}
+
+QDebug operator<<(QDebug dbg, const GstVideoInfo *info)
+{
+#if GST_CHECK_VERSION(1, 20, 0)
+ return dbg << QGstCaps{
+ gst_video_info_to_caps(info),
+ QGstCaps::NeedsRef,
+ };
+#else
+ return dbg << QGstCaps{
+ gst_video_info_to_caps(const_cast<GstVideoInfo *>(info)),
+ QGstCaps::NeedsRef,
+ };
+#endif
+}
+
+QDebug operator<<(QDebug dbg, const GstStructure *structure)
+{
+ if (structure)
+ return dbg << QGString(gst_structure_to_string(structure));
+ else
+ return dbg << "null";
+}
+
+QDebug operator<<(QDebug dbg, const GstObject *object)
+{
+ dbg << QGString{gst_object_get_name(const_cast<GstObject*>(object))};
+
+ {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+
+ dbg << "{";
+
+ guint numProperties;
+ GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(object), &numProperties);
+
+ for (guint i = 0; i < numProperties; i++) {
+ GParamSpec *param = properties[i];
+
+ const gchar *name = g_param_spec_get_name(param);
+ constexpr bool trace_blurb = false;
+ if constexpr (trace_blurb) {
+ const gchar *blurb = g_param_spec_get_blurb(param);
+ dbg << name << " (" << blurb << "): ";
+ } else
+ dbg << name << ": ";
+
+ bool readable = bool(param->flags & G_PARAM_READABLE);
+ if (!readable) {
+ dbg << "(not readable)";
+ } else if (QLatin1StringView(name) == QLatin1StringView("parent")) {
+ if (object->parent)
+ dbg << QGString{ gst_object_get_name(object->parent) };
+ else
+ dbg << "(none)";
+ } else {
+ GValue value = {};
+ g_object_get_property(&const_cast<GstObject *>(object)->object, param->name,
+ &value);
+ dbg << &value;
+ }
+ if (i != numProperties - 1)
+ dbg << ", ";
+ }
+
+ dbg << "}";
+
+ g_free(properties);
+ }
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const GstElement *element)
+{
+ return dbg << GST_OBJECT_CAST(element); // LATER: output other members?
+}
+
+QDebug operator<<(QDebug dbg, const GstPad *pad)
+{
+ return dbg << GST_OBJECT_CAST(pad); // LATER: output other members?
+}
+
+QDebug operator<<(QDebug dbg, const GstDevice *device)
+{
+ GstDevice *d = const_cast<GstDevice *>(device);
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+
+ dbg << gst_device_get_display_name(d) << "(" << gst_device_get_device_class(d) << ") ";
+ dbg << "Caps: " << QGstCaps{ gst_device_get_caps(d), QGstCaps::NeedsRef, } << ", ";
+ dbg << "Properties: " << QUniqueGstStructureHandle{ gst_device_get_properties(d) }.get();
+ return dbg;
+}
+
+namespace {
+
+struct Timepoint
+{
+ explicit Timepoint(guint64 us) : ts{ us } { }
+ guint64 ts;
+};
+
+QDebug operator<<(QDebug dbg, Timepoint ts)
+{
+ char buffer[128];
+ snprintf(buffer, sizeof(buffer), "%" GST_TIME_FORMAT, GST_TIME_ARGS(ts.ts));
+ dbg << buffer;
+ return dbg;
+}
+
+} // namespace
+
+QDebug operator<<(QDebug dbg, const GstMessage *msg)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+
+ dbg << GST_MESSAGE_TYPE_NAME(msg) << ", Source: " << GST_MESSAGE_SRC_NAME(msg);
+ if (GST_MESSAGE_TIMESTAMP(msg) != 0xFFFFFFFFFFFFFFFF)
+ dbg << ", Timestamp: " << GST_MESSAGE_TIMESTAMP(msg);
+
+ switch (msg->type) {
+ case GST_MESSAGE_ERROR: {
+ QUniqueGErrorHandle err;
+ QGString debug;
+ gst_message_parse_error(const_cast<GstMessage *>(msg), &err, &debug);
+
+ dbg << ", Error: " << err << " (" << debug << ")";
+ break;
+ }
+
+ case GST_MESSAGE_WARNING: {
+ QUniqueGErrorHandle err;
+ QGString debug;
+ gst_message_parse_warning(const_cast<GstMessage *>(msg), &err, &debug);
+
+ dbg << ", Warning: " << err << " (" << debug << ")";
+ break;
+ }
+
+ case GST_MESSAGE_INFO: {
+ QUniqueGErrorHandle err;
+ QGString debug;
+ gst_message_parse_info(const_cast<GstMessage *>(msg), &err, &debug);
+
+ dbg << ", Info: " << err << " (" << debug << ")";
+ break;
+ }
+
+ case GST_MESSAGE_QOS: {
+ gboolean live;
+ guint64 running_time;
+ guint64 stream_time;
+ guint64 timestamp;
+ guint64 duration;
+
+ gst_message_parse_qos(const_cast<GstMessage *>(msg), &live, &running_time, &stream_time,
+ &timestamp, &duration);
+
+ dbg << ", Live: " << bool(live) << ", Running time: " << Timepoint{ running_time }
+ << ", Stream time: " << Timepoint{ stream_time }
+ << ", Timestamp: " << Timepoint{ timestamp } << ", Duration: " << Timepoint{ duration };
+ break;
+ }
+
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState oldState;
+ GstState newState;
+ GstState pending;
+
+ gst_message_parse_state_changed(const_cast<GstMessage *>(msg), &oldState, &newState,
+ &pending);
+
+ dbg << ", Transition: " << oldState << "->" << newState;
+
+ if (pending != GST_STATE_VOID_PENDING)
+ dbg << ", Pending State: " << pending;
+ break;
+ }
+
+ default:
+ break;
+ }
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const GstTagList *tagList)
+{
+ dbg << QGString{ gst_tag_list_to_string(tagList) };
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const GstQuery *query)
+{
+ dbg << GST_QUERY_TYPE_NAME(query);
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const GstEvent *event)
+{
+ dbg << GST_EVENT_TYPE_NAME(event);
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const GstPadTemplate *padTemplate)
+{
+ QGstCaps caps = padTemplate
+ ? QGstCaps{ gst_pad_template_get_caps(const_cast<GstPadTemplate *>(padTemplate)), QGstCaps::HasRef, }
+ : QGstCaps{};
+
+ dbg << caps;
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, GstState state)
+{
+ return dbg << gst_element_state_get_name(state);
+}
+
+QDebug operator<<(QDebug dbg, GstStateChange transition)
+{
+ return dbg << gst_state_change_get_name(transition);
+}
+
+QDebug operator<<(QDebug dbg, GstStateChangeReturn stateChangeReturn)
+{
+ return dbg << gst_element_state_change_return_get_name(stateChangeReturn);
+}
+
+QDebug operator<<(QDebug dbg, GstMessageType type)
+{
+ return dbg << gst_message_type_get_name(type);
+}
+
+QDebug operator<<(QDebug dbg, GstPadDirection direction)
+{
+ switch (direction) {
+ case GST_PAD_UNKNOWN:
+ return dbg << "GST_PAD_UNKNOWN";
+ case GST_PAD_SRC:
+ return dbg << "GST_PAD_SRC";
+ case GST_PAD_SINK:
+ return dbg << "GST_PAD_SINK";
+ }
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const GValue *value)
+{
+ switch (G_VALUE_TYPE(value)) {
+ case G_TYPE_STRING:
+ return dbg << g_value_get_string(value);
+ case G_TYPE_BOOLEAN:
+ return dbg << g_value_get_boolean(value);
+ case G_TYPE_ULONG:
+ return dbg << g_value_get_ulong(value);
+ case G_TYPE_LONG:
+ return dbg << g_value_get_long(value);
+ case G_TYPE_UINT:
+ return dbg << g_value_get_uint(value);
+ case G_TYPE_INT:
+ return dbg << g_value_get_int(value);
+ case G_TYPE_UINT64:
+ return dbg << g_value_get_uint64(value);
+ case G_TYPE_INT64:
+ return dbg << g_value_get_int64(value);
+ case G_TYPE_FLOAT:
+ return dbg << g_value_get_float(value);
+ case G_TYPE_DOUBLE:
+ return dbg << g_value_get_double(value);
+ default:
+ break;
+ }
+
+ if (GST_VALUE_HOLDS_BITMASK(value)) {
+ QDebugStateSaver saver(dbg);
+ return dbg << Qt::hex << gst_value_get_bitmask(value);
+ }
+
+ if (GST_VALUE_HOLDS_FRACTION(value))
+ return dbg << gst_value_get_fraction_numerator(value) << "/"
+ << gst_value_get_fraction_denominator(value);
+
+ if (GST_VALUE_HOLDS_CAPS(value))
+ return dbg << gst_value_get_caps(value);
+
+ if (GST_VALUE_HOLDS_STRUCTURE(value))
+ return dbg << gst_value_get_structure(value);
+
+ if (GST_VALUE_HOLDS_ARRAY(value)) {
+ const guint size = gst_value_array_get_size(value);
+ const guint last = size - 1;
+ dbg << "[";
+ for (guint index = 0; index != size; ++index) {
+ dbg << gst_value_array_get_value(value, index);
+ if (index != last)
+ dbg << ", ";
+ }
+ dbg << "}";
+ return dbg;
+ }
+
+ if (G_VALUE_TYPE(value) == GST_TYPE_PAD_DIRECTION) {
+ GstPadDirection direction = static_cast<GstPadDirection>(g_value_get_enum(value));
+ return dbg << direction;
+ }
+
+ if (G_VALUE_TYPE(value) == GST_TYPE_PAD_TEMPLATE) {
+ GstPadTemplate *padTemplate = static_cast<GstPadTemplate *>(g_value_get_object(value));
+ return dbg << padTemplate;
+ }
+
+ dbg << "(not implemented: " << G_VALUE_TYPE_NAME(value) << ")";
+
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const GError *error)
+{
+ return dbg << error->message;
+}
+
+QCompactGstMessageAdaptor::QCompactGstMessageAdaptor(const QGstreamerMessage &m)
+ : QCompactGstMessageAdaptor{
+ m.message(),
+ }
+{
+}
+
+QCompactGstMessageAdaptor::QCompactGstMessageAdaptor(GstMessage *m)
+ : msg{
+ m,
+ }
+{
+}
+
+QDebug operator<<(QDebug dbg, const QCompactGstMessageAdaptor &m)
+{
+ std::optional<QDebugStateSaver> saver(dbg);
+ dbg.nospace();
+
+ switch (GST_MESSAGE_TYPE(m.msg)) {
+ case GST_MESSAGE_ERROR: {
+ QUniqueGErrorHandle err;
+ QGString debug;
+ gst_message_parse_error(m.msg, &err, &debug);
+ dbg << err << " (" << debug << ")";
+ return dbg;
+ }
+
+ case GST_MESSAGE_WARNING: {
+ QUniqueGErrorHandle err;
+ QGString debug;
+ gst_message_parse_warning(m.msg, &err, &debug);
+ dbg << err << " (" << debug << ")";
+ return dbg;
+ }
+
+ case GST_MESSAGE_INFO: {
+ QUniqueGErrorHandle err;
+ QGString debug;
+ gst_message_parse_info(m.msg, &err, &debug);
+
+ dbg << err << " (" << debug << ")";
+ return dbg;
+ }
+
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState oldState;
+ GstState newState;
+ GstState pending;
+
+ gst_message_parse_state_changed(m.msg, &oldState, &newState, &pending);
+
+ dbg << oldState << " -> " << newState;
+ if (pending != GST_STATE_VOID_PENDING)
+ dbg << " (pending: " << pending << ")";
+ return dbg;
+ }
+
+ default: {
+ saver.reset();
+ return dbg << m.msg;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgst_debug_p.h b/src/plugins/multimedia/gstreamer/common/qgst_debug_p.h
new file mode 100644
index 000000000..31c722a90
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst_debug_p.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGST_DEBUG_P_H
+#define QGST_DEBUG_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgst_p.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerMessage;
+
+QDebug operator<<(QDebug, const QGstCaps &);
+QDebug operator<<(QDebug, const QGstStructure &);
+QDebug operator<<(QDebug, const QGstElement &);
+QDebug operator<<(QDebug, const QGstPad &);
+QDebug operator<<(QDebug, const QGString &);
+QDebug operator<<(QDebug, const QGValue &);
+QDebug operator<<(QDebug, const QGstreamerMessage &);
+QDebug operator<<(QDebug, const QUniqueGErrorHandle &);
+QDebug operator<<(QDebug, const QUniqueGStringHandle &);
+
+QDebug operator<<(QDebug, const GstCaps *);
+QDebug operator<<(QDebug, const GstVideoInfo *);
+QDebug operator<<(QDebug, const GstStructure *);
+QDebug operator<<(QDebug, const GstObject *);
+QDebug operator<<(QDebug, const GstElement *);
+QDebug operator<<(QDebug, const GstPad *);
+QDebug operator<<(QDebug, const GstDevice *);
+QDebug operator<<(QDebug, const GstMessage *);
+QDebug operator<<(QDebug, const GstTagList *);
+QDebug operator<<(QDebug, const GstQuery *);
+QDebug operator<<(QDebug, const GstEvent *);
+QDebug operator<<(QDebug, const GstPadTemplate *);
+
+QDebug operator<<(QDebug, GstState);
+QDebug operator<<(QDebug, GstStateChange);
+QDebug operator<<(QDebug, GstStateChangeReturn);
+QDebug operator<<(QDebug, GstMessageType);
+QDebug operator<<(QDebug, GstPadDirection);
+
+QDebug operator<<(QDebug, const GValue *);
+QDebug operator<<(QDebug, const GError *);
+
+struct QCompactGstMessageAdaptor
+{
+ explicit QCompactGstMessageAdaptor(const QGstreamerMessage &m);
+ explicit QCompactGstMessageAdaptor(GstMessage *m);
+ GstMessage *msg;
+};
+
+QDebug operator<<(QDebug, const QCompactGstMessageAdaptor &);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h b/src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h
new file mode 100644
index 000000000..bda9d5683
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h
@@ -0,0 +1,267 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGST_HANDLE_TYPES_P_H
+#define QGST_HANDLE_TYPES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qcore_unix_p.h>
+#include <QtCore/private/quniquehandle_p.h>
+#include <QtCore/qtconfigmacros.h>
+
+#include <QtMultimedia/private/qtmultimedia-config_p.h>
+
+#include <gst/gst.h>
+
+#if QT_CONFIG(gstreamer_gl)
+# include <gst/gl/gstglcontext.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QGstImpl {
+
+template <typename HandleTraits>
+struct QSharedHandle : private QUniqueHandle<HandleTraits>
+{
+ using BaseClass = QUniqueHandle<HandleTraits>;
+
+ enum RefMode { HasRef, NeedsRef };
+
+ QSharedHandle() = default;
+
+ explicit QSharedHandle(typename HandleTraits::Type object, RefMode mode)
+ : BaseClass{ mode == NeedsRef ? HandleTraits::ref(object) : object }
+ {
+ }
+
+ QSharedHandle(const QSharedHandle &o)
+ : BaseClass{
+ HandleTraits::ref(o.get()),
+ }
+ {
+ }
+
+ QSharedHandle(QSharedHandle &&) noexcept = default;
+
+ QSharedHandle &operator=(const QSharedHandle &o) // NOLINT: bugprone-unhandled-self-assign
+ {
+ if (BaseClass::get() != o.get())
+ reset(HandleTraits::ref(o.get()));
+ return *this;
+ };
+
+ QSharedHandle &operator=(QSharedHandle &&) noexcept = default;
+
+ [[nodiscard]] friend bool operator==(const QSharedHandle &lhs,
+ const QSharedHandle &rhs) noexcept
+ {
+ return lhs.get() == rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator!=(const QSharedHandle &lhs,
+ const QSharedHandle &rhs) noexcept
+ {
+ return lhs.get() != rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator<(const QSharedHandle &lhs, const QSharedHandle &rhs) noexcept
+ {
+ return lhs.get() < rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator<=(const QSharedHandle &lhs,
+ const QSharedHandle &rhs) noexcept
+ {
+ return lhs.get() <= rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator>(const QSharedHandle &lhs, const QSharedHandle &rhs) noexcept
+ {
+ return lhs.get() > rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator>=(const QSharedHandle &lhs,
+ const QSharedHandle &rhs) noexcept
+ {
+ return lhs.get() >= rhs.get();
+ }
+
+ using BaseClass::get;
+ using BaseClass::isValid;
+ using BaseClass::operator bool;
+ using BaseClass::release;
+ using BaseClass::reset;
+ using BaseClass::operator&;
+ using BaseClass::close;
+};
+
+struct QGstTagListHandleTraits
+{
+ using Type = GstTagList *;
+ static constexpr Type invalidValue() noexcept { return nullptr; }
+ static bool close(Type handle) noexcept
+ {
+ gst_tag_list_unref(handle);
+ return true;
+ }
+ static Type ref(Type handle) noexcept { return gst_tag_list_ref(handle); }
+};
+
+struct QGstSampleHandleTraits
+{
+ using Type = GstSample *;
+ static constexpr Type invalidValue() noexcept { return nullptr; }
+ static bool close(Type handle) noexcept
+ {
+ gst_sample_unref(handle);
+ return true;
+ }
+ static Type ref(Type handle) noexcept { return gst_sample_ref(handle); }
+};
+
+struct QUniqueGstStructureHandleTraits
+{
+ using Type = GstStructure *;
+ static constexpr Type invalidValue() noexcept { return nullptr; }
+ static bool close(Type handle) noexcept
+ {
+ gst_structure_free(handle);
+ return true;
+ }
+};
+
+struct QUniqueGStringHandleTraits
+{
+ using Type = gchar *;
+ static constexpr Type invalidValue() noexcept { return nullptr; }
+ static bool close(Type handle) noexcept
+ {
+ g_free(handle);
+ return true;
+ }
+};
+
+struct QUniqueGErrorHandleTraits
+{
+ using Type = GError *;
+ static constexpr Type invalidValue() noexcept { return nullptr; }
+ static bool close(Type handle) noexcept
+ {
+ g_error_free(handle);
+ return true;
+ }
+};
+
+
+struct QUniqueGstDateTimeHandleTraits
+{
+ using Type = GstDateTime *;
+ static constexpr Type invalidValue() noexcept { return nullptr; }
+ static bool close(Type handle) noexcept
+ {
+ gst_date_time_unref(handle);
+ return true;
+ }
+};
+
+struct QFileDescriptorHandleTraits
+{
+ using Type = int;
+ static constexpr Type invalidValue() noexcept { return -1; }
+ static bool close(Type fd) noexcept
+ {
+ int closeResult = qt_safe_close(fd);
+ return closeResult == 0;
+ }
+};
+
+template <typename GstType>
+struct QGstHandleHelper
+{
+ struct QGstSafeObjectHandleTraits
+ {
+ using Type = GstType *;
+ static constexpr Type invalidValue() noexcept { return nullptr; }
+ static bool close(Type handle) noexcept
+ {
+ gst_object_unref(G_OBJECT(handle));
+ return true;
+ }
+
+ static Type ref(Type handle) noexcept
+ {
+ gst_object_ref_sink(G_OBJECT(handle));
+ return handle;
+ }
+ };
+
+ using SharedHandle = QSharedHandle<QGstSafeObjectHandleTraits>;
+ using UniqueHandle = QUniqueHandle<QGstSafeObjectHandleTraits>;
+};
+
+template <typename GstType>
+struct QGstMiniObjectHandleHelper
+{
+ struct Traits
+ {
+ using Type = GstType *;
+ static constexpr Type invalidValue() noexcept { return nullptr; }
+ static bool close(Type handle) noexcept
+ {
+ gst_mini_object_unref(GST_MINI_OBJECT_CAST(handle));
+ return true;
+ }
+
+ static Type ref(Type handle) noexcept
+ {
+ gst_mini_object_ref(GST_MINI_OBJECT_CAST(handle));
+ return handle;
+ }
+ };
+
+ using SharedHandle = QSharedHandle<Traits>;
+ using UniqueHandle = QUniqueHandle<Traits>;
+};
+
+} // namespace QGstImpl
+
+using QGstClockHandle = QGstImpl::QGstHandleHelper<GstClock>::UniqueHandle;
+using QGstElementHandle = QGstImpl::QGstHandleHelper<GstElement>::UniqueHandle;
+using QGstElementFactoryHandle = QGstImpl::QGstHandleHelper<GstElementFactory>::UniqueHandle;
+using QGstDeviceHandle = QGstImpl::QGstHandleHelper<GstDevice>::SharedHandle;
+using QGstDeviceMonitorHandle = QGstImpl::QGstHandleHelper<GstDeviceMonitor>::UniqueHandle;
+using QGstBusHandle = QGstImpl::QGstHandleHelper<GstBus>::UniqueHandle;
+
+using QGstTagListHandle = QGstImpl::QSharedHandle<QGstImpl::QGstTagListHandleTraits>;
+using QGstSampleHandle = QGstImpl::QSharedHandle<QGstImpl::QGstSampleHandleTraits>;
+
+using QUniqueGstStructureHandle = QUniqueHandle<QGstImpl::QUniqueGstStructureHandleTraits>;
+using QUniqueGStringHandle = QUniqueHandle<QGstImpl::QUniqueGStringHandleTraits>;
+using QUniqueGErrorHandle = QUniqueHandle<QGstImpl::QUniqueGErrorHandleTraits>;
+using QUniqueGstDateTimeHandle = QUniqueHandle<QGstImpl::QUniqueGstDateTimeHandleTraits>;
+using QFileDescriptorHandle = QUniqueHandle<QGstImpl::QFileDescriptorHandleTraits>;
+using QGstBufferHandle = QGstImpl::QGstMiniObjectHandleHelper<GstBuffer>::SharedHandle;
+using QGstContextHandle = QGstImpl::QGstMiniObjectHandleHelper<GstContext>::UniqueHandle;
+using QGstGstDateTimeHandle = QGstImpl::QGstMiniObjectHandleHelper<GstDateTime>::SharedHandle;
+using QGstPluginFeatureHandle = QGstImpl::QGstHandleHelper<GstPluginFeature>::SharedHandle;
+using QGstQueryHandle = QGstImpl::QGstMiniObjectHandleHelper<GstQuery>::SharedHandle;
+
+#if QT_CONFIG(gstreamer_gl)
+using QGstGLContextHandle = QGstImpl::QGstHandleHelper<GstGLContext>::UniqueHandle;
+using QGstGLDisplayHandle = QGstImpl::QGstHandleHelper<GstGLDisplay>::UniqueHandle;
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgst_p.h b/src/plugins/multimedia/gstreamer/common/qgst_p.h
index 66b1b156f..285a4ec47 100644
--- a/src/plugins/multimedia/gstreamer/common/qgst_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgst_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGST_P_H
#define QGST_P_H
@@ -51,30 +15,160 @@
// We mean it.
//
-#include <private/qtmultimediaglobal_p.h>
-
-#include <QSemaphore>
+#include <QtCore/qdebug.h>
#include <QtCore/qlist.h>
+#include <QtCore/qsemaphore.h>
#include <QtMultimedia/qaudioformat.h>
#include <QtMultimedia/qvideoframe.h>
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <QtMultimedia/private/qmultimediautils_p.h>
#include <gst/gst.h>
#include <gst/video/video-info.h>
-#include <functional>
+#include "qgst_handle_types_p.h"
+
+#include <type_traits>
#if QT_CONFIG(gstreamer_photography)
-#define GST_USE_UNSTABLE_API
-#include <gst/interfaces/photography.h>
-#undef GST_USE_UNSTABLE_API
+# define GST_USE_UNSTABLE_API
+# include <gst/interfaces/photography.h>
+# undef GST_USE_UNSTABLE_API
#endif
-#ifndef QT_NO_DEBUG
-#include <qdebug.h>
+
+#if QT_CONFIG(gstreamer_app)
+# include <gst/app/gstappsink.h>
+# include <gst/app/gstappsrc.h>
#endif
QT_BEGIN_NAMESPACE
+namespace QGstImpl {
+
+template <typename T>
+struct GstObjectTraits
+{
+ // using Type = T;
+ // template <typename U>
+ // static bool isObjectOfType(U *);
+ // template <typename U>
+ // static T *cast(U *);
+};
+
+#define QGST_DEFINE_CAST_TRAITS(ClassName, MACRO_LABEL) \
+ template <> \
+ struct GstObjectTraits<ClassName> \
+ { \
+ using Type = ClassName; \
+ template <typename U> \
+ static bool isObjectOfType(U *arg) \
+ { \
+ return GST_IS_##MACRO_LABEL(arg); \
+ } \
+ template <typename U> \
+ static Type *cast(U *arg) \
+ { \
+ return GST_##MACRO_LABEL##_CAST(arg); \
+ } \
+ template <typename U> \
+ static Type *checked_cast(U *arg) \
+ { \
+ return GST_##MACRO_LABEL(arg); \
+ } \
+ }; \
+ static_assert(true, "ensure semicolon")
+
+#define QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(ClassName, MACRO_LABEL) \
+ template <> \
+ struct GstObjectTraits<ClassName> \
+ { \
+ using Type = ClassName; \
+ template <typename U> \
+ static bool isObjectOfType(U *arg) \
+ { \
+ return GST_IS_##MACRO_LABEL(arg); \
+ } \
+ template <typename U> \
+ static Type *cast(U *arg) \
+ { \
+ return checked_cast(arg); \
+ } \
+ template <typename U> \
+ static Type *checked_cast(U *arg) \
+ { \
+ return GST_##MACRO_LABEL(arg); \
+ } \
+ }; \
+ static_assert(true, "ensure semicolon")
+
+QGST_DEFINE_CAST_TRAITS(GstBin, BIN);
+QGST_DEFINE_CAST_TRAITS(GstClock, CLOCK);
+QGST_DEFINE_CAST_TRAITS(GstElement, ELEMENT);
+QGST_DEFINE_CAST_TRAITS(GstObject, OBJECT);
+QGST_DEFINE_CAST_TRAITS(GstPad, PAD);
+QGST_DEFINE_CAST_TRAITS(GstPipeline, PIPELINE);
+QGST_DEFINE_CAST_TRAITS(GstBaseSink, BASE_SINK);
+QGST_DEFINE_CAST_TRAITS(GstBaseSrc, BASE_SRC);
+
+QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(GstTagSetter, TAG_SETTER);
+
+#if QT_CONFIG(gstreamer_app)
+QGST_DEFINE_CAST_TRAITS(GstAppSink, APP_SINK);
+QGST_DEFINE_CAST_TRAITS(GstAppSrc, APP_SRC);
+#endif
+
+template <>
+struct GstObjectTraits<GObject>
+{
+ using Type = GObject;
+ template <typename U>
+ static bool isObjectOfType(U *arg)
+ {
+ return G_IS_OBJECT(arg);
+ }
+ template <typename U>
+ static Type *cast(U *arg)
+ {
+ return G_OBJECT(arg);
+ }
+ template <typename U>
+ static Type *checked_cast(U *arg)
+ {
+ return G_OBJECT(arg);
+ }
+};
+
+#undef QGST_DEFINE_CAST_TRAITS
+#undef QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE
+
+} // namespace QGstImpl
+
+template <typename DestinationType, typename SourceType>
+bool qIsGstObjectOfType(SourceType *arg)
+{
+ using Traits = QGstImpl::GstObjectTraits<DestinationType>;
+ return arg && Traits::isObjectOfType(arg);
+}
+
+template <typename DestinationType, typename SourceType>
+DestinationType *qGstSafeCast(SourceType *arg)
+{
+ using Traits = QGstImpl::GstObjectTraits<DestinationType>;
+ if (arg && Traits::isObjectOfType(arg))
+ return Traits::cast(arg);
+ return nullptr;
+}
+
+template <typename DestinationType, typename SourceType>
+DestinationType *qGstCheckedCast(SourceType *arg)
+{
+ using Traits = QGstImpl::GstObjectTraits<DestinationType>;
+ if (arg)
+ Q_ASSERT(Traits::isObjectOfType(arg));
+ return Traits::cast(arg);
+}
+
class QSize;
class QGstStructure;
class QGstCaps;
@@ -87,265 +181,283 @@ template <typename T> struct QGRange
T max;
};
-class QGString
+struct QGString : QUniqueGStringHandle
{
- char *str;
-public:
- QGString(char *string) : str(string) {}
- ~QGString() { g_free(str); }
- operator QByteArray() { return QByteArray(str); }
- operator const char *() { return str; }
+ using QUniqueGStringHandle::QUniqueGStringHandle;
+
+ QLatin1StringView asStringView() const { return QLatin1StringView{ get() }; }
+ QString toQString() const { return QString::fromUtf8(get()); }
};
class QGValue
{
public:
- QGValue(const GValue *v) : value(v) {}
+ explicit QGValue(const GValue *v);
const GValue *value;
- bool isNull() const { return !value; }
+ bool isNull() const;
- std::optional<bool> toBool() const
+ std::optional<bool> toBool() const;
+ std::optional<int> toInt() const;
+ std::optional<int> toInt64() const;
+ template<typename T>
+ T *getPointer() const
{
- if (!G_VALUE_HOLDS_BOOLEAN(value))
- return std::nullopt;
- return g_value_get_boolean(value);
+ return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr;
}
- std::optional<int> toInt() const
+
+ const char *toString() const;
+ std::optional<float> getFraction() const;
+ std::optional<QGRange<float>> getFractionRange() const;
+ std::optional<QGRange<int>> toIntRange() const;
+
+ QGstStructure toStructure() const;
+ QGstCaps toCaps() const;
+
+ bool isList() const;
+ int listSize() const;
+ QGValue at(int index) const;
+
+ QList<QAudioFormat::SampleFormat> getSampleFormats() const;
+};
+
+namespace QGstPointerImpl {
+
+template <typename RefcountedObject>
+struct QGstRefcountingAdaptor;
+
+template <typename GstType>
+class QGstObjectWrapper
+{
+ using Adaptor = QGstRefcountingAdaptor<GstType>;
+
+ GstType *m_object = nullptr;
+
+public:
+ enum RefMode { HasRef, NeedsRef };
+
+ constexpr QGstObjectWrapper() = default;
+
+ explicit QGstObjectWrapper(GstType *object, RefMode mode) : m_object(object)
{
- if (!G_VALUE_HOLDS_INT(value))
- return std::nullopt;
- return g_value_get_int(value);
+ if (m_object && mode == NeedsRef)
+ Adaptor::ref(m_object);
}
- std::optional<int> toInt64() const
+
+ QGstObjectWrapper(const QGstObjectWrapper &other) : m_object(other.m_object)
{
- if (!G_VALUE_HOLDS_INT64(value))
- return std::nullopt;
- return g_value_get_int64(value);
+ if (m_object)
+ Adaptor::ref(m_object);
}
- template<typename T>
- T *getPointer() const
+
+ ~QGstObjectWrapper()
{
- return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr;
+ if (m_object)
+ Adaptor::unref(m_object);
}
- const char *toString() const
+ QGstObjectWrapper(QGstObjectWrapper &&other) noexcept
+ : m_object(std::exchange(other.m_object, nullptr))
{
- return value ? g_value_get_string(value) : nullptr;
}
- std::optional<float> getFraction() const
+
+ QGstObjectWrapper &
+ operator=(const QGstObjectWrapper &other) // NOLINT: bugprone-unhandled-self-assign
{
- if (!GST_VALUE_HOLDS_FRACTION(value))
- return std::nullopt;
- return (float)gst_value_get_fraction_numerator(value)/(float)gst_value_get_fraction_denominator(value);
+ if (m_object != other.m_object) {
+ GstType *originalObject = m_object;
+
+ m_object = other.m_object;
+ if (m_object)
+ Adaptor::ref(m_object);
+ if (originalObject)
+ Adaptor::unref(originalObject);
+ }
+ return *this;
}
- std::optional<QGRange<float>> getFractionRange() const
+ QGstObjectWrapper &operator=(QGstObjectWrapper &&other) noexcept
{
- if (!GST_VALUE_HOLDS_FRACTION_RANGE(value))
- return std::nullopt;
- QGValue min = gst_value_get_fraction_range_min(value);
- QGValue max = gst_value_get_fraction_range_max(value);
- return QGRange<float>{ *min.getFraction(), *max.getFraction() };
+ if (this != &other) {
+ GstType *originalObject = m_object;
+ m_object = std::exchange(other.m_object, nullptr);
+
+ if (originalObject)
+ Adaptor::unref(originalObject);
+ }
+ return *this;
}
- std::optional<QGRange<int>> toIntRange() const
+ friend bool operator==(const QGstObjectWrapper &a, const QGstObjectWrapper &b)
{
- if (!GST_VALUE_HOLDS_INT_RANGE(value))
- return std::nullopt;
- return QGRange<int>{ gst_value_get_int_range_min(value), gst_value_get_int_range_max(value) };
+ return a.m_object == b.m_object;
+ }
+ friend bool operator!=(const QGstObjectWrapper &a, const QGstObjectWrapper &b)
+ {
+ return a.m_object != b.m_object;
}
- inline QGstStructure toStructure() const;
- inline QGstCaps toCaps() const;
-
- inline bool isList() const { return value && GST_VALUE_HOLDS_LIST(value); }
- inline int listSize() const { return gst_value_list_get_size(value); }
- inline QGValue at(int index) const { return gst_value_list_get_value(value, index); }
+ explicit operator bool() const { return bool(m_object); }
+ bool isNull() const { return !m_object; }
+ GstType *release() { return std::exchange(m_object, nullptr); }
- Q_MULTIMEDIA_EXPORT QList<QAudioFormat::SampleFormat> getSampleFormats() const;
+protected:
+ GstType *get() const { return m_object; }
};
-class QGstStructure {
+} // namespace QGstPointerImpl
+
+class QGstreamerMessage;
+
+class QGstStructure
+{
public:
const GstStructure *structure = nullptr;
QGstStructure() = default;
- QGstStructure(const GstStructure *s) : structure(s) {}
- void free() { if (structure) gst_structure_free(const_cast<GstStructure *>(structure)); structure = nullptr; }
+ QGstStructure(const GstStructure *s);
+ void free();
- bool isNull() const { return !structure; }
+ bool isNull() const;
- QByteArrayView name() const { return gst_structure_get_name(structure); }
+ QByteArrayView name() const;
+ QGValue operator[](const char *name) const;
- QGValue operator[](const char *name) const { return gst_structure_get_value(structure, name); }
+ QSize resolution() const;
+ QVideoFrameFormat::PixelFormat pixelFormat() const;
+ QGRange<float> frameRateRange() const;
+ QGstreamerMessage getMessage();
+ std::optional<Fraction> pixelAspectRatio() const;
+ QSize nativeSize() const;
- Q_MULTIMEDIA_EXPORT QSize resolution() const;
- Q_MULTIMEDIA_EXPORT QVideoFrameFormat::PixelFormat pixelFormat() const;
- Q_MULTIMEDIA_EXPORT QGRange<float> frameRateRange() const;
+ QGstStructure copy() const;
+};
- QByteArray toString() const
- {
- char *s = gst_structure_to_string(structure);
- QByteArray str(s);
- g_free(s);
- return str;
- }
- QGstStructure copy() const { return gst_structure_copy(structure); }
+template <>
+struct QGstPointerImpl::QGstRefcountingAdaptor<GstCaps>
+{
+ static void ref(GstCaps *arg) noexcept { gst_caps_ref(arg); }
+ static void unref(GstCaps *arg) noexcept { gst_caps_unref(arg); }
};
-class QGstCaps {
- const GstCaps *caps = nullptr;
+class QGstCaps : public QGstPointerImpl::QGstObjectWrapper<GstCaps>
+{
+ using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstCaps>;
+
public:
- enum MemoryFormat {
- CpuMemory,
- GLTexture,
- DMABuf
- };
+ using BaseClass::BaseClass;
+ QGstCaps(const QGstCaps &) = default;
+ QGstCaps(QGstCaps &&) noexcept = default;
+ QGstCaps &operator=(const QGstCaps &) = default;
+ QGstCaps &operator=(QGstCaps &&) noexcept = default;
- QGstCaps() = default;
- QGstCaps(const GstCaps *c) : caps(c) {}
+ enum MemoryFormat { CpuMemory, GLTexture, DMABuf };
- bool isNull() const { return !caps; }
+ int size() const;
+ QGstStructure at(int index) const;
+ GstCaps *caps() const;
- int size() const { return gst_caps_get_size(caps); }
- QGstStructure at(int index) { return gst_caps_get_structure(caps, index); }
- const GstCaps *get() const { return caps; }
- QByteArray toString() const
- {
- gchar *c = gst_caps_to_string(caps);
- QByteArray b(c);
- g_free(c);
- return b;
- }
- MemoryFormat memoryFormat() const {
- auto *features = gst_caps_get_features(caps, 0);
- if (gst_caps_features_contains(features, "memory:GLMemory"))
- return GLTexture;
- else if (gst_caps_features_contains(features, "memory:DMABuf"))
- return DMABuf;
- return CpuMemory;
- }
- QVideoFrameFormat formatForCaps(GstVideoInfo *info) const;
+ MemoryFormat memoryFormat() const;
+ std::optional<std::pair<QVideoFrameFormat, GstVideoInfo>> formatAndVideoInfo() const;
+
+ void addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier = nullptr);
+ void setResolution(QSize);
+
+ static QGstCaps create();
+
+ static QGstCaps fromCameraFormat(const QCameraFormat &format);
+
+ QGstCaps copy() const;
};
-class QGstMutableCaps : public QGstCaps {
- GstCaps *caps = nullptr;
+template <>
+struct QGstPointerImpl::QGstRefcountingAdaptor<GstObject>
+{
+ static void ref(GstObject *arg) noexcept { gst_object_ref_sink(arg); }
+ static void unref(GstObject *arg) noexcept { gst_object_unref(arg); }
+};
+
+class QGObjectHandlerConnection;
+
+class QGstObject : public QGstPointerImpl::QGstObjectWrapper<GstObject>
+{
+ using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstObject>;
+
public:
- enum RefMode { HasRef, NeedsRef };
- QGstMutableCaps() = default;
- QGstMutableCaps(GstCaps *c, RefMode mode = HasRef)
- : QGstCaps(c), caps(c)
- {
- Q_ASSERT(QGstCaps::get() == caps);
- if (mode == NeedsRef)
- gst_caps_ref(caps);
- }
- QGstMutableCaps(const QGstMutableCaps &other)
- : QGstCaps(other), caps(other.caps)
- {
- Q_ASSERT(QGstCaps::get() == caps);
- if (caps)
- gst_caps_ref(caps);
- }
- QGstMutableCaps &operator=(const QGstMutableCaps &other)
- {
- QGstCaps::operator=(other);
- if (other.caps)
- gst_caps_ref(other.caps);
- if (caps)
- gst_caps_unref(caps);
- caps = other.caps;
- Q_ASSERT(QGstCaps::get() == caps);
- return *this;
- }
- ~QGstMutableCaps() {
- Q_ASSERT(QGstCaps::get() == caps);
- if (caps)
- gst_caps_unref(caps);
- }
+ using BaseClass::BaseClass;
+ QGstObject(const QGstObject &) = default;
+ QGstObject(QGstObject &&) noexcept = default;
+
+ QGstObject &operator=(const QGstObject &) = default;
+ QGstObject &operator=(QGstObject &&) noexcept = default;
+
+ void set(const char *property, const char *str);
+ void set(const char *property, bool b);
+ void set(const char *property, uint i);
+ void set(const char *property, int i);
+ void set(const char *property, qint64 i);
+ void set(const char *property, quint64 i);
+ void set(const char *property, double d);
+ void set(const char *property, const QGstObject &o);
+ void set(const char *property, const QGstCaps &c);
+
+ QGString getString(const char *property) const;
+ QGstStructure getStructure(const char *property) const;
+ bool getBool(const char *property) const;
+ uint getUInt(const char *property) const;
+ int getInt(const char *property) const;
+ quint64 getUInt64(const char *property) const;
+ qint64 getInt64(const char *property) const;
+ float getFloat(const char *property) const;
+ double getDouble(const char *property) const;
+ QGstObject getObject(const char *property) const;
+
+ QGObjectHandlerConnection connect(const char *name, GCallback callback, gpointer userData);
+ void disconnect(gulong handlerId);
+
+ GType type() const;
+ const char *typeName() const;
+ GstObject *object() const;
+ const char *name() const;
+};
- void create() {
- caps = gst_caps_new_empty();
- QGstCaps::operator=(QGstCaps(caps));
- }
+class QGObjectHandlerConnection
+{
+public:
+ QGObjectHandlerConnection(QGstObject object, gulong handler);
- void addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier = nullptr);
- static QGstMutableCaps fromCameraFormat(const QCameraFormat &format);
+ QGObjectHandlerConnection() = default;
+ QGObjectHandlerConnection(const QGObjectHandlerConnection &) = default;
+ QGObjectHandlerConnection(QGObjectHandlerConnection &&) = default;
+ QGObjectHandlerConnection &operator=(const QGObjectHandlerConnection &) = default;
+ QGObjectHandlerConnection &operator=(QGObjectHandlerConnection &&) = default;
+
+ void disconnect();
- GstCaps *get() const { return caps; }
+private:
+ static constexpr gulong invalidHandlerId = std::numeric_limits<gulong>::max();
+
+ QGstObject object;
+ gulong handlerId = invalidHandlerId;
};
-class QGstObject
+// disconnects in dtor
+class QGObjectHandlerScopedConnection
{
-protected:
- GstObject *m_object = nullptr;
public:
- enum RefMode { HasRef, NeedsRef };
+ QGObjectHandlerScopedConnection(QGObjectHandlerConnection connection);
- QGstObject() = default;
- explicit QGstObject(GstObject *o, RefMode mode = HasRef)
- : m_object(o)
- {
- if (o && mode == NeedsRef)
- // Use ref_sink to remove any floating references
- gst_object_ref_sink(m_object);
- }
- QGstObject(const QGstObject &other)
- : m_object(other.m_object)
- {
- if (m_object)
- gst_object_ref(m_object);
- }
- QGstObject &operator=(const QGstObject &other)
- {
- if (this == &other)
- return *this;
- if (other.m_object)
- gst_object_ref(other.m_object);
- if (m_object)
- gst_object_unref(m_object);
- m_object = other.m_object;
- return *this;
- }
- virtual ~QGstObject() {
- if (m_object)
- gst_object_unref(m_object);
- }
+ QGObjectHandlerScopedConnection() = default;
+ QGObjectHandlerScopedConnection(const QGObjectHandlerScopedConnection &) = delete;
+ QGObjectHandlerScopedConnection &operator=(const QGObjectHandlerScopedConnection &) = delete;
+ QGObjectHandlerScopedConnection(QGObjectHandlerScopedConnection &&) = default;
+ QGObjectHandlerScopedConnection &operator=(QGObjectHandlerScopedConnection &&) = default;
- friend bool operator==(const QGstObject &a, const QGstObject &b)
- { return a.m_object == b.m_object; }
- friend bool operator!=(const QGstObject &a, const QGstObject &b)
- { return a.m_object != b.m_object; }
+ ~QGObjectHandlerScopedConnection();
- bool isNull() const { return !m_object; }
+ void disconnect();
- void set(const char *property, const char *str) { g_object_set(m_object, property, str, nullptr); }
- void set(const char *property, bool b) { g_object_set(m_object, property, gboolean(b), nullptr); }
- void set(const char *property, uint i) { g_object_set(m_object, property, guint(i), nullptr); }
- void set(const char *property, int i) { g_object_set(m_object, property, gint(i), nullptr); }
- void set(const char *property, qint64 i) { g_object_set(m_object, property, gint64(i), nullptr); }
- void set(const char *property, quint64 i) { g_object_set(m_object, property, guint64(i), nullptr); }
- void set(const char *property, double d) { g_object_set(m_object, property, gdouble(d), nullptr); }
- void set(const char *property, const QGstObject &o) { g_object_set(m_object, property, o.object(), nullptr); }
- void set(const char *property, const QGstMutableCaps &c) { g_object_set(m_object, property, c.get(), nullptr); }
-
- QGString getString(const char *property) const
- { char *s = nullptr; g_object_get(m_object, property, &s, nullptr); return s; }
- QGstStructure getStructure(const char *property) const
- { GstStructure *s = nullptr; g_object_get(m_object, property, &s, nullptr); return QGstStructure(s); }
- bool getBool(const char *property) const { gboolean b = false; g_object_get(m_object, property, &b, nullptr); return b; }
- uint getUInt(const char *property) const { guint i = 0; g_object_get(m_object, property, &i, nullptr); return i; }
- int getInt(const char *property) const { gint i = 0; g_object_get(m_object, property, &i, nullptr); return i; }
- quint64 getUInt64(const char *property) const { guint64 i = 0; g_object_get(m_object, property, &i, nullptr); return i; }
- qint64 getInt64(const char *property) const { gint64 i = 0; g_object_get(m_object, property, &i, nullptr); return i; }
- float getFloat(const char *property) const { gfloat d = 0; g_object_get(m_object, property, &d, nullptr); return d; }
- double getDouble(const char *property) const { gdouble d = 0; g_object_get(m_object, property, &d, nullptr); return d; }
- QGstObject getObject(const char *property) const { GstObject *o = nullptr; g_object_get(m_object, property, &o, nullptr); return QGstObject(o, HasRef); }
-
- void connect(const char *name, GCallback callback, gpointer userData) { g_signal_connect(m_object, name, callback, userData); }
-
- GstObject *object() const { return m_object; }
- const char *name() const { return m_object ? GST_OBJECT_NAME(m_object) : "(null)"; }
+private:
+ QGObjectHandlerConnection connection;
};
class QGstElement;
@@ -353,48 +465,52 @@ class QGstElement;
class QGstPad : public QGstObject
{
public:
- QGstPad() = default;
- QGstPad(const QGstObject &o)
- : QGstPad(GST_PAD(o.object()), NeedsRef)
- {}
- QGstPad(GstPad *pad, RefMode mode = NeedsRef)
- : QGstObject(&pad->object, mode)
- {}
-
- QGstMutableCaps currentCaps() const
- { return QGstMutableCaps(gst_pad_get_current_caps(pad())); }
- QGstCaps queryCaps() const
- { return QGstCaps(gst_pad_query_caps(pad(), nullptr)); }
-
- bool isLinked() const { return gst_pad_is_linked(pad()); }
- bool link(const QGstPad &sink) const { return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK; }
- bool unlink(const QGstPad &sink) const { return gst_pad_unlink(pad(), sink.pad()); }
- bool unlinkPeer() const { return unlink(peer()); }
- QGstPad peer() const { return QGstPad(gst_pad_get_peer(pad()), HasRef); }
- inline QGstElement parent() const;
-
- GstPad *pad() const { return GST_PAD_CAST(object()); }
-
- GstEvent *stickyEvent(GstEventType type) { return gst_pad_get_sticky_event(pad(), type, 0); }
- bool sendEvent(GstEvent *event) { return gst_pad_send_event (pad(), event); }
+ using QGstObject::QGstObject;
+ QGstPad(const QGstPad &) = default;
+ QGstPad(QGstPad &&) noexcept = default;
+
+ explicit QGstPad(const QGstObject &o);
+ explicit QGstPad(GstPad *pad, RefMode mode);
+
+ QGstPad &operator=(const QGstPad &) = default;
+ QGstPad &operator=(QGstPad &&) noexcept = default;
+
+ QGstCaps currentCaps() const;
+ QGstCaps queryCaps() const;
+
+ bool isLinked() const;
+ bool link(const QGstPad &sink) const;
+ bool unlink(const QGstPad &sink) const;
+ bool unlinkPeer() const;
+ QGstPad peer() const;
+ QGstElement parent() const;
+
+ GstPad *pad() const;
+
+ GstEvent *stickyEvent(GstEventType type);
+ bool sendEvent(GstEvent *event);
template<auto Member, typename T>
void addProbe(T *instance, GstPadProbeType type) {
- struct Impl {
- static GstPadProbeReturn callback(GstPad *pad, GstPadProbeInfo *info, gpointer userData) {
- return (static_cast<T *>(userData)->*Member)(QGstPad(pad, NeedsRef), info);
- };
+ auto callback = [](GstPad *pad, GstPadProbeInfo *info, gpointer userData) {
+ return (static_cast<T *>(userData)->*Member)(QGstPad(pad, NeedsRef), info);
};
- gst_pad_add_probe (pad(), type, Impl::callback, instance, nullptr);
+ gst_pad_add_probe(pad(), type, callback, instance, nullptr);
}
- void doInIdleProbe(std::function<void()> work) {
+ template <typename Functor>
+ void doInIdleProbe(Functor &&work)
+ {
struct CallbackData {
QSemaphore waitDone;
- std::function<void()> work;
- } cd;
- cd.work = work;
+ Functor work;
+ };
+
+ CallbackData cd{
+ .waitDone = QSemaphore{},
+ .work = std::forward<Functor>(work),
+ };
auto callback= [](GstPad *, GstPadProbeInfo *, gpointer p) {
auto cd = reinterpret_cast<CallbackData*>(p);
@@ -409,16 +525,14 @@ public:
template<auto Member, typename T>
void addEosProbe(T *instance) {
- struct Impl {
- static GstPadProbeReturn callback(GstPad */*pad*/, GstPadProbeInfo *info, gpointer userData) {
- if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_DATA(info)) != GST_EVENT_EOS)
- return GST_PAD_PROBE_PASS;
- (static_cast<T *>(userData)->*Member)();
- return GST_PAD_PROBE_REMOVE;
- };
+ auto callback = [](GstPad *, GstPadProbeInfo *info, gpointer userData) {
+ if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_DATA(info)) != GST_EVENT_EOS)
+ return GST_PAD_PROBE_PASS;
+ (static_cast<T *>(userData)->*Member)();
+ return GST_PAD_PROBE_REMOVE;
};
- gst_pad_add_probe (pad(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, Impl::callback, instance, nullptr);
+ gst_pad_add_probe(pad(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, callback, instance, nullptr);
}
};
@@ -426,195 +540,271 @@ class QGstClock : public QGstObject
{
public:
QGstClock() = default;
- QGstClock(const QGstObject &o)
- : QGstClock(GST_CLOCK(o.object()))
- {}
- QGstClock(GstClock *clock, RefMode mode = NeedsRef)
- : QGstObject(&clock->object, mode)
- {}
+ explicit QGstClock(const QGstObject &o);
+ explicit QGstClock(GstClock *clock, RefMode mode);
- GstClock *clock() const { return GST_CLOCK_CAST(object()); }
-
- GstClockTime time() const { return gst_clock_get_time(clock()); }
+ GstClock *clock() const;
+ GstClockTime time() const;
};
+class QGstPipeline;
+
class QGstElement : public QGstObject
{
public:
- QGstElement() = default;
- QGstElement(const QGstObject &o)
- : QGstElement(GST_ELEMENT(o.object()), NeedsRef)
- {}
- QGstElement(GstElement *element, RefMode mode = NeedsRef)
- : QGstObject(&element->object, mode)
- {}
-
- QGstElement(const char *factory, const char *name = nullptr)
- : QGstElement(gst_element_factory_make(factory, name), NeedsRef)
- {
- }
-
- bool linkFiltered(const QGstElement &next, const QGstMutableCaps &caps)
- { return gst_element_link_filtered(element(), next.element(), caps.get()); }
- bool link(const QGstElement &next)
- { return gst_element_link(element(), next.element()); }
- bool link(const QGstElement &n1, const QGstElement &n2)
- { return gst_element_link_many(element(), n1.element(), n2.element(), nullptr); }
- bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3)
- { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), nullptr); }
- bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3, const QGstElement &n4)
- { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), n4.element(), nullptr); }
- bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3, const QGstElement &n4, const QGstElement &n5)
- { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), n4.element(), n5.element(), nullptr); }
-
- void unlink(const QGstElement &next)
- { gst_element_unlink(element(), next.element()); }
-
- QGstPad staticPad(const char *name) const { return QGstPad(gst_element_get_static_pad(element(), name), HasRef); }
- QGstPad src() const { return staticPad("src"); }
- QGstPad sink() const { return staticPad("sink"); }
- QGstPad getRequestPad(const char *name) const
- {
-#if GST_CHECK_VERSION(1,19,1)
- return QGstPad(gst_element_request_pad_simple(element(), name), HasRef);
-#else
- return QGstPad(gst_element_get_request_pad(element(), name), HasRef);
-#endif
- }
- void releaseRequestPad(const QGstPad &pad) const { return gst_element_release_request_pad(element(), pad.pad()); }
-
- GstState state() const
+ using QGstObject::QGstObject;
+
+ QGstElement(const QGstElement &) = default;
+ QGstElement(QGstElement &&) noexcept = default;
+ QGstElement &operator=(const QGstElement &) = default;
+ QGstElement &operator=(QGstElement &&) noexcept = default;
+
+ explicit QGstElement(GstElement *element, RefMode mode);
+ static QGstElement createFromFactory(const char *factory, const char *name = nullptr);
+ static QGstElement createFromFactory(GstElementFactory *, const char *name = nullptr);
+ static QGstElement createFromFactory(const QGstElementFactoryHandle &,
+ const char *name = nullptr);
+ static QGstElement createFromDevice(const QGstDeviceHandle &, const char *name = nullptr);
+ static QGstElement createFromDevice(GstDevice *, const char *name = nullptr);
+ static QGstElement createFromPipelineDescription(const char *);
+ static QGstElement createFromPipelineDescription(const QByteArray &);
+
+ QGstPad staticPad(const char *name) const;
+ QGstPad src() const;
+ QGstPad sink() const;
+ QGstPad getRequestPad(const char *name) const;
+ void releaseRequestPad(const QGstPad &pad) const;
+
+ GstState state(std::chrono::nanoseconds timeout = std::chrono::seconds(0)) const;
+ GstStateChangeReturn setState(GstState state);
+ bool setStateSync(GstState state, std::chrono::nanoseconds timeout = std::chrono::seconds(1));
+ bool syncStateWithParent();
+ bool finishStateChange(std::chrono::nanoseconds timeout = std::chrono::seconds(5));
+
+ void lockState(bool locked);
+ bool isStateLocked() const;
+
+ void sendEvent(GstEvent *event) const;
+ void sendEos() const;
+
+ template <auto Member, typename T>
+ QGObjectHandlerConnection onPadAdded(T *instance)
{
- GstState state;
- gst_element_get_state(element(), &state, nullptr, 0);
- return state;
- }
- GstStateChangeReturn setState(GstState state) { return gst_element_set_state(element(), state); }
- bool setStateSync(GstState state)
- {
- auto change = gst_element_set_state(element(), state);
- if (change == GST_STATE_CHANGE_ASYNC) {
- change = gst_element_get_state(element(), nullptr, &state, 1000*1e6 /*nano seconds*/);
- }
-#ifndef QT_NO_DEBUG
- if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL)
- qWarning() << "Could not change state of" << name() << "to" << state << change;
-#endif
- return change == GST_STATE_CHANGE_SUCCESS;
- }
- bool syncStateWithParent() { return gst_element_sync_state_with_parent(element()) == TRUE; }
- bool finishStateChange()
- {
- auto change = gst_element_get_state(element(), nullptr, nullptr, 1000*1e6 /*nano seconds*/);
-#ifndef QT_NO_DEBUG
- if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL)
- qWarning() << "Could finish change state of" << name();
-#endif
- return change == GST_STATE_CHANGE_SUCCESS;
- }
-
- void lockState(bool locked) { gst_element_set_locked_state(element(), locked); }
- bool isStateLocked() const { return gst_element_is_locked_state(element()); }
-
- void sendEvent(GstEvent *event) const { gst_element_send_event(element(), event); }
- void sendEos() const { sendEvent(gst_event_new_eos()); }
-
- template<auto Member, typename T>
- void onPadAdded(T *instance) {
- struct Impl {
- static void callback(GstElement *e, GstPad *pad, gpointer userData) {
- (static_cast<T *>(userData)->*Member)(QGstElement(e), QGstPad(pad, NeedsRef));
+ struct Impl
+ {
+ static void callback(GstElement *e, GstPad *pad, gpointer userData)
+ {
+ (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef),
+ QGstPad(pad, NeedsRef));
};
};
- connect("pad-added", G_CALLBACK(Impl::callback), instance);
+ return connect("pad-added", G_CALLBACK(Impl::callback), instance);
}
- template<auto Member, typename T>
- void onPadRemoved(T *instance) {
- struct Impl {
- static void callback(GstElement *e, GstPad *pad, gpointer userData) {
- (static_cast<T *>(userData)->*Member)(QGstElement(e), QGstPad(pad, NeedsRef));
+ template <auto Member, typename T>
+ QGObjectHandlerConnection onPadRemoved(T *instance)
+ {
+ struct Impl
+ {
+ static void callback(GstElement *e, GstPad *pad, gpointer userData)
+ {
+ (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef),
+ QGstPad(pad, NeedsRef));
};
};
- connect("pad-removed", G_CALLBACK(Impl::callback), instance);
+ return connect("pad-removed", G_CALLBACK(Impl::callback), instance);
}
- template<auto Member, typename T>
- void onNoMorePads(T *instance) {
- struct Impl {
- static void callback(GstElement *e, gpointer userData) {
- (static_cast<T *>(userData)->*Member)(QGstElement(e));
+ template <auto Member, typename T>
+ QGObjectHandlerConnection onNoMorePads(T *instance)
+ {
+ struct Impl
+ {
+ static void callback(GstElement *e, gpointer userData)
+ {
+ (static_cast<T *>(userData)->*Member)(QGstElement(e, NeedsRef));
};
};
- connect("no-more-pads", G_CALLBACK(Impl::callback), instance);
+ return connect("no-more-pads", G_CALLBACK(Impl::callback), instance);
}
- GstClockTime baseTime() const { return gst_element_get_base_time(element()); }
- void setBaseTime(GstClockTime time) const { gst_element_set_base_time(element(), time); }
+ GstClockTime baseTime() const;
+ void setBaseTime(GstClockTime time) const;
- GstElement *element() const { return GST_ELEMENT_CAST(m_object); }
+ GstElement *element() const;
+
+ QGstElement getParent() const;
+ QGstPipeline getPipeline() const;
};
-inline QGstElement QGstPad::parent() const
+template <typename... Ts>
+std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void>
+qLinkGstElements(const Ts &...ts)
{
- return QGstElement(gst_pad_get_parent_element(pad()), HasRef);
+ bool link_success = [&] {
+ if constexpr (sizeof...(Ts) == 2)
+ return gst_element_link(ts.element()...);
+ else
+ return gst_element_link_many(ts.element()..., nullptr);
+ }();
+
+ if (Q_UNLIKELY(!link_success)) {
+ qWarning() << "qLinkGstElements: could not link elements: "
+ << std::initializer_list<const char *>{
+ (GST_ELEMENT_NAME(ts.element()))...,
+ };
+ }
+}
+
+template <typename... Ts>
+std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void>
+qUnlinkGstElements(const Ts &...ts)
+{
+ if constexpr (sizeof...(Ts) == 2)
+ gst_element_unlink(ts.element()...);
+ else
+ gst_element_unlink_many(ts.element()..., nullptr);
}
class QGstBin : public QGstElement
{
public:
- QGstBin() = default;
- QGstBin(const QGstObject &o)
- : QGstBin(GST_BIN(o.object()), NeedsRef)
- {}
- QGstBin(const char *name)
- : QGstElement(gst_bin_new(name), NeedsRef)
+ using QGstElement::QGstElement;
+ QGstBin(const QGstBin &) = default;
+ QGstBin(QGstBin &&) noexcept = default;
+ QGstBin &operator=(const QGstBin &) = default;
+ QGstBin &operator=(QGstBin &&) noexcept = default;
+
+ explicit QGstBin(GstBin *bin, RefMode mode = NeedsRef);
+ static QGstBin create(const char *name);
+ static QGstBin createFromFactory(const char *factory, const char *name);
+ static QGstBin createFromPipelineDescription(const QByteArray &pipelineDescription,
+ const char *name = nullptr,
+ bool ghostUnlinkedPads = false);
+ static QGstBin createFromPipelineDescription(const char *pipelineDescription,
+ const char *name = nullptr,
+ bool ghostUnlinkedPads = false);
+
+ template <typename... Ts>
+ std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> add(const Ts &...ts)
{
+ if constexpr (sizeof...(Ts) == 1)
+ gst_bin_add(bin(), ts.element()...);
+ else
+ gst_bin_add_many(bin(), ts.element()..., nullptr);
}
- QGstBin(GstBin *bin, RefMode mode = NeedsRef)
- : QGstElement(&bin->element, mode)
- {}
-
- void add(const QGstElement &element)
- { gst_bin_add(bin(), element.element()); }
- void add(const QGstElement &e1, const QGstElement &e2)
- { gst_bin_add_many(bin(), e1.element(), e2.element(), nullptr); }
- void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3)
- { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), nullptr); }
- void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4)
- { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), nullptr); }
- void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4, const QGstElement &e5)
- { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), e5.element(), nullptr); }
- void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4, const QGstElement &e5, const QGstElement &e6)
- { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), e5.element(), e6.element(), nullptr); }
- void remove(const QGstElement &element)
- { gst_bin_remove(bin(), element.element()); }
-
- GstBin *bin() const { return GST_BIN_CAST(m_object); }
-
- void addGhostPad(const QGstElement &child, const char *name)
+
+ template <typename... Ts>
+ std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> remove(const Ts &...ts)
{
- addGhostPad(name, child.staticPad(name));
+ if constexpr (sizeof...(Ts) == 1)
+ gst_bin_remove(bin(), ts.element()...);
+ else
+ gst_bin_remove_many(bin(), ts.element()..., nullptr);
}
- void addGhostPad(const char *name, const QGstPad &pad)
+
+ template <typename... Ts>
+ std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void>
+ stopAndRemoveElements(Ts... ts)
{
- gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad()));
+ bool stateChangeSuccessful = (ts.setStateSync(GST_STATE_NULL) && ...);
+ Q_ASSERT(stateChangeSuccessful);
+ remove(ts...);
}
+
+ GstBin *bin() const;
+
+ void addGhostPad(const QGstElement &child, const char *name);
+ void addGhostPad(const char *name, const QGstPad &pad);
+
+ bool syncChildrenState();
+
+ void dumpGraph(const char *fileNamePrefix);
+
+ QGstElement findByName(const char *);
};
-inline QGstStructure QGValue::toStructure() const
+class QGstBaseSink : public QGstElement
{
- if (!value || !GST_VALUE_HOLDS_STRUCTURE(value))
- return QGstStructure();
- return QGstStructure(gst_value_get_structure(value));
-}
+public:
+ using QGstElement::QGstElement;
+
+ explicit QGstBaseSink(GstBaseSink *, RefMode);
+
+ QGstBaseSink(const QGstBaseSink &) = default;
+ QGstBaseSink(QGstBaseSink &&) noexcept = default;
+ QGstBaseSink &operator=(const QGstBaseSink &) = default;
+ QGstBaseSink &operator=(QGstBaseSink &&) noexcept = default;
+
+ GstBaseSink *baseSink() const;
+};
+
+class QGstBaseSrc : public QGstElement
+{
+public:
+ using QGstElement::QGstElement;
+
+ explicit QGstBaseSrc(GstBaseSrc *, RefMode);
+
+ QGstBaseSrc(const QGstBaseSrc &) = default;
+ QGstBaseSrc(QGstBaseSrc &&) noexcept = default;
+ QGstBaseSrc &operator=(const QGstBaseSrc &) = default;
+ QGstBaseSrc &operator=(QGstBaseSrc &&) noexcept = default;
+
+ GstBaseSrc *baseSrc() const;
+};
+
+#if QT_CONFIG(gstreamer_app)
+class QGstAppSink : public QGstBaseSink
+{
+public:
+ using QGstBaseSink::QGstBaseSink;
+
+ explicit QGstAppSink(GstAppSink *, RefMode);
+
+ QGstAppSink(const QGstAppSink &) = default;
+ QGstAppSink(QGstAppSink &&) noexcept = default;
+ QGstAppSink &operator=(const QGstAppSink &) = default;
+ QGstAppSink &operator=(QGstAppSink &&) noexcept = default;
+
+ static QGstAppSink create(const char *name);
+
+ GstAppSink *appSink() const;
+
+ void setCaps(const QGstCaps &caps);
+ void setCallbacks(GstAppSinkCallbacks &callbacks, gpointer user_data, GDestroyNotify notify);
+
+ QGstSampleHandle pullSample();
+};
+
+class QGstAppSrc : public QGstBaseSrc
+{
+public:
+ using QGstBaseSrc::QGstBaseSrc;
+
+ explicit QGstAppSrc(GstAppSrc *, RefMode);
+
+ QGstAppSrc(const QGstAppSrc &) = default;
+ QGstAppSrc(QGstAppSrc &&) noexcept = default;
+ QGstAppSrc &operator=(const QGstAppSrc &) = default;
+ QGstAppSrc &operator=(QGstAppSrc &&) noexcept = default;
+
+ static QGstAppSrc create(const char *name);
+
+ GstAppSrc *appSrc() const;
+
+ void setCallbacks(GstAppSrcCallbacks &callbacks, gpointer user_data, GDestroyNotify notify);
+
+ GstFlowReturn pushBuffer(GstBuffer *); // take ownership
+};
+
+#endif
-inline QGstCaps QGValue::toCaps() const
+inline QString errorMessageCannotFindElement(std::string_view element)
{
- if (!value || !GST_VALUE_HOLDS_CAPS(value))
- return QGstCaps();
- return QGstCaps(gst_value_get_caps(value));
+ return QStringLiteral("Could not find the %1 GStreamer element")
+ .arg(QLatin1StringView(element));
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstappsrc.cpp b/src/plugins/multimedia/gstreamer/common/qgstappsource.cpp
index 992e3d5a9..99af8443c 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstappsrc.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstappsource.cpp
@@ -1,82 +1,57 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QDebug>
-#include "qgstappsrc_p.h"
-#include "qgstutils_p.h"
+#include "qgstappsource_p.h"
+#include <common/qgstutils_p.h>
#include "qnetworkreply.h"
#include "qloggingcategory.h"
-Q_LOGGING_CATEGORY(qLcAppSrc, "qt.multimedia.appsrc")
+static Q_LOGGING_CATEGORY(qLcAppSrc, "qt.multimedia.appsrc")
-QGstAppSrc::QGstAppSrc(QObject *parent)
- : QObject(parent)
+QT_BEGIN_NAMESPACE
+
+QMaybe<QGstAppSource *> QGstAppSource::create(QObject *parent)
{
- m_appSrc = QGstElement("appsrc", "appsrc");
- if (m_appSrc.isNull())
- qWarning() << "Could not create GstAppSrc.";
+ QGstAppSrc appsrc = QGstAppSrc::create("appsrc");
+ if (!appsrc)
+ return errorMessageCannotFindElement("appsrc");
+
+ return new QGstAppSource(appsrc, parent);
+}
+
+QGstAppSource::QGstAppSource(QGstAppSrc appsrc, QObject *parent)
+ : QObject(parent), m_appSrc(std::move(appsrc))
+{
+ m_appSrc.set("emit-signals", false);
}
-QGstAppSrc::~QGstAppSrc()
+QGstAppSource::~QGstAppSource()
{
m_appSrc.setStateSync(GST_STATE_NULL);
streamDestroyed();
qCDebug(qLcAppSrc) << "~QGstAppSrc";
}
-bool QGstAppSrc::setup(QIODevice *stream, qint64 offset)
+bool QGstAppSource::setup(QIODevice *stream, qint64 offset)
{
+ QMutexLocker locker(&m_mutex);
+
if (m_appSrc.isNull())
return false;
if (!setStream(stream, offset))
return false;
- auto *appSrc = GST_APP_SRC(m_appSrc.element());
- GstAppSrcCallbacks m_callbacks;
- memset(&m_callbacks, 0, sizeof(GstAppSrcCallbacks));
- m_callbacks.need_data = &QGstAppSrc::on_need_data;
- m_callbacks.enough_data = &QGstAppSrc::on_enough_data;
- m_callbacks.seek_data = &QGstAppSrc::on_seek_data;
- gst_app_src_set_callbacks(appSrc, (GstAppSrcCallbacks*)&m_callbacks, this, nullptr);
+ GstAppSrcCallbacks callbacks{};
+ callbacks.need_data = QGstAppSource::on_need_data;
+ callbacks.enough_data = QGstAppSource::on_enough_data;
+ callbacks.seek_data = QGstAppSource::on_seek_data;
+
+ m_appSrc.setCallbacks(callbacks, this, nullptr);
+ GstAppSrc *appSrc = m_appSrc.appSrc();
m_maxBytes = gst_app_src_get_max_bytes(appSrc);
m_suspended = false;
@@ -87,14 +62,15 @@ bool QGstAppSrc::setup(QIODevice *stream, qint64 offset)
gst_app_src_set_stream_type(appSrc, m_streamType);
gst_app_src_set_size(appSrc, m_sequential ? -1 : m_stream->size() - m_offset);
- m_networkReply = qobject_cast<QNetworkReply *>(m_stream);
m_noMoreData = true;
return true;
}
-void QGstAppSrc::setAudioFormat(const QAudioFormat &f)
+void QGstAppSource::setAudioFormat(const QAudioFormat &f)
{
+ QMutexLocker locker(&m_mutex);
+
m_format = f;
if (!m_format.isValid())
return;
@@ -105,16 +81,17 @@ void QGstAppSrc::setAudioFormat(const QAudioFormat &f)
m_appSrc.set("format", GST_FORMAT_TIME);
}
-void QGstAppSrc::setExternalAppSrc(const QGstElement &appsrc)
+void QGstAppSource::setExternalAppSrc(QGstAppSrc appsrc)
{
- m_appSrc = appsrc;
+ QMutexLocker locker(&m_mutex);
+ m_appSrc = std::move(appsrc);
}
-bool QGstAppSrc::setStream(QIODevice *stream, qint64 offset)
+bool QGstAppSource::setStream(QIODevice *stream, qint64 offset)
{
if (m_stream) {
- disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady()));
- disconnect(m_stream, SIGNAL(destroyed()), this, SLOT(streamDestroyed()));
+ disconnect(m_stream, &QIODevice::readyRead, this, &QGstAppSource::onDataReady);
+ disconnect(m_stream, &QIODevice::destroyed, this, &QGstAppSource::streamDestroyed);
m_stream = nullptr;
}
@@ -127,21 +104,28 @@ bool QGstAppSrc::setStream(QIODevice *stream, qint64 offset)
if (!stream->isOpen() && !stream->open(QIODevice::ReadOnly))
return false;
m_stream = stream;
- connect(m_stream, SIGNAL(destroyed()), SLOT(streamDestroyed()));
- connect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady()));
+ connect(m_stream, &QIODevice::destroyed, this, &QGstAppSource::streamDestroyed);
+ connect(m_stream, &QIODevice::readyRead, this, &QGstAppSource::onDataReady);
m_sequential = m_stream->isSequential();
m_offset = offset;
}
return true;
}
-QGstElement QGstAppSrc::element()
+bool QGstAppSource::isStreamValid() const
+{
+ return m_stream != nullptr && m_stream->isOpen();
+}
+
+QGstElement QGstAppSource::element() const
{
return m_appSrc;
}
-void QGstAppSrc::write(const char *data, qsizetype size)
+void QGstAppSource::write(const char *data, qsizetype size)
{
+ QMutexLocker locker(&m_mutex);
+
qCDebug(qLcAppSrc) << "write" << size << m_noMoreData << m_dataRequestSize;
if (!size)
return;
@@ -151,13 +135,32 @@ void QGstAppSrc::write(const char *data, qsizetype size)
pushData();
}
-void QGstAppSrc::onDataReady()
+bool QGstAppSource::canAcceptMoreData() const
+{
+ QMutexLocker locker(&m_mutex);
+ return m_noMoreData || m_dataRequestSize != 0;
+}
+
+void QGstAppSource::suspend()
+{
+ QMutexLocker locker(&m_mutex);
+ m_suspended = true;
+}
+
+void QGstAppSource::resume()
+{
+ QMutexLocker locker(&m_mutex);
+ m_suspended = false;
+ m_noMoreData = true;
+}
+
+void QGstAppSource::onDataReady()
{
qCDebug(qLcAppSrc) << "onDataReady" << m_stream->bytesAvailable() << m_stream->size();
pushData();
}
-void QGstAppSrc::streamDestroyed()
+void QGstAppSource::streamDestroyed()
{
qCDebug(qLcAppSrc) << "stream destroyed";
m_stream = nullptr;
@@ -166,7 +169,7 @@ void QGstAppSrc::streamDestroyed()
sendEOS();
}
-void QGstAppSrc::pushData()
+void QGstAppSource::pushData()
{
if (m_appSrc.isNull() || !m_dataRequestSize || m_suspended) {
qCDebug(qLcAppSrc) << "push data: return immediately" << m_appSrc.isNull() << m_dataRequestSize << m_suspended;
@@ -230,7 +233,7 @@ void QGstAppSrc::pushData()
m_noMoreData = false;
emit bytesProcessed(bytesRead);
- GstFlowReturn ret = gst_app_src_push_buffer(GST_APP_SRC(m_appSrc.element()), buffer);
+ GstFlowReturn ret = m_appSrc.pushBuffer(buffer);
if (ret == GST_FLOW_ERROR) {
qWarning() << "QGstAppSrc: push buffer error";
} else if (ret == GST_FLOW_FLUSHING) {
@@ -240,48 +243,52 @@ void QGstAppSrc::pushData()
}
-bool QGstAppSrc::doSeek(qint64 value)
+bool QGstAppSource::doSeek(qint64 value)
{
if (isStreamValid())
return m_stream->seek(value + m_offset);
return false;
}
-
-gboolean QGstAppSrc::on_seek_data(GstAppSrc *, guint64 arg0, gpointer userdata)
+gboolean QGstAppSource::on_seek_data(GstAppSrc *, guint64 arg0, gpointer userdata)
{
// we do get some spurious seeks to INT_MAX, ignore those
if (arg0 == std::numeric_limits<quint64>::max())
return true;
- QGstAppSrc *self = reinterpret_cast<QGstAppSrc*>(userdata);
+ QGstAppSource *self = reinterpret_cast<QGstAppSource *>(userdata);
Q_ASSERT(self);
+
+ QMutexLocker locker(&self->m_mutex);
+
if (self->m_sequential)
return false;
- QMetaObject::invokeMethod(self, "doSeek", Qt::AutoConnection, Q_ARG(qint64, arg0));
+ self->doSeek(arg0);
return true;
}
-void QGstAppSrc::on_enough_data(GstAppSrc *, gpointer userdata)
+void QGstAppSource::on_enough_data(GstAppSrc *, gpointer userdata)
{
qCDebug(qLcAppSrc) << "on_enough_data";
- QGstAppSrc *self = static_cast<QGstAppSrc*>(userdata);
+ QGstAppSource *self = static_cast<QGstAppSource *>(userdata);
Q_ASSERT(self);
+ QMutexLocker locker(&self->m_mutex);
self->m_dataRequestSize = 0;
}
-void QGstAppSrc::on_need_data(GstAppSrc *, guint arg0, gpointer userdata)
+void QGstAppSource::on_need_data(GstAppSrc *, guint arg0, gpointer userdata)
{
qCDebug(qLcAppSrc) << "on_need_data requesting bytes" << arg0;
- QGstAppSrc *self = static_cast<QGstAppSrc*>(userdata);
+ QGstAppSource *self = static_cast<QGstAppSource *>(userdata);
Q_ASSERT(self);
+ QMutexLocker locker(&self->m_mutex);
self->m_dataRequestSize = arg0;
- QMetaObject::invokeMethod(self, "pushData", Qt::AutoConnection);
+ self->pushData();
qCDebug(qLcAppSrc) << "done on_need_data";
}
-void QGstAppSrc::sendEOS()
+void QGstAppSource::sendEOS()
{
qCDebug(qLcAppSrc) << "sending EOS";
if (m_appSrc.isNull())
@@ -290,7 +297,7 @@ void QGstAppSrc::sendEOS()
gst_app_src_end_of_stream(GST_APP_SRC(m_appSrc.element()));
}
-void QGstAppSrc::eosOrIdle()
+void QGstAppSource::eosOrIdle()
{
qCDebug(qLcAppSrc) << "eosOrIdle";
if (m_appSrc.isNull())
@@ -306,3 +313,7 @@ void QGstAppSrc::eosOrIdle()
m_noMoreData = true;
emit noMoreData();
}
+
+QT_END_NAMESPACE
+
+#include "moc_qgstappsource_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/common/qgstappsource_p.h b/src/plugins/multimedia/gstreamer/common/qgstappsource_p.h
new file mode 100644
index 000000000..59ced00dc
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/common/qgstappsource_p.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGSTAPPSRC_H
+#define QGSTAPPSRC_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtmultimediaglobal_p.h>
+#include <private/qmultimediautils_p.h>
+#include <qaudioformat.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/private/qringbuffer_p.h>
+#include <QtCore/qatomic.h>
+#include <QtCore/qmutex.h>
+
+#include <common/qgst_p.h>
+#include <gst/app/gstappsrc.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstAppSource : public QObject
+{
+ Q_OBJECT
+public:
+ static QMaybe<QGstAppSource *> create(QObject *parent = nullptr);
+ ~QGstAppSource();
+
+ bool setup(QIODevice *stream = nullptr, qint64 offset = 0);
+ void setAudioFormat(const QAudioFormat &f);
+
+ void setExternalAppSrc(QGstAppSrc);
+ QGstElement element() const;
+
+ void write(const char *data, qsizetype size);
+
+ bool canAcceptMoreData() const;
+
+ void suspend();
+ void resume();
+
+Q_SIGNALS:
+ void bytesProcessed(int bytes);
+ void noMoreData();
+
+private Q_SLOTS:
+ void pushData();
+ bool doSeek(qint64);
+ void onDataReady();
+
+ void streamDestroyed();
+private:
+ QGstAppSource(QGstAppSrc appsrc, QObject *parent);
+
+ bool setStream(QIODevice *, qint64 offset);
+ bool isStreamValid() const;
+
+ static gboolean on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata);
+ static void on_enough_data(GstAppSrc *element, gpointer userdata);
+ static void on_need_data(GstAppSrc *element, uint arg0, gpointer userdata);
+
+ void sendEOS();
+ void eosOrIdle();
+
+ mutable QMutex m_mutex;
+
+ QIODevice *m_stream = nullptr;
+ QRingBuffer m_buffer;
+ QAudioFormat m_format;
+
+ QGstAppSrc m_appSrc;
+ bool m_sequential = true;
+ bool m_suspended = false;
+ bool m_noMoreData = false;
+ GstAppStreamType m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS;
+ qint64 m_offset = 0;
+ qint64 m_maxBytes = 0;
+ qint64 bytesReadSoFar = 0;
+ QAtomicInteger<unsigned int> m_dataRequestSize = 0;
+ int streamedSamples = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstappsrc_p.h b/src/plugins/multimedia/gstreamer/common/qgstappsrc_p.h
deleted file mode 100644
index 373dbea65..000000000
--- a/src/plugins/multimedia/gstreamer/common/qgstappsrc_p.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QGSTAPPSRC_H
-#define QGSTAPPSRC_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <private/qtmultimediaglobal_p.h>
-#include <qaudioformat.h>
-
-#include <QtCore/qobject.h>
-#include <QtCore/qiodevice.h>
-#include <QtCore/private/qringbuffer_p.h>
-#include <QtCore/qatomic.h>
-
-#include <qgst_p.h>
-#include <gst/app/gstappsrc.h>
-
-QT_BEGIN_NAMESPACE
-
-class QNetworkReply;
-
-class Q_MULTIMEDIA_EXPORT QGstAppSrc : public QObject
-{
- Q_OBJECT
-public:
- QGstAppSrc(QObject *parent = 0);
- ~QGstAppSrc();
-
- bool setup(QIODevice *stream = nullptr, qint64 offset = 0);
- void setAudioFormat(const QAudioFormat &f);
-
- void setExternalAppSrc(const QGstElement &appsrc);
- QGstElement element();
-
- void write(const char *data, qsizetype size);
-
- bool canAcceptMoreData() { return m_noMoreData || m_dataRequestSize != 0; }
-
- void suspend() { m_suspended = true; }
- void resume() { m_suspended = false; m_noMoreData = true; }
-
-Q_SIGNALS:
- void bytesProcessed(int bytes);
- void noMoreData();
-
-private Q_SLOTS:
- void pushData();
- bool doSeek(qint64);
- void onDataReady();
-
- void streamDestroyed();
-private:
- bool setStream(QIODevice *, qint64 offset);
- bool isStreamValid() const
- {
- return m_stream != nullptr && m_stream->isOpen();
- }
-
- static gboolean on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata);
- static void on_enough_data(GstAppSrc *element, gpointer userdata);
- static void on_need_data(GstAppSrc *element, uint arg0, gpointer userdata);
-
- void sendEOS();
- void eosOrIdle();
-
- QIODevice *m_stream = nullptr;
- QNetworkReply *m_networkReply = nullptr;
- QRingBuffer m_buffer;
- QAudioFormat m_format;
-
- QGstElement m_appSrc;
- bool m_sequential = true;
- bool m_suspended = false;
- bool m_noMoreData = false;
- GstAppStreamType m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS;
- qint64 m_offset = 0;
- qint64 m_maxBytes = 0;
- qint64 bytesReadSoFar = 0;
- QAtomicInteger<unsigned int> m_dataRequestSize = 0;
- int streamedSamples = 0;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp b/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp
index 1f20fe4ec..1a7ba6508 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qmap.h>
#include <QtCore/qtimer.h>
@@ -52,9 +16,7 @@ QT_BEGIN_NAMESPACE
class QGstPipelinePrivate : public QObject
{
- Q_OBJECT
public:
-
int m_ref = 0;
guint m_tag = 0;
GstBus *m_bus = nullptr;
@@ -71,7 +33,7 @@ public:
int m_configCounter = 0;
GstState m_savedState = GST_STATE_NULL;
- QGstPipelinePrivate(GstBus* bus, QObject* parent = 0);
+ explicit QGstPipelinePrivate(GstBus *bus, QObject *parent = nullptr);
~QGstPipelinePrivate();
void ref() { ++ m_ref; }
@@ -82,13 +44,27 @@ public:
void installMessageFilter(QGstreamerBusMessageFilter *filter);
void removeMessageFilter(QGstreamerBusMessageFilter *filter);
- static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstPipelinePrivate *d)
+ void processMessage(const QGstreamerMessage &msg)
+ {
+ for (QGstreamerBusMessageFilter *filter : std::as_const(busFilters)) {
+ if (filter->processBusMessage(msg))
+ break;
+ }
+ }
+
+private:
+ static GstBusSyncReply syncGstBusFilter(GstBus *bus, GstMessage *message,
+ QGstPipelinePrivate *d)
{
+ if (!message)
+ return GST_BUS_PASS;
+
Q_UNUSED(bus);
QMutexLocker lock(&d->filterMutex);
- for (QGstreamerSyncMessageFilter *filter : qAsConst(d->syncFilters)) {
- if (filter->processSyncMessage(QGstreamerMessage(message))) {
+ for (QGstreamerSyncMessageFilter *filter : std::as_const(d->syncFilters)) {
+ if (filter->processSyncMessage(
+ QGstreamerMessage{ message, QGstreamerMessage::NeedsRef })) {
gst_message_unref(message);
return GST_BUS_DROP;
}
@@ -97,41 +73,22 @@ public:
return GST_BUS_PASS;
}
-private Q_SLOTS:
- void interval()
- {
- GstMessage* message;
- while ((message = gst_bus_poll(m_bus, GST_MESSAGE_ANY, 0)) != nullptr) {
- processMessage(message);
- gst_message_unref(message);
- }
- }
- void doProcessMessage(const QGstreamerMessage& msg)
+ void processMessage(GstMessage *message)
{
- for (QGstreamerBusMessageFilter *filter : qAsConst(busFilters)) {
- if (filter->processBusMessage(msg))
- break;
- }
- }
+ if (!message)
+ return;
-private:
- void processMessage(GstMessage* message)
- {
- QGstreamerMessage msg(message);
- doProcessMessage(msg);
- }
+ QGstreamerMessage msg{
+ message,
+ QGstreamerMessage::NeedsRef,
+ };
- void queueMessage(GstMessage* message)
- {
- QGstreamerMessage msg(message);
- QMetaObject::invokeMethod(this, "doProcessMessage", Qt::QueuedConnection,
- Q_ARG(QGstreamerMessage, msg));
+ processMessage(msg);
}
- static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data)
+ static gboolean busCallback(GstBus *, GstMessage *message, gpointer data)
{
- Q_UNUSED(bus);
- static_cast<QGstPipelinePrivate *>(data)->queueMessage(message);
+ static_cast<QGstPipelinePrivate *>(data)->processMessage(message);
return TRUE;
}
};
@@ -146,7 +103,13 @@ QGstPipelinePrivate::QGstPipelinePrivate(GstBus* bus, QObject* parent)
if (!hasGlib) {
m_intervalTimer = new QTimer(this);
m_intervalTimer->setInterval(250);
- connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval()));
+ QObject::connect(m_intervalTimer, &QTimer::timeout, this, [this] {
+ GstMessage *message;
+ while ((message = gst_bus_poll(m_bus, GST_MESSAGE_ANY, 0)) != nullptr) {
+ processMessage(message);
+ gst_message_unref(message);
+ }
+ });
m_intervalTimer->start();
} else {
m_tag = gst_bus_add_watch_full(bus, G_PRIORITY_DEFAULT, busCallback, this, nullptr);
@@ -195,90 +158,78 @@ void QGstPipelinePrivate::removeMessageFilter(QGstreamerBusMessageFilter *filter
busFilters.removeAll(filter);
}
-QGstPipeline::QGstPipeline(const QGstPipeline &o)
- : QGstBin(o.bin(), NeedsRef),
- d(o.d)
-{
- if (d)
- d->ref();
-}
-
-QGstPipeline &QGstPipeline::operator=(const QGstPipeline &o)
+QGstPipeline QGstPipeline::create(const char *name)
{
- if (this == &o)
- return *this;
- if (o.d)
- o.d->ref();
- if (d)
- d->deref();
- QGstBin::operator=(o);
- d = o.d;
- return *this;
+ GstPipeline *pipeline = qGstCheckedCast<GstPipeline>(gst_pipeline_new(name));
+ return adopt(pipeline);
}
-QGstPipeline::QGstPipeline(const char *name)
- : QGstBin(GST_BIN(gst_pipeline_new(name)), NeedsRef)
+QGstPipeline QGstPipeline::adopt(GstPipeline *pipeline)
{
- d = new QGstPipelinePrivate(gst_pipeline_get_bus(pipeline()));
- d->ref();
+ QGstPipelinePrivate *d = new QGstPipelinePrivate(gst_pipeline_get_bus(pipeline));
+ g_object_set_data_full(qGstCheckedCast<GObject>(pipeline), "pipeline-private", d,
+ [](gpointer ptr) {
+ delete reinterpret_cast<QGstPipelinePrivate *>(ptr);
+ return;
+ });
+
+ return QGstPipeline{
+ pipeline,
+ QGstPipeline::NeedsRef,
+ };
}
-QGstPipeline::QGstPipeline(GstPipeline *p)
- : QGstBin(&p->bin, NeedsRef)
+QGstPipeline::QGstPipeline(GstPipeline *p, RefMode mode) : QGstBin(qGstCheckedCast<GstBin>(p), mode)
{
- d = new QGstPipelinePrivate(gst_pipeline_get_bus(pipeline()));
- d->ref();
}
-QGstPipeline::~QGstPipeline()
-{
- if (d)
- d->deref();
-}
+QGstPipeline::~QGstPipeline() = default;
bool QGstPipeline::inStoppedState() const
{
- Q_ASSERT(d);
+ QGstPipelinePrivate *d = getPrivate();
return d->inStoppedState;
}
void QGstPipeline::setInStoppedState(bool stopped)
{
- Q_ASSERT(d);
+ QGstPipelinePrivate *d = getPrivate();
d->inStoppedState = stopped;
}
void QGstPipeline::setFlushOnConfigChanges(bool flush)
{
+ QGstPipelinePrivate *d = getPrivate();
d->m_flushOnConfigChanges = flush;
}
void QGstPipeline::installMessageFilter(QGstreamerSyncMessageFilter *filter)
{
- Q_ASSERT(d);
+ QGstPipelinePrivate *d = getPrivate();
d->installMessageFilter(filter);
}
void QGstPipeline::removeMessageFilter(QGstreamerSyncMessageFilter *filter)
{
- Q_ASSERT(d);
+ QGstPipelinePrivate *d = getPrivate();
d->removeMessageFilter(filter);
}
void QGstPipeline::installMessageFilter(QGstreamerBusMessageFilter *filter)
{
- Q_ASSERT(d);
+ QGstPipelinePrivate *d = getPrivate();
d->installMessageFilter(filter);
}
void QGstPipeline::removeMessageFilter(QGstreamerBusMessageFilter *filter)
{
- Q_ASSERT(d);
+ QGstPipelinePrivate *d = getPrivate();
d->removeMessageFilter(filter);
}
GstStateChangeReturn QGstPipeline::setState(GstState state)
{
+ QGstPipelinePrivate *d = getPrivate();
auto retval = gst_element_set_state(element(), state);
if (d->m_pendingFlush) {
d->m_pendingFlush = false;
@@ -287,25 +238,67 @@ GstStateChangeReturn QGstPipeline::setState(GstState state)
return retval;
}
-void QGstPipeline::beginConfig()
+void QGstPipeline::processMessages(GstMessageType types)
+{
+ QGstPipelinePrivate *d = getPrivate();
+ QGstreamerMessage message{
+ gst_bus_pop_filtered(d->m_bus, types),
+ QGstreamerMessage::HasRef,
+ };
+ d->processMessage(message);
+}
+
+void QGstPipeline::dumpGraph(const char *fileName)
{
- if (!d)
+ if (isNull())
return;
+
+ QGstBin{ bin(), QGstBin::NeedsRef }.dumpGraph(fileName);
+}
+
+void QGstPipeline::beginConfig()
+{
+ QGstPipelinePrivate *d = getPrivate();
Q_ASSERT(!isNull());
++d->m_configCounter;
if (d->m_configCounter > 1)
return;
- d->m_savedState = state();
+ GstState state;
+ GstState pending;
+ GstStateChangeReturn stateChangeReturn = gst_element_get_state(element(), &state, &pending, 0);
+ switch (stateChangeReturn) {
+ case GST_STATE_CHANGE_ASYNC: {
+ if (state == GST_STATE_PLAYING) {
+ // playing->paused transition in progress. wait for it to finish
+ bool stateChangeSuccessful = this->finishStateChange();
+ if (!stateChangeSuccessful)
+ qWarning() << "QGstPipeline::beginConfig: timeout when waiting for state change";
+ }
+
+ state = pending;
+ break;
+ }
+ case GST_STATE_CHANGE_FAILURE: {
+ qDebug() << "QGstPipeline::beginConfig: state change failure";
+ dumpGraph("beginConfigFailure");
+ break;
+ }
+
+ case GST_STATE_CHANGE_NO_PREROLL:
+ case GST_STATE_CHANGE_SUCCESS:
+ break;
+ }
+
+ d->m_savedState = state;
if (d->m_savedState == GST_STATE_PLAYING)
setStateSync(GST_STATE_PAUSED);
}
void QGstPipeline::endConfig()
{
- if (!d)
- return;
+ QGstPipelinePrivate *d = getPrivate();
Q_ASSERT(!isNull());
--d->m_configCounter;
@@ -321,18 +314,22 @@ void QGstPipeline::endConfig()
void QGstPipeline::flush()
{
+ QGstPipelinePrivate *d = getPrivate();
seek(position(), d->m_rate);
}
bool QGstPipeline::seek(qint64 pos, double rate)
{
+ QGstPipelinePrivate *d = getPrivate();
// always adjust the rate, so it can be set before playback starts
// setting position needs a loaded media file that's seekable
d->m_rate = rate;
+ qint64 from = rate > 0 ? pos : 0;
+ qint64 to = rate > 0 ? duration() : pos;
bool success = gst_element_seek(element(), rate, GST_FORMAT_TIME,
GstSeekFlags(GST_SEEK_FLAG_FLUSH),
- GST_SEEK_TYPE_SET, pos,
- GST_SEEK_TYPE_SET, -1);
+ GST_SEEK_TYPE_SET, from,
+ GST_SEEK_TYPE_SET, to);
if (!success)
return false;
@@ -340,27 +337,48 @@ bool QGstPipeline::seek(qint64 pos, double rate)
return true;
}
-bool QGstPipeline::setPlaybackRate(double rate)
+bool QGstPipeline::setPlaybackRate(double rate, bool applyToPipeline)
{
+ QGstPipelinePrivate *d = getPrivate();
if (rate == d->m_rate)
return false;
- seek(position(), rate);
- return true;
+
+ if (!applyToPipeline) {
+ d->m_rate = rate;
+ return true;
+ }
+
+ constexpr GstSeekFlags seekFlags =
+#if GST_CHECK_VERSION(1, 18, 0)
+ GST_SEEK_FLAG_INSTANT_RATE_CHANGE;
+#else
+ GST_SEEK_FLAG_FLUSH;
+#endif
+
+ bool success = gst_element_seek(element(), rate, GST_FORMAT_TIME, seekFlags, GST_SEEK_TYPE_NONE,
+ GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ if (success)
+ d->m_rate = rate;
+
+ return success;
}
double QGstPipeline::playbackRate() const
{
+ QGstPipelinePrivate *d = getPrivate();
return d->m_rate;
}
bool QGstPipeline::setPosition(qint64 pos)
{
+ QGstPipelinePrivate *d = getPrivate();
return seek(pos, d->m_rate);
}
qint64 QGstPipeline::position() const
{
gint64 pos;
+ QGstPipelinePrivate *d = getPrivate();
if (gst_element_query_position(element(), GST_FORMAT_TIME, &pos))
d->m_position = pos;
return d->m_position;
@@ -374,7 +392,12 @@ qint64 QGstPipeline::duration() const
return d;
}
-QT_END_NAMESPACE
-
-#include "qgstpipeline.moc"
+QGstPipelinePrivate *QGstPipeline::getPrivate() const
+{
+ gpointer p = g_object_get_data(qGstCheckedCast<GObject>(object()), "pipeline-private");
+ auto *d = reinterpret_cast<QGstPipelinePrivate *>(p);
+ Q_ASSERT(d);
+ return d;
+}
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstpipeline_p.h b/src/plugins/multimedia/gstreamer/common/qgstpipeline_p.h
index d1f585cc2..ca4633edd 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstpipeline_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstpipeline_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef qgstpipeline_p_H
#define qgstpipeline_p_H
@@ -51,10 +15,10 @@
// We mean it.
//
-#include <private/qtmultimediaglobal_p.h>
-#include <QObject>
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <QtCore/qobject.h>
-#include <qgst_p.h>
+#include "qgst_p.h"
QT_BEGIN_NAMESPACE
@@ -77,14 +41,18 @@ class QGstPipelinePrivate;
class QGstPipeline : public QGstBin
{
- QGstPipelinePrivate *d = nullptr;
public:
constexpr QGstPipeline() = default;
- QGstPipeline(const QGstPipeline &o);
- QGstPipeline &operator=(const QGstPipeline &o);
- QGstPipeline(const char *name);
- QGstPipeline(GstPipeline *p);
- ~QGstPipeline() override;
+ QGstPipeline(const QGstPipeline &) = default;
+ QGstPipeline(QGstPipeline &&) = default;
+ QGstPipeline &operator=(const QGstPipeline &) = default;
+ QGstPipeline &operator=(QGstPipeline &&) noexcept = default;
+ QGstPipeline(GstPipeline *, RefMode mode);
+ ~QGstPipeline();
+
+ // installs QGstPipelinePrivate as "pipeline-private" gobject property
+ static QGstPipeline create(const char *name);
+ static QGstPipeline adopt(GstPipeline *);
// This is needed to help us avoid sending QVideoFrames or audio buffers to the
// application while we're prerolling the pipeline.
@@ -103,36 +71,45 @@ public:
GstStateChangeReturn setState(GstState state);
- GstPipeline *pipeline() const { return GST_PIPELINE_CAST(m_object); }
+ GstPipeline *pipeline() const { return GST_PIPELINE_CAST(get()); }
+
+ void processMessages(GstMessageType = GST_MESSAGE_ANY);
+
+ void dumpGraph(const char *fileName);
- void dumpGraph(const char *fileName)
+ template <typename Functor>
+ void modifyPipelineWhileNotRunning(Functor &&fn)
{
- if (isNull())
- return;
-
-#if 1 //def QT_GST_CAPTURE_DEBUG
- GST_DEBUG_BIN_TO_DOT_FILE(bin(),
- GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL |
- GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES),
- fileName);
-#else
- Q_UNUSED(fileName);
-#endif
+ beginConfig();
+ fn();
+ endConfig();
}
- void beginConfig();
- void endConfig();
+ template <typename Functor>
+ static void modifyPipelineWhileNotRunning(QGstPipeline &&pipeline, Functor &&fn)
+ {
+ if (pipeline)
+ pipeline.modifyPipelineWhileNotRunning(fn);
+ else
+ fn();
+ }
void flush();
bool seek(qint64 pos, double rate);
- bool setPlaybackRate(double rate);
+ bool setPlaybackRate(double rate, bool applyToPipeline = true);
double playbackRate() const;
bool setPosition(qint64 pos);
qint64 position() const;
qint64 duration() const;
+
+private:
+ QGstPipelinePrivate *getPrivate() const;
+
+ void beginConfig();
+ void endConfig();
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp
index 402b3c825..ca56c4572 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp
@@ -1,103 +1,64 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qgstreameraudioinput_p.h>
-#include <qgstreameraudiodevice_p.h>
-#include <qaudiodevice.h>
-#include <qaudioinput.h>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtMultimedia/qaudiodevice.h>
+#include <QtMultimedia/qaudioinput.h>
#include <QtCore/qloggingcategory.h>
-#include <QtNetwork/qnetworkaccessmanager.h>
-#include <QtNetwork/qnetworkreply.h>
+
+#include <audio/qgstreameraudiodevice_p.h>
+#include <common/qgstreameraudioinput_p.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-Q_LOGGING_CATEGORY(qLcMediaAudioInput, "qt.multimedia.audioInput")
+#include <utility>
+
+static Q_LOGGING_CATEGORY(qLcMediaAudioInput, "qt.multimedia.audioInput")
QT_BEGIN_NAMESPACE
-QGstreamerAudioInput::QGstreamerAudioInput(QAudioInput *parent)
- : QObject(parent),
- QPlatformAudioInput(parent),
- gstAudioInput("audioInput")
+QMaybe<QPlatformAudioInput *> QGstreamerAudioInput::create(QAudioInput *parent)
{
- audioSrc = QGstElement("autoaudiosrc", "autoaudiosrc");
- audioVolume = QGstElement("volume", "volume");
- gstAudioInput.add(audioSrc, audioVolume);
- audioSrc.link(audioVolume);
+ QGstElement autoaudiosrc = QGstElement::createFromFactory("autoaudiosrc", "autoaudiosrc");
+ if (!autoaudiosrc)
+ return errorMessageCannotFindElement("autoaudiosrc");
- gstAudioInput.addGhostPad(audioVolume, "src");
-}
+ QGstElement volume = QGstElement::createFromFactory("volume", "volume");
+ if (!volume)
+ return errorMessageCannotFindElement("volume");
-QGstreamerAudioInput::~QGstreamerAudioInput()
-{
- gstAudioInput.setStateSync(GST_STATE_NULL);
+ return new QGstreamerAudioInput(autoaudiosrc, volume, parent);
}
-int QGstreamerAudioInput::volume() const
+QGstreamerAudioInput::QGstreamerAudioInput(QGstElement autoaudiosrc, QGstElement volume,
+ QAudioInput *parent)
+ : QObject(parent),
+ QPlatformAudioInput(parent),
+ gstAudioInput(QGstBin::create("audioInput")),
+ audioSrc(std::move(autoaudiosrc)),
+ audioVolume(std::move(volume))
{
- return m_volume;
+ gstAudioInput.add(audioSrc, audioVolume);
+ qLinkGstElements(audioSrc, audioVolume);
+
+ gstAudioInput.addGhostPad(audioVolume, "src");
}
-bool QGstreamerAudioInput::isMuted() const
+QGstreamerAudioInput::~QGstreamerAudioInput()
{
- return m_muted;
+ gstAudioInput.setStateSync(GST_STATE_NULL);
}
-void QGstreamerAudioInput::setVolume(float vol)
+void QGstreamerAudioInput::setVolume(float volume)
{
- if (vol == m_volume)
- return;
- m_volume = vol;
- audioVolume.set("volume", vol);
- emit volumeChanged(m_volume);
+ audioVolume.set("volume", volume);
}
void QGstreamerAudioInput::setMuted(bool muted)
{
- if (muted == m_muted)
- return;
- m_muted = muted;
audioVolume.set("mute", muted);
- emit mutedChanged(muted);
}
void QGstreamerAudioInput::setAudioDevice(const QAudioDevice &device)
@@ -106,38 +67,45 @@ void QGstreamerAudioInput::setAudioDevice(const QAudioDevice &device)
return;
qCDebug(qLcMediaAudioInput) << "setAudioInput" << device.description() << device.isNull();
m_audioDevice = device;
+ const QByteArray &id = m_audioDevice.id();
QGstElement newSrc;
-#if QT_CONFIG(pulseaudio)
- auto id = m_audioDevice.id();
- newSrc = QGstElement("pulsesrc", "audiosrc");
- if (!newSrc.isNull())
- newSrc.set("device", id.constData());
- else
- qCWarning(qLcMediaAudioInput) << "Invalid audio device";
-#else
- auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_audioDevice.handle());
- if (deviceInfo && deviceInfo->gstDevice)
- newSrc = gst_device_create_element(deviceInfo->gstDevice, "audiosrc");
- else
- qCWarning(qLcMediaAudioInput) << "Invalid audio device";
-#endif
+ if constexpr (QT_CONFIG(pulseaudio)) {
+ newSrc = QGstElement::createFromFactory("pulsesrc", "audiosrc");
+ if (newSrc)
+ newSrc.set("device", id.constData());
+ else
+ qWarning() << "Cannot create pulsesrc";
+ } else if constexpr (QT_CONFIG(alsa)) {
+ newSrc = QGstElement::createFromFactory("alsasrc", "audiosrc");
+ if (newSrc)
+ newSrc.set("device", id.constData());
+ else
+ qWarning() << "Cannot create alsasrc";
+ } else {
+ auto *gstDeviceInfo =
+ dynamic_cast<const QGStreamerAudioDeviceInfo *>(m_audioDevice.handle());
+ if (gstDeviceInfo && gstDeviceInfo->gstDevice) {
+ newSrc = QGstElement::createFromDevice(gstDeviceInfo->gstDevice, "audiosrc");
+ } else {
+ qWarning() << "Invalid audio device";
+ }
+ }
if (newSrc.isNull()) {
- qCWarning(qLcMediaAudioInput) << "Failed to create a gst element for the audio device, using a default audio source";
- newSrc = QGstElement("autoaudiosrc", "audiosrc");
+ qWarning() << "Failed to create a gst element for the audio device, using a default audio "
+ "source";
+ newSrc = QGstElement::createFromFactory("autoaudiosrc", "audiosrc");
}
- // FIXME: most probably source can be disconnected outside of idle probe
- audioSrc.staticPad("src").doInIdleProbe([&](){
- audioSrc.unlink(audioVolume);
+ QGstPipeline::modifyPipelineWhileNotRunning(gstAudioInput.getPipeline(), [&] {
+ qUnlinkGstElements(audioSrc, audioVolume);
+ gstAudioInput.stopAndRemoveElements(audioSrc);
+ audioSrc = std::move(newSrc);
+ gstAudioInput.add(audioSrc);
+ qLinkGstElements(audioSrc, audioVolume);
+ audioSrc.syncStateWithParent();
});
- audioSrc.setStateSync(GST_STATE_NULL);
- gstAudioInput.remove(audioSrc);
- audioSrc = newSrc;
- gstAudioInput.add(audioSrc);
- audioSrc.link(audioVolume);
- audioSrc.syncStateWithParent();
}
QAudioDevice QGstreamerAudioInput::audioInput() const
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h
index 3b4ad3475..69500ecab 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERAUDIOINPUT_P_H
#define QGSTREAMERAUDIOINPUT_P_H
@@ -51,47 +15,36 @@
// We mean it.
//
-#include <private/qtmultimediaglobal_p.h>
-#include <qaudiodevice.h>
-
#include <QtCore/qobject.h>
+#include <QtMultimedia/private/qmultimediautils_p.h>
+#include <QtMultimedia/private/qplatformaudioinput_p.h>
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <QtMultimedia/qaudiodevice.h>
-#include <qgst_p.h>
-#include <qgstpipeline_p.h>
-#include <private/qplatformaudioinput_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
QT_BEGIN_NAMESPACE
-class QGstreamerMessage;
class QAudioDevice;
-class Q_MULTIMEDIA_EXPORT QGstreamerAudioInput : public QObject, public QPlatformAudioInput
+class QGstreamerAudioInput : public QObject, public QPlatformAudioInput
{
- Q_OBJECT
-
public:
- QGstreamerAudioInput(QAudioInput *parent);
+ static QMaybe<QPlatformAudioInput *> create(QAudioInput *parent);
~QGstreamerAudioInput();
- int volume() const;
- bool isMuted() const;
-
bool setAudioInput(const QAudioDevice &);
QAudioDevice audioInput() const;
void setAudioDevice(const QAudioDevice &) override;
- void setVolume(float volume) override;
- void setMuted(bool muted) override;
+ void setVolume(float) override;
+ void setMuted(bool) override;
QGstElement gstElement() const { return gstAudioInput; }
-Q_SIGNALS:
- void mutedChanged(bool);
- void volumeChanged(int);
-
private:
- float m_volume = 1.;
- bool m_muted = false;
+ QGstreamerAudioInput(QGstElement autoaudiosrc, QGstElement volume, QAudioInput *parent);
QAudioDevice m_audioDevice;
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp
index 0c9f84fbc..6156c97be 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp
@@ -1,71 +1,54 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qgstreameraudiooutput_p.h>
-#include <qgstreameraudiodevice_p.h>
-#include <qaudiodevice.h>
-#include <qaudiooutput.h>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include <common/qgstreameraudiooutput_p.h>
+#include <audio/qgstreameraudiodevice_p.h>
+
+#include <QtMultimedia/qaudiodevice.h>
+#include <QtMultimedia/qaudiooutput.h>
#include <QtCore/qloggingcategory.h>
-#include <QtNetwork/qnetworkaccessmanager.h>
-#include <QtNetwork/qnetworkreply.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
+#include <utility>
-Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput")
+static Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput")
QT_BEGIN_NAMESPACE
-QGstreamerAudioOutput::QGstreamerAudioOutput(QAudioOutput *parent)
- : QObject(parent),
- QPlatformAudioOutput(parent),
- gstAudioOutput("audioOutput")
+QMaybe<QPlatformAudioOutput *> QGstreamerAudioOutput::create(QAudioOutput *parent)
+{
+ QGstElement audioconvert = QGstElement::createFromFactory("audioconvert", "audioConvert");
+ if (!audioconvert)
+ return errorMessageCannotFindElement("audioconvert");
+
+ QGstElement audioresample = QGstElement::createFromFactory("audioresample", "audioResample");
+ if (!audioresample)
+ return errorMessageCannotFindElement("audioresample");
+
+ QGstElement volume = QGstElement::createFromFactory("volume", "volume");
+ if (!volume)
+ return errorMessageCannotFindElement("volume");
+
+ QGstElement autoaudiosink = QGstElement::createFromFactory("autoaudiosink", "autoAudioSink");
+ if (!autoaudiosink)
+ return errorMessageCannotFindElement("autoaudiosink");
+
+ return new QGstreamerAudioOutput(audioconvert, audioresample, volume, autoaudiosink, parent);
+}
+
+QGstreamerAudioOutput::QGstreamerAudioOutput(QGstElement audioconvert, QGstElement audioresample,
+ QGstElement volume, QGstElement autoaudiosink,
+ QAudioOutput *parent)
+ : QObject(parent),
+ QPlatformAudioOutput(parent),
+ gstAudioOutput(QGstBin::create("audioOutput")),
+ audioConvert(std::move(audioconvert)),
+ audioResample(std::move(audioresample)),
+ audioVolume(std::move(volume)),
+ audioSink(std::move(autoaudiosink))
{
- audioQueue = QGstElement("queue", "audioQueue");
- audioConvert = QGstElement("audioconvert", "audioConvert");
- audioResample = QGstElement("audioresample", "audioResample");
- audioVolume = QGstElement("volume", "volume");
- audioSink = QGstElement("autoaudiosink", "autoAudioSink");
+ audioQueue = QGstElement::createFromFactory("queue", "audioQueue");
gstAudioOutput.add(audioQueue, audioConvert, audioResample, audioVolume, audioSink);
- audioQueue.link(audioConvert, audioResample, audioVolume, audioSink);
+ qLinkGstElements(audioQueue, audioConvert, audioResample, audioVolume, audioSink);
gstAudioOutput.addGhostPad(audioQueue, "sink");
}
@@ -75,9 +58,9 @@ QGstreamerAudioOutput::~QGstreamerAudioOutput()
gstAudioOutput.setStateSync(GST_STATE_NULL);
}
-void QGstreamerAudioOutput::setVolume(float vol)
+void QGstreamerAudioOutput::setVolume(float volume)
{
- audioVolume.set("volume", vol);
+ audioVolume.set("volume", volume);
}
void QGstreamerAudioOutput::setMuted(bool muted)
@@ -85,49 +68,50 @@ void QGstreamerAudioOutput::setMuted(bool muted)
audioVolume.set("mute", muted);
}
-void QGstreamerAudioOutput::setPipeline(const QGstPipeline &pipeline)
-{
- gstPipeline = pipeline;
-}
-
void QGstreamerAudioOutput::setAudioDevice(const QAudioDevice &info)
{
if (info == m_audioOutput)
return;
qCDebug(qLcMediaAudioOutput) << "setAudioOutput" << info.description() << info.isNull();
+
m_audioOutput = info;
+ const QByteArray &id = m_audioOutput.id();
QGstElement newSink;
-#if QT_CONFIG(pulseaudio)
- auto id = m_audioOutput.id();
- newSink = QGstElement("pulsesink", "audiosink");
- if (!newSink.isNull())
- newSink.set("device", id.constData());
- else
- qCWarning(qLcMediaAudioOutput) << "Invalid audio device";
-#else
- auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_audioOutput.handle());
- if (deviceInfo && deviceInfo->gstDevice)
- newSink = gst_device_create_element(deviceInfo->gstDevice , "audiosink");
- else
- qCWarning(qLcMediaAudioOutput) << "Invalid audio device";
-#endif
+ if constexpr (QT_CONFIG(pulseaudio)) {
+ newSink = QGstElement::createFromFactory("pulsesink", "audiosink");
+ if (newSink)
+ newSink.set("device", id.constData());
+ else
+ qWarning() << "Cannot create pulsesink";
+ } else if constexpr (QT_CONFIG(alsa)) {
+ newSink = QGstElement::createFromFactory("alsasink", "audiosink");
+ if (newSink)
+ newSink.set("device", id.constData());
+ else
+ qWarning() << "Cannot create alsasink";
+ } else {
+ auto *deviceInfo = dynamic_cast<const QGStreamerAudioDeviceInfo *>(m_audioOutput.handle());
+ if (deviceInfo && deviceInfo->gstDevice)
+ newSink = QGstElement::createFromDevice(deviceInfo->gstDevice, "audiosink");
+ else
+ qWarning() << "Invalid audio device";
+ }
if (newSink.isNull()) {
- qCWarning(qLcMediaAudioOutput) << "Failed to create a gst element for the audio device, using a default audio sink";
- newSink = QGstElement("autoaudiosink", "audiosink");
+ qWarning() << "Failed to create a gst element for the audio "
+ "device using a default audio sink";
+ newSink = QGstElement::createFromFactory("autoaudiosink", "audiosink");
}
- audioVolume.staticPad("src").doInIdleProbe([&](){
- audioVolume.unlink(audioSink);
- gstAudioOutput.remove(audioSink);
- gstAudioOutput.add(newSink);
- newSink.syncStateWithParent();
- audioVolume.link(newSink);
+ QGstPipeline::modifyPipelineWhileNotRunning(gstAudioOutput.getPipeline(), [&] {
+ qUnlinkGstElements(audioVolume, audioSink);
+ gstAudioOutput.stopAndRemoveElements(audioSink);
+ audioSink = std::move(newSink);
+ gstAudioOutput.add(audioSink);
+ audioSink.syncStateWithParent();
+ qLinkGstElements(audioVolume, audioSink);
});
-
- audioSink.setStateSync(GST_STATE_NULL);
- audioSink = newSink;
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h
index 318419247..4b528d9ee 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERAUDIOOUTPUT_P_H
#define QGSTREAMERAUDIOOUTPUT_P_H
@@ -51,41 +15,35 @@
// We mean it.
//
-#include <private/qtmultimediaglobal_p.h>
-#include <qaudiodevice.h>
-
#include <QtCore/qobject.h>
+#include <QtMultimedia/private/qmultimediautils_p.h>
+#include <QtMultimedia/private/qplatformaudiooutput_p.h>
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <QtMultimedia/qaudiodevice.h>
-#include <qgst_p.h>
-#include <qgstpipeline_p.h>
-#include <private/qplatformaudiooutput_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
QT_BEGIN_NAMESPACE
-class QGstreamerMessage;
class QAudioDevice;
-class Q_MULTIMEDIA_EXPORT QGstreamerAudioOutput : public QObject, public QPlatformAudioOutput
+class QGstreamerAudioOutput : public QObject, public QPlatformAudioOutput
{
- Q_OBJECT
-
public:
- QGstreamerAudioOutput(QAudioOutput *parent);
+ static QMaybe<QPlatformAudioOutput *> create(QAudioOutput *parent);
~QGstreamerAudioOutput();
void setAudioDevice(const QAudioDevice &) override;
- void setVolume(float volume) override;
- void setMuted(bool muted) override;
-
- void setPipeline(const QGstPipeline &pipeline);
+ void setVolume(float) override;
+ void setMuted(bool) override;
QGstElement gstElement() const { return gstAudioOutput; }
-Q_SIGNALS:
- void mutedChanged(bool);
- void volumeChanged(int);
-
private:
+ QGstreamerAudioOutput(QGstElement audioconvert, QGstElement audioresample, QGstElement volume,
+ QGstElement autoaudiosink, QAudioOutput *parent);
+
QAudioDevice m_audioOutput;
// Gst elements
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp
index 3af8000bb..9cba810db 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe.cpp
@@ -1,44 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qgstreamerbufferprobe_p.h"
-#include "qgstutils_p.h"
+// Copyright (C) 2016 Jolla Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <common/qgstreamerbufferprobe_p.h>
+
+#include <common/qgst_p.h>
QT_BEGIN_NAMESPACE
@@ -51,10 +16,14 @@ QGstreamerBufferProbe::~QGstreamerBufferProbe() = default;
void QGstreamerBufferProbe::addProbeToPad(GstPad *pad, bool downstream)
{
- if (GstCaps *caps = gst_pad_get_current_caps(pad)) {
- probeCaps(caps);
- gst_caps_unref(caps);
- }
+ QGstCaps caps{
+ gst_pad_get_current_caps(pad),
+ QGstCaps::HasRef,
+ };
+
+ if (caps)
+ probeCaps(caps.caps());
+
if (m_flags & ProbeCaps) {
m_capsProbeId = gst_pad_add_probe(
pad,
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h
index d3ca9db2e..71996a0cc 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamerbufferprobe_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Jolla Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERBUFFERPROBE_H
#define QGSTREAMERBUFFERPROBE_H
@@ -59,7 +23,7 @@
QT_BEGIN_NAMESPACE
-class Q_MULTIMEDIA_EXPORT QGstreamerBufferProbe
+class QGstreamerBufferProbe
{
public:
enum Flags
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
index c8e730c3d..58b25a120 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp
@@ -1,73 +1,37 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qgstreamermediaplayer_p.h>
-#include <qgstpipeline_p.h>
-#include <qgstreamermetadata_p.h>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <common/qgstreamermediaplayer_p.h>
+
+#include <audio/qgstreameraudiodevice_p.h>
+#include <common/qgst_debug_p.h>
+#include <common/qgstappsource_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreameraudiooutput_p.h>
+#include <common/qgstreamermessage_p.h>
+#include <common/qgstreamermetadata_p.h>
+#include <common/qgstreamervideooutput_p.h>
+#include <common/qgstreamervideosink_p.h>
#include <qgstreamerformatinfo_p.h>
-#include <qgstreameraudiooutput_p.h>
-#include <qgstreamervideooutput_p.h>
-#include <qgstreamervideosink_p.h>
-#include "qgstreamermessage_p.h"
-#include <qgstreameraudiodevice_p.h>
-#include <qgstappsrc_p.h>
-#include <qaudiodevice.h>
+#include <QtMultimedia/qaudiodevice.h>
#include <QtCore/qdir.h>
#include <QtCore/qsocketnotifier.h>
#include <QtCore/qurl.h>
#include <QtCore/qdebug.h>
#include <QtCore/qloggingcategory.h>
-#include <QtNetwork/qnetworkaccessmanager.h>
-#include <QtNetwork/qnetworkreply.h>
+#include <QtCore/private/quniquehandle_p.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-Q_LOGGING_CATEGORY(qLcMediaPlayer, "qt.multimedia.player")
+static Q_LOGGING_CATEGORY(qLcMediaPlayer, "qt.multimedia.player")
QT_BEGIN_NAMESPACE
-QGstreamerMediaPlayer::TrackSelector::TrackSelector(TrackType type, const char *name)
- : selector(QGstElement("input-selector", name)),
- type(type)
+QGstreamerMediaPlayer::TrackSelector::TrackSelector(TrackType type, QGstElement selector)
+ : selector(selector), type(type)
{
selector.set("sync-streams", true);
selector.set("sync-mode", 1 /*clock*/);
@@ -110,17 +74,59 @@ QGstreamerMediaPlayer::TrackSelector &QGstreamerMediaPlayer::trackSelector(Track
return ts;
}
-QGstreamerMediaPlayer::QGstreamerMediaPlayer(QMediaPlayer *parent)
+void QGstreamerMediaPlayer::disconnectDecoderHandlers()
+{
+ auto handlers = std::initializer_list<QGObjectHandlerScopedConnection *>{
+ &padAdded, &padRemoved, &sourceSetup, &uridecodebinElementAdded,
+ &unknownType, &elementAdded, &elementRemoved,
+ };
+ for (QGObjectHandlerScopedConnection *handler : handlers)
+ handler->disconnect();
+
+ decodeBinQueues = 0;
+}
+
+QMaybe<QPlatformMediaPlayer *> QGstreamerMediaPlayer::create(QMediaPlayer *parent)
+{
+ auto videoOutput = QGstreamerVideoOutput::create();
+ if (!videoOutput)
+ return videoOutput.error();
+
+ QGstElement videoInputSelector =
+ QGstElement::createFromFactory("input-selector", "videoInputSelector");
+ if (!videoInputSelector)
+ return errorMessageCannotFindElement("input-selector");
+
+ QGstElement audioInputSelector =
+ QGstElement::createFromFactory("input-selector", "audioInputSelector");
+ if (!audioInputSelector)
+ return errorMessageCannotFindElement("input-selector");
+
+ QGstElement subTitleInputSelector =
+ QGstElement::createFromFactory("input-selector", "subTitleInputSelector");
+ if (!subTitleInputSelector)
+ return errorMessageCannotFindElement("input-selector");
+
+ return new QGstreamerMediaPlayer(videoOutput.value(), videoInputSelector, audioInputSelector,
+ subTitleInputSelector, parent);
+}
+
+QGstreamerMediaPlayer::QGstreamerMediaPlayer(QGstreamerVideoOutput *videoOutput,
+ QGstElement videoInputSelector,
+ QGstElement audioInputSelector,
+ QGstElement subTitleInputSelector,
+ QMediaPlayer *parent)
: QObject(parent),
QPlatformMediaPlayer(parent),
- trackSelectors{{{ VideoStream, "videoInputSelector" },
- { AudioStream, "audioInputSelector" },
- { SubtitleStream, "subTitleInputSelector" }}},
- playerPipeline("playerPipeline")
+ trackSelectors{ { { VideoStream, videoInputSelector },
+ { AudioStream, audioInputSelector },
+ { SubtitleStream, subTitleInputSelector } } },
+ playerPipeline(QGstPipeline::create("playerPipeline")),
+ gstVideoOutput(videoOutput)
{
playerPipeline.setFlushOnConfigChanges(true);
- gstVideoOutput = new QGstreamerVideoOutput(this);
+ gstVideoOutput->setParent(this);
gstVideoOutput->setPipeline(playerPipeline);
for (auto &ts : trackSelectors)
@@ -130,15 +136,15 @@ QGstreamerMediaPlayer::QGstreamerMediaPlayer(QMediaPlayer *parent)
playerPipeline.installMessageFilter(static_cast<QGstreamerBusMessageFilter *>(this));
playerPipeline.installMessageFilter(static_cast<QGstreamerSyncMessageFilter *>(this));
- gst_pipeline_use_clock(playerPipeline.pipeline(), gst_system_clock_obtain());
+ QGstClockHandle systemClock{
+ gst_system_clock_obtain(),
+ };
+
+ gst_pipeline_use_clock(playerPipeline.pipeline(), systemClock.get());
- /* Taken from gstdicoverer.c:
- * This is ugly. We get the GType of decodebin so we can quickly detect
- * when a decodebin is added to uridecodebin so we can set the
- * post-stream-topology setting to TRUE */
- auto decodebin = QGstElement("decodebin", nullptr);
- decodebinType = G_OBJECT_TYPE(decodebin.element());
- connect(&positionUpdateTimer, &QTimer::timeout, this, &QGstreamerMediaPlayer::updatePosition);
+ connect(&positionUpdateTimer, &QTimer::timeout, this, [this] {
+ updatePosition();
+ });
}
QGstreamerMediaPlayer::~QGstreamerMediaPlayer()
@@ -179,7 +185,8 @@ qreal QGstreamerMediaPlayer::playbackRate() const
void QGstreamerMediaPlayer::setPlaybackRate(qreal rate)
{
- if (playerPipeline.setPlaybackRate(rate))
+ bool applyRateToPipeline = state() != QMediaPlayer::StoppedState;
+ if (playerPipeline.setPlaybackRate(rate, applyRateToPipeline))
playbackRateChanged(rate);
}
@@ -218,15 +225,15 @@ void QGstreamerMediaPlayer::play()
}
if (ret == GST_STATE_CHANGE_FAILURE)
qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the playing state.";
- if (mediaStatus() == QMediaPlayer::LoadedMedia)
- mediaStatusChanged(QMediaPlayer::BufferedMedia);
- emit stateChanged(QMediaPlayer::PlayingState);
+
positionUpdateTimer.start(100);
+ emit stateChanged(QMediaPlayer::PlayingState);
}
void QGstreamerMediaPlayer::pause()
{
- if (state() == QMediaPlayer::PausedState || m_url.isEmpty())
+ if (state() == QMediaPlayer::PausedState || m_url.isEmpty()
+ || m_resourceErrorState != ResourceErrorState::NoError)
return;
positionUpdateTimer.stop();
@@ -239,19 +246,36 @@ void QGstreamerMediaPlayer::pause()
qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the paused state.";
if (mediaStatus() == QMediaPlayer::EndOfMedia) {
playerPipeline.setPosition(0);
- mediaStatusChanged(QMediaPlayer::BufferedMedia);
}
updatePosition();
emit stateChanged(QMediaPlayer::PausedState);
+
+ if (m_bufferProgress > 0 || !canTrackProgress())
+ mediaStatusChanged(QMediaPlayer::BufferedMedia);
+ else
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+
+ emit bufferProgressChanged(m_bufferProgress / 100.);
}
void QGstreamerMediaPlayer::stop()
{
- if (state() == QMediaPlayer::StoppedState)
+ if (state() == QMediaPlayer::StoppedState) {
+ if (position() != 0) {
+ playerPipeline.setPosition(0);
+ positionChanged(0);
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+ }
return;
+ }
stopOrEOS(false);
}
+void *QGstreamerMediaPlayer::nativePipeline()
+{
+ return playerPipeline.pipeline();
+}
+
void QGstreamerMediaPlayer::stopOrEOS(bool eos)
{
positionUpdateTimer.stop();
@@ -263,26 +287,53 @@ void QGstreamerMediaPlayer::stopOrEOS(bool eos)
playerPipeline.setPosition(0);
updatePosition();
emit stateChanged(QMediaPlayer::StoppedState);
- mediaStatusChanged(eos ? QMediaPlayer::EndOfMedia : QMediaPlayer::LoadedMedia);
+ if (eos)
+ mediaStatusChanged(QMediaPlayer::EndOfMedia);
+ else
+ mediaStatusChanged(QMediaPlayer::LoadedMedia);
+ m_initialBufferProgressSent = false;
}
-bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
+void QGstreamerMediaPlayer::detectPipelineIsSeekable()
{
- if (message.isNull())
- return false;
+ qCDebug(qLcMediaPlayer) << "detectPipelineIsSeekable";
+ QGstQueryHandle query{
+ gst_query_new_seeking(GST_FORMAT_TIME),
+ QGstQueryHandle::HasRef,
+ };
+ gboolean canSeek = false;
+ if (gst_element_query(playerPipeline.element(), query.get())) {
+ gst_query_parse_seeking(query.get(), nullptr, &canSeek, nullptr, nullptr);
+ qCDebug(qLcMediaPlayer) << " pipeline is seekable:" << canSeek;
+ } else {
+ qCWarning(qLcMediaPlayer) << " query for seekable failed.";
+ }
+ seekableChanged(canSeek);
+}
-// qCDebug(qLcMediaPlayer) << "received bus message from" << message.source().name() << message.type() << (message.type() == GST_MESSAGE_TAG);
+bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
+{
+ qCDebug(qLcMediaPlayer) << "received bus message:" << message;
- GstMessage* gm = message.rawMessage();
+ GstMessage* gm = message.message();
switch (message.type()) {
case GST_MESSAGE_TAG: {
// #### This isn't ideal. We shouldn't catch stream specific tags here, rather the global ones
- GstTagList *tag_list;
- gst_message_parse_tag(gm, &tag_list);
- //qCDebug(qLcMediaPlayer) << "Got tags: " << message.source().name() << gst_tag_list_to_string(tag_list);
- auto metaData = QGstreamerMetaData::fromGstTagList(tag_list);
+ QGstTagListHandle tagList;
+ gst_message_parse_tag(gm, &tagList);
+
+ qCDebug(qLcMediaPlayer) << " Got tags: " << tagList.get();
+ auto metaData = taglistToMetaData(tagList);
+ auto keys = metaData.keys();
for (auto k : metaData.keys())
m_metaData.insert(k, metaData.value(k));
+ if (!keys.isEmpty())
+ emit metaDataChanged();
+
+ if (gstVideoOutput) {
+ QVariant rotation = m_metaData.value(QMediaMetaData::Orientation);
+ gstVideoOutput->setRotation(rotation.value<QtVideo::Rotation>());
+ }
break;
}
case GST_MESSAGE_DURATION_CHANGED: {
@@ -302,12 +353,29 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
stopOrEOS(true);
break;
case GST_MESSAGE_BUFFERING: {
- qCDebug(qLcMediaPlayer) << " buffering message";
int progress = 0;
gst_message_parse_buffering(gm, &progress);
+
+ qCDebug(qLcMediaPlayer) << " buffering message: " << progress;
+
+ if (state() != QMediaPlayer::StoppedState && !prerolling) {
+ if (!m_initialBufferProgressSent) {
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+ m_initialBufferProgressSent = true;
+ }
+
+ if (m_bufferProgress > 0 && progress == 0)
+ mediaStatusChanged(QMediaPlayer::StalledMedia);
+ else if (progress >= 50)
+ // QTBUG-124517: rethink buffering
+ mediaStatusChanged(QMediaPlayer::BufferedMedia);
+ else
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+ }
+
m_bufferProgress = progress;
- mediaStatusChanged(m_bufferProgress == 100 ? QMediaPlayer::BufferedMedia : QMediaPlayer::BufferingMedia);
- emit bufferProgressChanged(m_bufferProgress/100.);
+
+ emit bufferProgressChanged(m_bufferProgress / 100.);
break;
}
case GST_MESSAGE_STATE_CHANGED: {
@@ -319,33 +387,22 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
GstState pending;
gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
- qCDebug(qLcMediaPlayer) << " state changed message" << oldState << newState << pending;
-
-#ifdef DEBUG_PLAYBIN
- static QStringList states = {
- QStringLiteral("GST_STATE_VOID_PENDING"), QStringLiteral("GST_STATE_NULL"),
- QStringLiteral("GST_STATE_READY"), QStringLiteral("GST_STATE_PAUSED"),
- QStringLiteral("GST_STATE_PLAYING") };
-
- qCDebug(qLcMediaPlayer) << QStringLiteral("state changed: old: %1 new: %2 pending: %3") \
- .arg(states[oldState]) \
- .arg(states[newState]) \
- .arg(states[pending]);
-#endif
+ qCDebug(qLcMediaPlayer) << " state changed message from"
+ << QCompactGstMessageAdaptor(message);
switch (newState) {
case GST_STATE_VOID_PENDING:
case GST_STATE_NULL:
case GST_STATE_READY:
break;
- case GST_STATE_PAUSED:
- {
+ case GST_STATE_PAUSED: {
if (prerolling) {
qCDebug(qLcMediaPlayer) << "Preroll done, setting status to Loaded";
prerolling = false;
- GST_DEBUG_BIN_TO_DOT_FILE(playerPipeline.bin(), GST_DEBUG_GRAPH_SHOW_ALL, "playerPipeline");
+ GST_DEBUG_BIN_TO_DOT_FILE(playerPipeline.bin(), GST_DEBUG_GRAPH_SHOW_ALL,
+ "playerPipeline");
- qint64 d = playerPipeline.duration()/1e6;
+ qint64 d = playerPipeline.duration() / 1e6;
if (d != m_duration) {
m_duration = d;
qCDebug(qLcMediaPlayer) << " duration changed" << d;
@@ -357,61 +414,75 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
emit tracksChanged();
mediaStatusChanged(QMediaPlayer::LoadedMedia);
- GstQuery *query = gst_query_new_seeking(GST_FORMAT_TIME);
- gboolean canSeek = false;
- if (gst_element_query(playerPipeline.element(), query)) {
- gst_query_parse_seeking(query, NULL, &canSeek, nullptr, nullptr);
- qCDebug(qLcMediaPlayer) << " pipeline is seekable:" << canSeek;
- } else {
- qCDebug(qLcMediaPlayer) << " query for seekable failed.";
+ if (!playerPipeline.inStoppedState()) {
+ Q_ASSERT(!m_initialBufferProgressSent);
+
+ bool immediatelySendBuffered = !canTrackProgress() || m_bufferProgress > 0;
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+ m_initialBufferProgressSent = true;
+ if (immediatelySendBuffered)
+ mediaStatusChanged(QMediaPlayer::BufferedMedia);
}
- gst_query_unref(query);
- seekableChanged(canSeek);
}
break;
}
- case GST_STATE_PLAYING:
- mediaStatusChanged(QMediaPlayer::BufferedMedia);
+ case GST_STATE_PLAYING: {
+ if (!m_initialBufferProgressSent) {
+ bool immediatelySendBuffered = !canTrackProgress() || m_bufferProgress > 0;
+ mediaStatusChanged(QMediaPlayer::BufferingMedia);
+ m_initialBufferProgressSent = true;
+ if (immediatelySendBuffered)
+ mediaStatusChanged(QMediaPlayer::BufferedMedia);
+ }
break;
}
+ }
break;
}
case GST_MESSAGE_ERROR: {
- GError *err;
- gchar *debug;
+ qCDebug(qLcMediaPlayer) << " error" << QCompactGstMessageAdaptor(message);
+
+ QUniqueGErrorHandle err;
+ QUniqueGStringHandle debug;
gst_message_parse_error(gm, &err, &debug);
- if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND)
- emit error(QMediaPlayer::FormatError, tr("Cannot play stream of type: <unknown>"));
- else
- emit error(QMediaPlayer::ResourceError, QString::fromUtf8(err->message));
- playerPipeline.dumpGraph("error");
+ GQuark errorDomain = err.get()->domain;
+ gint errorCode = err.get()->code;
+
+ if (errorDomain == GST_STREAM_ERROR) {
+ if (errorCode == GST_STREAM_ERROR_CODEC_NOT_FOUND)
+ error(QMediaPlayer::FormatError, tr("Cannot play stream of type: <unknown>"));
+ else {
+ error(QMediaPlayer::FormatError, QString::fromUtf8(err.get()->message));
+ }
+ } else if (errorDomain == GST_RESOURCE_ERROR) {
+ if (errorCode == GST_RESOURCE_ERROR_NOT_FOUND) {
+ if (m_resourceErrorState != ResourceErrorState::ErrorReported) {
+ // gstreamer seems to deliver multiple GST_RESOURCE_ERROR_NOT_FOUND events
+ error(QMediaPlayer::ResourceError, QString::fromUtf8(err.get()->message));
+ m_resourceErrorState = ResourceErrorState::ErrorReported;
+ m_url.clear();
+ }
+ } else {
+ error(QMediaPlayer::ResourceError, QString::fromUtf8(err.get()->message));
+ }
+ } else {
+ playerPipeline.dumpGraph("error");
+ }
mediaStatusChanged(QMediaPlayer::InvalidMedia);
- g_error_free(err);
- g_free(debug);
break;
}
- case GST_MESSAGE_WARNING: {
- GError *err;
- gchar *debug;
- gst_message_parse_warning (gm, &err, &debug);
- qCWarning(qLcMediaPlayer) << "Warning:" << QString::fromUtf8(err->message);
+
+ case GST_MESSAGE_WARNING:
+ qCWarning(qLcMediaPlayer) << "Warning:" << QCompactGstMessageAdaptor(message);
playerPipeline.dumpGraph("warning");
- g_error_free (err);
- g_free (debug);
break;
- }
- case GST_MESSAGE_INFO: {
- if (qLcMediaPlayer().isDebugEnabled()) {
- GError *err;
- gchar *debug;
- gst_message_parse_info (gm, &err, &debug);
- qCDebug(qLcMediaPlayer) << "Info:" << QString::fromUtf8(err->message);
- g_error_free (err);
- g_free (debug);
- }
+
+ case GST_MESSAGE_INFO:
+ if (qLcMediaPlayer().isDebugEnabled())
+ qCDebug(qLcMediaPlayer) << "Info:" << QCompactGstMessageAdaptor(message);
break;
- }
+
case GST_MESSAGE_SEGMENT_START: {
qCDebug(qLcMediaPlayer) << " segment start message, updating position";
QGstStructure structure(gst_message_get_structure(gm));
@@ -432,6 +503,11 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message)
break;
}
+ case GST_MESSAGE_ASYNC_DONE: {
+ detectPipelineIsSeekable();
+ break;
+ }
+
default:
// qCDebug(qLcMediaPlayer) << " default message handler, doing nothing";
@@ -447,7 +523,7 @@ bool QGstreamerMediaPlayer::processSyncMessage(const QGstreamerMessage &message)
if (message.type() != GST_MESSAGE_NEED_CONTEXT)
return false;
const gchar *type = nullptr;
- gst_message_parse_context_type (message.rawMessage(), &type);
+ gst_message_parse_context_type (message.message(), &type);
if (strcmp(type, GST_GL_DISPLAY_CONTEXT_TYPE))
return false;
if (!gstVideoOutput || !gstVideoOutput->gstreamerVideoSink())
@@ -455,7 +531,7 @@ bool QGstreamerMediaPlayer::processSyncMessage(const QGstreamerMessage &message)
auto *context = gstVideoOutput->gstreamerVideoSink()->gstGlDisplayContext();
if (!context)
return false;
- gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message.rawMessage())), context);
+ gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message.message())), context);
playerPipeline.dumpGraph("need_context");
return true;
#else
@@ -482,7 +558,7 @@ void QGstreamerMediaPlayer::decoderPadAdded(const QGstElement &src, const QGstPa
auto caps = pad.currentCaps();
auto type = caps.at(0).name();
qCDebug(qLcMediaPlayer) << "Received new pad" << pad.name() << "from" << src.name() << "type" << type;
- qCDebug(qLcMediaPlayer) << " " << caps.toString();
+ qCDebug(qLcMediaPlayer) << " " << caps;
TrackType streamType = NTrackTypes;
if (type.startsWith("video/x-raw")) {
@@ -587,7 +663,7 @@ void QGstreamerMediaPlayer::connectOutput(TrackSelector &ts)
if (!e.isNull()) {
qCDebug(qLcMediaPlayer) << "connecting output for track type" << ts.type;
playerPipeline.add(e);
- ts.selector.link(e);
+ qLinkGstElements(ts.selector, e);
e.setState(GST_STATE_PAUSED);
}
@@ -617,29 +693,133 @@ void QGstreamerMediaPlayer::removeOutput(TrackSelector &ts)
if (!e.isNull()) {
qCDebug(qLcMediaPlayer) << "removing output for track type" << ts.type;
- playerPipeline.remove(e);
- e.setStateSync(GST_STATE_NULL);
+ playerPipeline.stopAndRemoveElements(e);
}
ts.isConnected = false;
}
-void QGstreamerMediaPlayer::uridecodebinElementAddedCallback(GstElement */*uridecodebin*/, GstElement *child, QGstreamerMediaPlayer *that)
+void QGstreamerMediaPlayer::removeDynamicPipelineElements()
+{
+ for (QGstElement *element : { &src, &decoder }) {
+ if (element->isNull())
+ continue;
+
+ element->setStateSync(GstState::GST_STATE_NULL);
+ playerPipeline.remove(*element);
+ *element = QGstElement{};
+ }
+}
+
+void QGstreamerMediaPlayer::uridecodebinElementAddedCallback(GstElement * /*uridecodebin*/,
+ GstElement *child,
+ QGstreamerMediaPlayer *)
{
- QGstElement c(child);
+ QGstElement c(child, QGstElement::NeedsRef);
qCDebug(qLcMediaPlayer) << "New element added to uridecodebin:" << c.name();
- if (G_OBJECT_TYPE(child) == that->decodebinType) {
+ static const GType decodeBinType = [] {
+ QGstElementFactoryHandle factory = QGstElementFactoryHandle{
+ gst_element_factory_find("decodebin"),
+ };
+ return gst_element_factory_get_element_type(factory.get());
+ }();
+
+ if (c.type() == decodeBinType) {
qCDebug(qLcMediaPlayer) << " -> setting post-stream-topology property";
c.set("post-stream-topology", true);
}
}
+void QGstreamerMediaPlayer::sourceSetupCallback(GstElement *uridecodebin, GstElement *source, QGstreamerMediaPlayer *that)
+{
+ Q_UNUSED(uridecodebin)
+ Q_UNUSED(that)
+
+ qCDebug(qLcMediaPlayer) << "Setting up source:" << g_type_name_from_instance((GTypeInstance*)source);
+
+ if (std::string_view("GstRTSPSrc") == g_type_name_from_instance((GTypeInstance *)source)) {
+ QGstElement s(source, QGstElement::NeedsRef);
+ int latency{40};
+ bool ok{false};
+ int v = qEnvironmentVariableIntValue("QT_MEDIA_RTSP_LATENCY", &ok);
+ if (ok)
+ latency = v;
+ qCDebug(qLcMediaPlayer) << " -> setting source latency to:" << latency << "ms";
+ s.set("latency", latency);
+
+ bool drop{true};
+ v = qEnvironmentVariableIntValue("QT_MEDIA_RTSP_DROP_ON_LATENCY", &ok);
+ if (ok && v == 0)
+ drop = false;
+ qCDebug(qLcMediaPlayer) << " -> setting drop-on-latency to:" << drop;
+ s.set("drop-on-latency", drop);
+
+ bool retrans{false};
+ v = qEnvironmentVariableIntValue("QT_MEDIA_RTSP_DO_RETRANSMISSION", &ok);
+ if (ok && v != 0)
+ retrans = true;
+ qCDebug(qLcMediaPlayer) << " -> setting do-retransmission to:" << retrans;
+ s.set("do-retransmission", retrans);
+ }
+}
+
+void QGstreamerMediaPlayer::unknownTypeCallback(GstElement *decodebin, GstPad *pad, GstCaps *caps,
+ QGstreamerMediaPlayer *self)
+{
+ Q_UNUSED(decodebin)
+ Q_UNUSED(pad)
+ Q_UNUSED(self)
+ qCDebug(qLcMediaPlayer) << "Unknown type:" << caps;
+
+ QMetaObject::invokeMethod(self, [self] {
+ self->stop();
+ });
+}
+
+static bool isQueue(const QGstElement &element)
+{
+ static const GType queueType = [] {
+ QGstElementFactoryHandle factory = QGstElementFactoryHandle{
+ gst_element_factory_find("queue"),
+ };
+ return gst_element_factory_get_element_type(factory.get());
+ }();
+
+ static const GType multiQueueType = [] {
+ QGstElementFactoryHandle factory = QGstElementFactoryHandle{
+ gst_element_factory_find("multiqueue"),
+ };
+ return gst_element_factory_get_element_type(factory.get());
+ }();
+
+ return element.type() == queueType || element.type() == multiQueueType;
+}
+
+void QGstreamerMediaPlayer::decodebinElementAddedCallback(GstBin * /*decodebin*/,
+ GstBin * /*sub_bin*/, GstElement *child,
+ QGstreamerMediaPlayer *self)
+{
+ QGstElement c(child, QGstElement::NeedsRef);
+ if (isQueue(c))
+ self->decodeBinQueues += 1;
+}
+
+void QGstreamerMediaPlayer::decodebinElementRemovedCallback(GstBin * /*decodebin*/,
+ GstBin * /*sub_bin*/, GstElement *child,
+ QGstreamerMediaPlayer *self)
+{
+ QGstElement c(child, QGstElement::NeedsRef);
+ if (isQueue(c))
+ self->decodeBinQueues -= 1;
+}
+
void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
{
qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << "setting location to" << content;
prerolling = true;
+ m_resourceErrorState = ResourceErrorState::NoError;
bool ret = playerPipeline.setStateSync(GST_STATE_NULL);
if (!ret)
@@ -648,15 +828,11 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
m_url = content;
m_stream = stream;
- if (!src.isNull())
- playerPipeline.remove(src);
- if (!decoder.isNull())
- playerPipeline.remove(decoder);
- src = QGstElement();
- decoder = QGstElement();
+ removeDynamicPipelineElements();
+ disconnectDecoderHandlers();
removeAllOutputs();
seekableChanged(false);
- playerPipeline.setInStoppedState(true);
+ Q_ASSERT(playerPipeline.inStoppedState());
if (m_duration != 0) {
m_duration = 0;
@@ -665,52 +841,92 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream)
stateChanged(QMediaPlayer::StoppedState);
if (position() != 0)
positionChanged(0);
- mediaStatusChanged(QMediaPlayer::NoMedia);
if (!m_metaData.isEmpty()) {
m_metaData.clear();
metaDataChanged();
}
+ if (content.isEmpty() && !stream)
+ mediaStatusChanged(QMediaPlayer::NoMedia);
+
if (content.isEmpty())
return;
if (m_stream) {
- if (!m_appSrc)
- m_appSrc = new QGstAppSrc(this);
+ if (!m_appSrc) {
+ auto maybeAppSrc = QGstAppSource::create(this);
+ if (maybeAppSrc) {
+ m_appSrc = maybeAppSrc.value();
+ } else {
+ error(QMediaPlayer::ResourceError, maybeAppSrc.error());
+ return;
+ }
+ }
src = m_appSrc->element();
- decoder = QGstElement("decodebin", "decoder");
+ decoder = QGstElement::createFromFactory("decodebin", "decoder");
+ if (!decoder) {
+ error(QMediaPlayer::ResourceError, errorMessageCannotFindElement("decodebin"));
+ return;
+ }
decoder.set("post-stream-topology", true);
+ decoder.set("use-buffering", true);
+ unknownType = decoder.connect("unknown-type", GCallback(unknownTypeCallback), this);
+ elementAdded = decoder.connect("deep-element-added",
+ GCallback(decodebinElementAddedCallback), this);
+ elementRemoved = decoder.connect("deep-element-removed",
+ GCallback(decodebinElementAddedCallback), this);
+
playerPipeline.add(src, decoder);
- src.link(decoder);
+ qLinkGstElements(src, decoder);
m_appSrc->setup(m_stream);
seekableChanged(!stream->isSequential());
} else {
// use uridecodebin
- decoder = QGstElement("uridecodebin", "uridecoder");
+ decoder = QGstElement::createFromFactory("uridecodebin", "decoder");
+ if (!decoder) {
+ error(QMediaPlayer::ResourceError, errorMessageCannotFindElement("uridecodebin"));
+ return;
+ }
playerPipeline.add(decoder);
- // can't set post-stream-topology to true, as uridecodebin doesn't have the property. Use a hack
- decoder.connect("element-added", GCallback(QGstreamerMediaPlayer::uridecodebinElementAddedCallback), this);
+
+ constexpr bool hasPostStreamTopology = GST_CHECK_VERSION(1, 22, 0);
+ if constexpr (hasPostStreamTopology) {
+ decoder.set("post-stream-topology", true);
+ } else {
+ // can't set post-stream-topology to true, as uridecodebin doesn't have the property.
+ // Use a hack
+ uridecodebinElementAdded = decoder.connect(
+ "element-added", GCallback(uridecodebinElementAddedCallback), this);
+ }
+
+ sourceSetup = decoder.connect("source-setup", GCallback(sourceSetupCallback), this);
+ unknownType = decoder.connect("unknown-type", GCallback(unknownTypeCallback), this);
decoder.set("uri", content.toEncoded().constData());
+ decoder.set("use-buffering", true);
+
+ constexpr int mb = 1024 * 1024;
+ decoder.set("ring-buffer-max-size", 2 * mb);
+
if (m_bufferProgress != 0) {
m_bufferProgress = 0;
emit bufferProgressChanged(0.);
}
+
+ elementAdded = decoder.connect("deep-element-added",
+ GCallback(decodebinElementAddedCallback), this);
+ elementRemoved = decoder.connect("deep-element-removed",
+ GCallback(decodebinElementAddedCallback), this);
}
- decoder.onPadAdded<&QGstreamerMediaPlayer::decoderPadAdded>(this);
- decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemoved>(this);
+ padAdded = decoder.onPadAdded<&QGstreamerMediaPlayer::decoderPadAdded>(this);
+ padRemoved = decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemoved>(this);
mediaStatusChanged(QMediaPlayer::LoadingMedia);
-
- if (state() == QMediaPlayer::PlayingState) {
- int ret = playerPipeline.setState(GST_STATE_PLAYING);
- if (ret == GST_STATE_CHANGE_FAILURE)
- qCWarning(qLcMediaPlayer) << "Unable to set the pipeline to the playing state.";
- } else {
- int ret = playerPipeline.setState(GST_STATE_PAUSED);
- if (!ret)
- qCWarning(qLcMediaPlayer) << "Unable to set the pipeline to the paused state.";
+ if (!playerPipeline.setState(GST_STATE_PAUSED)) {
+ qCWarning(qLcMediaPlayer) << "Unable to set the pipeline to the paused state.";
+ // Note: no further error handling: errors will be delivered via a GstMessage
+ return;
}
playerPipeline.setPosition(0);
@@ -724,17 +940,14 @@ void QGstreamerMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
auto &ts = trackSelector(AudioStream);
- playerPipeline.beginConfig();
- if (gstAudioOutput) {
- removeOutput(ts);
- gstAudioOutput->setPipeline({});
- }
- gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
- if (gstAudioOutput) {
- gstAudioOutput->setPipeline(playerPipeline);
- connectOutput(ts);
- }
- playerPipeline.endConfig();
+ playerPipeline.modifyPipelineWhileNotRunning([&] {
+ if (gstAudioOutput)
+ removeOutput(ts);
+
+ gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
+ if (gstAudioOutput)
+ connectOutput(ts);
+ });
}
QMediaMetaData QGstreamerMediaPlayer::metaData() const
@@ -770,15 +983,16 @@ void QGstreamerMediaPlayer::parseStreamsAndMetadata()
auto caps = topology["caps"].toCaps();
auto structure = caps.at(0);
auto fileFormat = QGstreamerFormatInfo::fileFormatForCaps(structure);
- qCDebug(qLcMediaPlayer) << caps.toString() << fileFormat;
+ qCDebug(qLcMediaPlayer) << caps << fileFormat;
m_metaData.insert(QMediaMetaData::FileFormat, QVariant::fromValue(fileFormat));
m_metaData.insert(QMediaMetaData::Duration, duration());
m_metaData.insert(QMediaMetaData::Url, m_url);
QGValue tags = topology["tags"];
if (!tags.isNull()) {
- GstTagList *tagList = nullptr;
+ QGstTagListHandle tagList;
gst_structure_get(topology.structure, "tags", GST_TYPE_TAG_LIST, &tagList, nullptr);
- const auto metaData = QGstreamerMetaData::fromGstTagList(tagList);
+
+ const auto metaData = taglistToMetaData(tagList);
for (auto k : metaData.keys())
m_metaData.insert(k, metaData.value(k));
}
@@ -800,28 +1014,33 @@ void QGstreamerMediaPlayer::parseStreamsAndMetadata()
if (structure.name().startsWith("audio/")) {
auto codec = QGstreamerFormatInfo::audioCodecForCaps(structure);
m_metaData.insert(QMediaMetaData::AudioCodec, QVariant::fromValue(codec));
- qCDebug(qLcMediaPlayer) << " audio" << caps.toString() << (int)codec;
+ qCDebug(qLcMediaPlayer) << " audio" << caps << (int)codec;
} else if (structure.name().startsWith("video/")) {
auto codec = QGstreamerFormatInfo::videoCodecForCaps(structure);
m_metaData.insert(QMediaMetaData::VideoCodec, QVariant::fromValue(codec));
- qCDebug(qLcMediaPlayer) << " video" << caps.toString() << (int)codec;
+ qCDebug(qLcMediaPlayer) << " video" << caps << (int)codec;
auto framerate = structure["framerate"].getFraction();
if (framerate)
m_metaData.insert(QMediaMetaData::VideoFrameRate, *framerate);
- auto width = structure["width"].toInt();
- auto height = structure["height"].toInt();
- if (width && height)
- m_metaData.insert(QMediaMetaData::Resolution, QSize(*width, *height));
+
+ QSize resolution = structure.resolution();
+ if (resolution.isValid())
+ m_metaData.insert(QMediaMetaData::Resolution, resolution);
+
+ QSize nativeSize = structure.nativeSize();
+ gstVideoOutput->setNativeSize(nativeSize);
}
}
auto sinkPad = trackSelector(VideoStream).activeInputPad();
if (!sinkPad.isNull()) {
- bool hasTags = g_object_class_find_property (G_OBJECT_GET_CLASS (sinkPad.object()), "tags") != NULL;
+ QGstTagListHandle tagList;
- GstTagList *tl = nullptr;
- g_object_get(sinkPad.object(), "tags", &tl, nullptr);
- qCDebug(qLcMediaPlayer) << " tags=" << hasTags << (tl ? gst_tag_list_to_string(tl) : "(null)");
+ g_object_get(sinkPad.object(), "tags", &tagList, nullptr);
+ if (tagList)
+ qCDebug(qLcMediaPlayer) << " tags=" << tagList.get();
+ else
+ qCDebug(qLcMediaPlayer) << " tags=(null)";
}
@@ -841,10 +1060,10 @@ QMediaMetaData QGstreamerMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackT
if (track.isNull())
return {};
- GstTagList *tagList = nullptr;
+ QGstTagListHandle tagList;
g_object_get(track.object(), "tags", &tagList, nullptr);
- return tagList ? QGstreamerMetaData::fromGstTagList(tagList) : QMediaMetaData{};
+ return taglistToMetaData(tagList);
}
int QGstreamerMediaPlayer::activeTrack(TrackType type)
@@ -866,14 +1085,14 @@ void QGstreamerMediaPlayer::setActiveTrack(TrackType type, int index)
if (type == QPlatformMediaPlayer::SubtitleStream)
gstVideoOutput->flushSubtitles();
- playerPipeline.beginConfig();
- if (track.isNull()) {
- removeOutput(ts);
- } else {
- ts.setActiveInputPad(track);
- connectOutput(ts);
- }
- playerPipeline.endConfig();
+ playerPipeline.modifyPipelineWhileNotRunning([&] {
+ if (track.isNull()) {
+ removeOutput(ts);
+ } else {
+ ts.setActiveInputPad(track);
+ connectOutput(ts);
+ }
+ });
// seek to force an immediate change of the stream
if (playerPipeline.state() == GST_STATE_PLAYING)
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h
index cfc576643..306fabaaf 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERMEDIAPLAYER_P_H
#define QGSTREAMERMEDIAPLAYER_P_H
@@ -54,9 +18,10 @@
#include <QtCore/qstack.h>
#include <private/qplatformmediaplayer_p.h>
#include <private/qtmultimediaglobal_p.h>
+#include <private/qmultimediautils_p.h>
#include <qurl.h>
-#include <qgst_p.h>
-#include <qgstpipeline_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
#include <QtCore/qtimer.h>
@@ -66,20 +31,17 @@ QT_BEGIN_NAMESPACE
class QNetworkAccessManager;
class QGstreamerMessage;
-class QGstAppSrc;
+class QGstAppSource;
class QGstreamerAudioOutput;
class QGstreamerVideoOutput;
-class Q_MULTIMEDIA_EXPORT QGstreamerMediaPlayer
- : public QObject,
- public QPlatformMediaPlayer,
- public QGstreamerBusMessageFilter,
- public QGstreamerSyncMessageFilter
+class QGstreamerMediaPlayer : public QObject,
+ public QPlatformMediaPlayer,
+ public QGstreamerBusMessageFilter,
+ public QGstreamerSyncMessageFilter
{
- Q_OBJECT
-
public:
- QGstreamerMediaPlayer(QMediaPlayer *parent = 0);
+ static QMaybe<QPlatformMediaPlayer *> create(QMediaPlayer *parent = nullptr);
~QGstreamerMediaPlayer();
qint64 position() const override;
@@ -94,7 +56,7 @@ public:
QUrl media() const override;
const QIODevice *mediaStream() const override;
- void setMedia(const QUrl&, QIODevice *) override;
+ void setMedia(const QUrl &, QIODevice *) override;
bool streamPlaybackSupported() const override { return true; }
@@ -115,21 +77,30 @@ public:
void pause() override;
void stop() override;
+ void *nativePipeline() override;
+
bool processBusMessage(const QGstreamerMessage& message) override;
bool processSyncMessage(const QGstreamerMessage& message) override;
-public Q_SLOTS:
void updatePosition() { positionChanged(position()); }
private:
- struct TrackSelector {
- TrackSelector(TrackType, const char *name);
+ QGstreamerMediaPlayer(QGstreamerVideoOutput *videoOutput, QGstElement videoInputSelector,
+ QGstElement audioInputSelector, QGstElement subTitleInputSelector,
+ QMediaPlayer *parent);
+
+ struct TrackSelector
+ {
+ TrackSelector(TrackType, QGstElement selector);
QGstPad createInputPad();
void removeInputPad(QGstPad pad);
void removeAllInputPads();
QGstPad inputPad(int index);
int activeInputIndex() const { return isConnected ? tracks.indexOf(activeInputPad()) : -1; }
- QGstPad activeInputPad() const { return isConnected ? selector.getObject("active-pad") : QGstPad{}; }
+ QGstPad activeInputPad() const
+ {
+ return isConnected ? QGstPad{ selector.getObject("active-pad") } : QGstPad{};
+ }
void setActiveInputPad(QGstPad input) { selector.set("active-pad", input); }
int trackCount() const { return tracks.count(); }
@@ -142,30 +113,51 @@ private:
friend class QGstreamerStreamsControl;
void decoderPadAdded(const QGstElement &src, const QGstPad &pad);
void decoderPadRemoved(const QGstElement &src, const QGstPad &pad);
- static void uridecodebinElementAddedCallback(GstElement *uridecodebin, GstElement *child, QGstreamerMediaPlayer *that);
+ void disconnectDecoderHandlers();
+ static void uridecodebinElementAddedCallback(GstElement *uridecodebin, GstElement *child,
+ QGstreamerMediaPlayer *that);
+ static void sourceSetupCallback(GstElement *uridecodebin, GstElement *source,
+ QGstreamerMediaPlayer *that);
+ static void unknownTypeCallback(GstElement *decodebin, GstPad *pad, GstCaps *caps,
+ QGstreamerMediaPlayer *self);
+ static void decodebinElementAddedCallback(GstBin *decodebin, GstBin *sub_bin,
+ GstElement *element, QGstreamerMediaPlayer *self);
+ static void decodebinElementRemovedCallback(GstBin *decodebin, GstBin *sub_bin,
+ GstElement *element, QGstreamerMediaPlayer *self);
+
void parseStreamsAndMetadata();
void connectOutput(TrackSelector &ts);
void removeOutput(TrackSelector &ts);
+ void removeDynamicPipelineElements();
void removeAllOutputs();
void stopOrEOS(bool eos);
+ bool canTrackProgress() const { return decodeBinQueues > 0; }
+ void detectPipelineIsSeekable();
std::array<TrackSelector, NTrackTypes> trackSelectors;
TrackSelector &trackSelector(TrackType type);
QMediaMetaData m_metaData;
- int m_bufferProgress = -1;
+ int m_bufferProgress = 0;
QUrl m_url;
QIODevice *m_stream = nullptr;
+ enum class ResourceErrorState : uint8_t {
+ NoError,
+ ErrorOccurred,
+ ErrorReported,
+ };
+
bool prerolling = false;
bool m_requiresSeekOnPlay = false;
+ bool m_initialBufferProgressSent = false;
+ ResourceErrorState m_resourceErrorState = ResourceErrorState::NoError;
qint64 m_duration = 0;
QTimer positionUpdateTimer;
- QGstAppSrc *m_appSrc = nullptr;
+ QGstAppSource *m_appSrc = nullptr;
- GType decodebinType;
QGstStructure topology;
// Gst elements
@@ -179,6 +171,17 @@ private:
// QGstElement streamSynchronizer;
QHash<QByteArray, QGstPad> decoderOutputMap;
+
+ // decoder connections
+ QGObjectHandlerScopedConnection padAdded;
+ QGObjectHandlerScopedConnection padRemoved;
+ QGObjectHandlerScopedConnection sourceSetup;
+ QGObjectHandlerScopedConnection uridecodebinElementAdded;
+ QGObjectHandlerScopedConnection unknownType;
+ QGObjectHandlerScopedConnection elementAdded;
+ QGObjectHandlerScopedConnection elementRemoved;
+
+ int decodeBinQueues = 0;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermessage.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermessage.cpp
deleted file mode 100644
index 02a5dd3bc..000000000
--- a/src/plugins/multimedia/gstreamer/common/qgstreamermessage.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <gst/gst.h>
-
-#include "qgstreamermessage_p.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QGstreamerMessage
- \internal
-*/
-
-QGstreamerMessage::QGstreamerMessage(GstMessage* message):
- m_message(message)
-{
- gst_message_ref(m_message);
-}
-
-QGstreamerMessage::QGstreamerMessage(QGstreamerMessage const& m):
- m_message(m.m_message)
-{
- gst_message_ref(m_message);
-}
-
-QGstreamerMessage::QGstreamerMessage(const QGstStructure &structure)
-{
- gst_structure_get(structure.structure, "message", GST_TYPE_MESSAGE, &m_message, nullptr);
-}
-
-QGstreamerMessage::~QGstreamerMessage()
-{
- if (m_message != nullptr)
- gst_message_unref(m_message);
-}
-
-GstMessage* QGstreamerMessage::rawMessage() const
-{
- return m_message;
-}
-
-QGstreamerMessage& QGstreamerMessage::operator=(QGstreamerMessage const& rhs)
-{
- if (rhs.m_message != m_message) {
- if (rhs.m_message != nullptr)
- gst_message_ref(rhs.m_message);
-
- if (m_message != nullptr)
- gst_message_unref(m_message);
-
- m_message = rhs.m_message;
- }
-
- return *this;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h
index 483f8f5e3..01fe68acb 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermessage_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERMESSAGE_P_H
#define QGSTREAMERMESSAGE_P_H
@@ -52,34 +16,36 @@
//
#include <private/qtmultimediaglobal_p.h>
-#include <qgst_p.h>
+#include <common/qgst_p.h>
QT_BEGIN_NAMESPACE
// Required for QDoc workaround
class QString;
-class Q_MULTIMEDIA_EXPORT QGstreamerMessage
+template <>
+struct QGstPointerImpl::QGstRefcountingAdaptor<GstMessage>
{
-public:
- QGstreamerMessage() = default;
- QGstreamerMessage(QGstreamerMessage const& m);
- explicit QGstreamerMessage(GstMessage* message);
- explicit QGstreamerMessage(const QGstStructure &structure);
-
- ~QGstreamerMessage();
+ static void ref(GstMessage *arg) noexcept { gst_message_ref(arg); }
+ static void unref(GstMessage *arg) noexcept { gst_message_unref(arg); }
+};
- bool isNull() const { return !m_message; }
- GstMessageType type() const { return GST_MESSAGE_TYPE(m_message); }
- QGstObject source() const { return QGstObject(GST_MESSAGE_SRC(m_message), QGstObject::NeedsRef); }
- QGstStructure structure() const { return QGstStructure(gst_message_get_structure(m_message)); }
+class QGstreamerMessage : public QGstPointerImpl::QGstObjectWrapper<GstMessage>
+{
+ using BaseClass = QGstPointerImpl::QGstObjectWrapper<GstMessage>;
- GstMessage* rawMessage() const;
+public:
+ using BaseClass::BaseClass;
+ QGstreamerMessage(const QGstreamerMessage &) = default;
+ QGstreamerMessage(QGstreamerMessage &&) noexcept = default;
+ QGstreamerMessage &operator=(const QGstreamerMessage &) = default;
+ QGstreamerMessage &operator=(QGstreamerMessage &&) noexcept = default;
- QGstreamerMessage& operator=(QGstreamerMessage const& rhs);
+ GstMessageType type() const { return GST_MESSAGE_TYPE(get()); }
+ QGstObject source() const { return QGstObject(GST_MESSAGE_SRC(get()), QGstObject::NeedsRef); }
+ QGstStructure structure() const { return QGstStructure(gst_message_get_structure(get())); }
-private:
- GstMessage* m_message = nullptr;
+ GstMessage *message() const { return get(); }
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp
index 55f5ef155..fab4750f9 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp
@@ -1,308 +1,448 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgstreamermetadata_p.h"
-#include <QDebug>
#include <QtMultimedia/qmediametadata.h>
+#include <QtMultimedia/qtvideo.h>
+#include <QtCore/qdebug.h>
#include <QtCore/qdatetime.h>
+#include <QtCore/qlocale.h>
+#include <QtCore/qtimezone.h>
+#include <QtGui/qimage.h>
#include <gst/gstversion.h>
-#include <qgstutils_p.h>
-#include <qlocale.h>
+#include <common/qgst_handle_types_p.h>
+#include <common/qgstutils_p.h>
QT_BEGIN_NAMESPACE
-struct {
+namespace {
+
+namespace MetadataLookupImpl {
+
+#ifdef __cpp_lib_constexpr_algorithms
+# define constexpr_lookup constexpr
+#else
+# define constexpr_lookup /*constexpr*/
+#endif
+
+struct MetadataKeyValuePair
+{
const char *tag;
QMediaMetaData::Key key;
-} gstTagToMetaDataKey[] = {
- { GST_TAG_TITLE, QMediaMetaData::Title },
- { GST_TAG_COMMENT, QMediaMetaData::Comment },
- { GST_TAG_DESCRIPTION, QMediaMetaData::Description },
- { GST_TAG_GENRE, QMediaMetaData::Genre },
- { GST_TAG_DATE_TIME, QMediaMetaData::Date },
- { GST_TAG_DATE, QMediaMetaData::Date },
+};
- { GST_TAG_LANGUAGE_CODE, QMediaMetaData::Language },
+constexpr const char *toTag(const char *t)
+{
+ return t;
+}
+constexpr const char *toTag(const MetadataKeyValuePair &kv)
+{
+ return kv.tag;
+}
- { GST_TAG_ORGANIZATION, QMediaMetaData::Publisher },
- { GST_TAG_COPYRIGHT, QMediaMetaData::Copyright },
+constexpr QMediaMetaData::Key toKey(QMediaMetaData::Key k)
+{
+ return k;
+}
+constexpr QMediaMetaData::Key toKey(const MetadataKeyValuePair &kv)
+{
+ return kv.key;
+}
- // Media
- { GST_TAG_DURATION, QMediaMetaData::Duration },
+constexpr auto compareByKey = [](const auto &lhs, const auto &rhs) {
+ return toKey(lhs) < toKey(rhs);
+};
- // Audio
- { GST_TAG_BITRATE, QMediaMetaData::AudioBitRate },
- { GST_TAG_AUDIO_CODEC, QMediaMetaData::AudioCodec },
+constexpr auto compareByTag = [](const auto &lhs, const auto &rhs) {
+ return std::strcmp(toTag(lhs), toTag(rhs)) < 0;
+};
- // Music
- { GST_TAG_ALBUM, QMediaMetaData::AlbumTitle },
- { GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist },
- { GST_TAG_ARTIST, QMediaMetaData::ContributingArtist },
- { GST_TAG_TRACK_NUMBER, QMediaMetaData::TrackNumber },
+constexpr_lookup auto makeLookupTable()
+{
+ std::array<MetadataKeyValuePair, 22> lookupTable{ {
+ { GST_TAG_TITLE, QMediaMetaData::Title },
+ { GST_TAG_COMMENT, QMediaMetaData::Comment },
+ { GST_TAG_DESCRIPTION, QMediaMetaData::Description },
+ { GST_TAG_GENRE, QMediaMetaData::Genre },
+ { GST_TAG_DATE_TIME, QMediaMetaData::Date },
+ { GST_TAG_DATE, QMediaMetaData::Date },
+
+ { GST_TAG_LANGUAGE_CODE, QMediaMetaData::Language },
+
+ { GST_TAG_ORGANIZATION, QMediaMetaData::Publisher },
+ { GST_TAG_COPYRIGHT, QMediaMetaData::Copyright },
+
+ // Media
+ { GST_TAG_DURATION, QMediaMetaData::Duration },
+
+ // Audio
+ { GST_TAG_BITRATE, QMediaMetaData::AudioBitRate },
+ { GST_TAG_AUDIO_CODEC, QMediaMetaData::AudioCodec },
+
+ // Music
+ { GST_TAG_ALBUM, QMediaMetaData::AlbumTitle },
+ { GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist },
+ { GST_TAG_ARTIST, QMediaMetaData::ContributingArtist },
+ { GST_TAG_TRACK_NUMBER, QMediaMetaData::TrackNumber },
+
+ { GST_TAG_PREVIEW_IMAGE, QMediaMetaData::ThumbnailImage },
+ { GST_TAG_IMAGE, QMediaMetaData::CoverArtImage },
+
+ // Image/Video
+ { "resolution", QMediaMetaData::Resolution },
+ { GST_TAG_IMAGE_ORIENTATION, QMediaMetaData::Orientation },
+
+ // Video
+ { GST_TAG_VIDEO_CODEC, QMediaMetaData::VideoCodec },
+
+ // Movie
+ { GST_TAG_PERFORMER, QMediaMetaData::LeadPerformer },
+ } };
+
+ std::sort(lookupTable.begin(), lookupTable.end(),
+ [](const MetadataKeyValuePair &lhs, const MetadataKeyValuePair &rhs) {
+ return std::string_view(lhs.tag) < std::string_view(rhs.tag);
+ });
+ return lookupTable;
+}
- { GST_TAG_PREVIEW_IMAGE, QMediaMetaData::ThumbnailImage },
- { GST_TAG_IMAGE, QMediaMetaData::CoverArtImage },
+constexpr_lookup auto gstTagToMetaDataKey = makeLookupTable();
+constexpr_lookup auto metaDataKeyToGstTag = [] {
+ auto array = gstTagToMetaDataKey;
+ std::sort(array.begin(), array.end(), compareByKey);
+ return array;
+}();
- // Image/Video
- { "resolution", QMediaMetaData::Resolution },
- { GST_TAG_IMAGE_ORIENTATION, QMediaMetaData::Orientation },
+} // namespace MetadataLookupImpl
- // Video
- { GST_TAG_VIDEO_CODEC, QMediaMetaData::VideoCodec },
+QMediaMetaData::Key tagToKey(const char *tag)
+{
+ if (tag == nullptr)
+ return QMediaMetaData::Key(-1);
- // Movie
- { GST_TAG_PERFORMER, QMediaMetaData::LeadPerformer },
+ using namespace MetadataLookupImpl;
+ auto foundIterator = std::lower_bound(gstTagToMetaDataKey.begin(), gstTagToMetaDataKey.end(),
+ tag, compareByTag);
+ if (std::strcmp(foundIterator->tag, tag) == 0)
+ return foundIterator->key;
- { nullptr, QMediaMetaData::Title }
-};
+ return QMediaMetaData::Key(-1);
+}
+
+const char *keyToTag(QMediaMetaData::Key key)
+{
+ using namespace MetadataLookupImpl;
+ auto foundIterator = std::lower_bound(metaDataKeyToGstTag.begin(), metaDataKeyToGstTag.end(),
+ key, compareByKey);
+ if (foundIterator->key == key)
+ return foundIterator->tag;
+
+ return nullptr;
+}
+
+#undef constexpr_lookup
-static QMediaMetaData::Key tagToKey(const char *tag)
+QtVideo::Rotation parseRotationTag(const char *string)
{
- auto *map = gstTagToMetaDataKey;
- while (map->tag) {
- if (!strcmp(map->tag, tag))
- return map->key;
- ++map;
+ using namespace std::string_view_literals;
+
+ if (string == "rotate-90"sv)
+ return QtVideo::Rotation::Clockwise90;
+ if (string == "rotate-180"sv)
+ return QtVideo::Rotation::Clockwise180;
+ if (string == "rotate-270"sv)
+ return QtVideo::Rotation::Clockwise270;
+ if (string == "rotate-0"sv)
+ return QtVideo::Rotation::None;
+
+ qCritical() << "cannot parse orientation: {}" << string;
+ return QtVideo::Rotation::None;
+}
+
+QDateTime parseDate(const GValue &val)
+{
+ Q_ASSERT(G_VALUE_TYPE(&val) == G_TYPE_DATE);
+
+ const GDate *date = (const GDate *)g_value_get_boxed(&val);
+ if (!g_date_valid(date))
+ return {};
+
+ int year = g_date_get_year(date);
+ int month = g_date_get_month(date);
+ int day = g_date_get_day(date);
+ return QDateTime(QDate(year, month, day), QTime());
+}
+
+QDateTime parseDateTime(const GValue &val)
+{
+ Q_ASSERT(G_VALUE_TYPE(&val) == GST_TYPE_DATE_TIME);
+
+ const GstDateTime *dateTime = (const GstDateTime *)g_value_get_boxed(&val);
+ int year = gst_date_time_has_year(dateTime) ? gst_date_time_get_year(dateTime) : 0;
+ int month = gst_date_time_has_month(dateTime) ? gst_date_time_get_month(dateTime) : 0;
+ int day = gst_date_time_has_day(dateTime) ? gst_date_time_get_day(dateTime) : 0;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ float tz = 0;
+ if (gst_date_time_has_time(dateTime)) {
+ hour = gst_date_time_get_hour(dateTime);
+ minute = gst_date_time_get_minute(dateTime);
+ second = gst_date_time_get_second(dateTime);
+ tz = gst_date_time_get_time_zone_offset(dateTime);
}
- return QMediaMetaData::Key(-1);
+ return QDateTime{
+ QDate(year, month, day),
+ QTime(hour, minute, second),
+ QTimeZone(tz * 60 * 60),
+ };
}
-static const char *keyToTag(QMediaMetaData::Key key)
+QImage parseImage(const GValue &val)
{
- auto *map = gstTagToMetaDataKey;
- while (map->tag) {
- if (map->key == key)
- return map->tag;
- ++map;
+ Q_ASSERT(G_VALUE_TYPE(&val) == GST_TYPE_SAMPLE);
+
+ GstSample *sample = (GstSample *)g_value_get_boxed(&val);
+ GstCaps *caps = gst_sample_get_caps(sample);
+ if (caps && !gst_caps_is_empty(caps)) {
+ GstStructure *structure = gst_caps_get_structure(caps, 0);
+ const gchar *name = gst_structure_get_name(structure);
+ if (QByteArray(name).startsWith("image/")) {
+ GstBuffer *buffer = gst_sample_get_buffer(sample);
+ if (buffer) {
+ GstMapInfo info;
+ gst_buffer_map(buffer, &info, GST_MAP_READ);
+ QImage image = QImage::fromData(info.data, info.size, name);
+ gst_buffer_unmap(buffer, &info);
+ return image;
+ }
+ }
}
- return nullptr;
+
+ return {};
+}
+
+std::optional<double> parseFractionAsDouble(const GValue &val)
+{
+ Q_ASSERT(G_VALUE_TYPE(&val) == GST_TYPE_FRACTION);
+
+ int nom = gst_value_get_fraction_numerator(&val);
+ int denom = gst_value_get_fraction_denominator(&val);
+ if (denom == 0)
+ return std::nullopt;
+ return double(nom) / double(denom);
}
-//internal
-static void addTagToMap(const GstTagList *list,
- const gchar *tag,
- gpointer user_data)
+constexpr std::string_view extendedComment{ GST_TAG_EXTENDED_COMMENT };
+
+void addTagsFromExtendedComment(const GstTagList *list, const gchar *tag, QMediaMetaData &metadata)
{
+ using namespace Qt::Literals;
+ assert(tag == extendedComment);
+
+ int entryCount = gst_tag_list_get_tag_size(list, tag);
+ for (int i = 0; i != entryCount; ++i) {
+ const GValue *value = gst_tag_list_get_value_index(list, tag, i);
+
+ const QLatin1StringView strValue{ g_value_get_string(value) };
+
+ auto equalIndex = strValue.indexOf(QLatin1StringView("="));
+ if (equalIndex == -1) {
+ qDebug() << "Cannot parse GST_TAG_EXTENDED_COMMENT entry: " << value;
+ continue;
+ }
+
+ const QLatin1StringView key = strValue.first(equalIndex);
+ const QLatin1StringView valueString = strValue.last(strValue.size() - equalIndex - 1);
+
+ if (key == "DURATION"_L1) {
+ QUniqueGstDateTimeHandle duration{
+ gst_date_time_new_from_iso8601_string(valueString.data()),
+ };
+
+ if (duration) {
+ using namespace std::chrono;
+
+ auto chronoDuration = hours(gst_date_time_get_hour(duration.get()))
+ + minutes(gst_date_time_get_minute(duration.get()))
+ + seconds(gst_date_time_get_second(duration.get()))
+ + microseconds(gst_date_time_get_microsecond(duration.get()));
+
+ metadata.insert(QMediaMetaData::Duration,
+ QVariant::fromValue(round<milliseconds>(chronoDuration).count()));
+ }
+ }
+ }
+}
+
+void addTagToMetaData(const GstTagList *list, const gchar *tag, void *userdata)
+{
+ QMediaMetaData &metadata = *reinterpret_cast<QMediaMetaData *>(userdata);
+
QMediaMetaData::Key key = tagToKey(tag);
- if (key == QMediaMetaData::Key(-1))
- return;
+ if (key == QMediaMetaData::Key(-1)) {
+ if (tag == extendedComment)
+ addTagsFromExtendedComment(list, tag, metadata);
- auto *map = reinterpret_cast<QHash<QMediaMetaData::Key, QVariant>* >(user_data);
+ return;
+ }
- GValue val;
- val.g_type = 0;
+ GValue val{};
gst_tag_list_copy_value(&val, list, tag);
+ GType type = G_VALUE_TYPE(&val);
- switch( G_VALUE_TYPE(&val) ) {
- case G_TYPE_STRING:
- {
- const gchar *str_value = g_value_get_string(&val);
- if (key == QMediaMetaData::Language) {
- map->insert(key, QVariant::fromValue(QLocale::codeToLanguage(QString::fromUtf8(str_value), QLocale::ISO639Part2)));
- break;
- }
- map->insert(key, QString::fromUtf8(str_value));
+ if (auto entryCount = gst_tag_list_get_tag_size(list, tag) != 0; entryCount != 1)
+ qWarning() << "addTagToMetaData: invaled entry count for" << tag << "-" << entryCount;
+
+ if (type == G_TYPE_STRING) {
+ const gchar *str_value = g_value_get_string(&val);
+
+ switch (key) {
+ case QMediaMetaData::Language: {
+ metadata.insert(key,
+ QVariant::fromValue(QLocale::codeToLanguage(
+ QString::fromUtf8(str_value), QLocale::ISO639Part2)));
break;
}
- case G_TYPE_INT:
- map->insert(key, g_value_get_int(&val));
- break;
- case G_TYPE_UINT:
- map->insert(key, g_value_get_uint(&val));
- break;
- case G_TYPE_LONG:
- map->insert(key, qint64(g_value_get_long(&val)));
- break;
- case G_TYPE_BOOLEAN:
- map->insert(key, g_value_get_boolean(&val));
- break;
- case G_TYPE_CHAR:
- map->insert(key, g_value_get_schar(&val));
- break;
- case G_TYPE_DOUBLE:
- map->insert(key, g_value_get_double(&val));
+ case QMediaMetaData::Orientation: {
+ metadata.insert(key, QVariant::fromValue(parseRotationTag(str_value)));
break;
+ }
default:
- // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch
- if (G_VALUE_TYPE(&val) == G_TYPE_DATE) {
- const GDate *date = (const GDate *)g_value_get_boxed(&val);
- if (g_date_valid(date)) {
- int year = g_date_get_year(date);
- int month = g_date_get_month(date);
- int day = g_date_get_day(date);
- // don't insert if we already have a datetime.
- if (!map->contains(key))
- map->insert(key, QDateTime(QDate(year, month, day), QTime()));
- }
- } else if (G_VALUE_TYPE(&val) == GST_TYPE_DATE_TIME) {
- const GstDateTime *dateTime = (const GstDateTime *)g_value_get_boxed(&val);
- int year = gst_date_time_has_year(dateTime) ? gst_date_time_get_year(dateTime) : 0;
- int month = gst_date_time_has_month(dateTime) ? gst_date_time_get_month(dateTime) : 0;
- int day = gst_date_time_has_day(dateTime) ? gst_date_time_get_day(dateTime) : 0;
- int hour = 0;
- int minute = 0;
- int second = 0;
- float tz = 0;
- if (gst_date_time_has_time(dateTime)) {
- hour = gst_date_time_get_hour(dateTime);
- minute = gst_date_time_get_minute(dateTime);
- second = gst_date_time_get_second(dateTime);
- tz = gst_date_time_get_time_zone_offset(dateTime);
- }
- QDateTime qDateTime(QDate(year, month, day), QTime(hour, minute, second),
- Qt::OffsetFromUTC, tz * 60 * 60);
- map->insert(key, qDateTime);
- } else if (G_VALUE_TYPE(&val) == GST_TYPE_SAMPLE) {
- GstSample *sample = (GstSample *)g_value_get_boxed(&val);
- GstCaps* caps = gst_sample_get_caps(sample);
- if (caps && !gst_caps_is_empty(caps)) {
- GstStructure *structure = gst_caps_get_structure(caps, 0);
- const gchar *name = gst_structure_get_name(structure);
- if (QByteArray(name).startsWith("image/")) {
- GstBuffer *buffer = gst_sample_get_buffer(sample);
- if (buffer) {
- GstMapInfo info;
- gst_buffer_map(buffer, &info, GST_MAP_READ);
- map->insert(key, QImage::fromData(info.data, info.size, name));
- gst_buffer_unmap(buffer, &info);
- }
- }
- }
- } else if (G_VALUE_TYPE(&val) == GST_TYPE_FRACTION) {
- int nom = gst_value_get_fraction_numerator(&val);
- int denom = gst_value_get_fraction_denominator(&val);
-
- if (denom > 0) {
- map->insert(key, double(nom)/denom);
- }
- }
+ metadata.insert(key, QString::fromUtf8(str_value));
break;
+ };
+ } else if (type == G_TYPE_INT) {
+ metadata.insert(key, g_value_get_int(&val));
+ } else if (type == G_TYPE_UINT) {
+ metadata.insert(key, g_value_get_uint(&val));
+ } else if (type == G_TYPE_LONG) {
+ metadata.insert(key, qint64(g_value_get_long(&val)));
+ } else if (type == G_TYPE_BOOLEAN) {
+ metadata.insert(key, g_value_get_boolean(&val));
+ } else if (type == G_TYPE_CHAR) {
+ metadata.insert(key, g_value_get_schar(&val));
+ } else if (type == G_TYPE_DOUBLE) {
+ metadata.insert(key, g_value_get_double(&val));
+ } else if (type == G_TYPE_DATE) {
+ if (!metadata.keys().contains(key)) {
+ QDateTime date = parseDate(val);
+ if (date.isValid())
+ metadata.insert(key, date);
+ }
+ } else if (type == GST_TYPE_DATE_TIME) {
+ metadata.insert(key, parseDateTime(val));
+ } else if (type == GST_TYPE_SAMPLE) {
+ QImage image = parseImage(val);
+ if (!image.isNull())
+ metadata.insert(key, image);
+ } else if (type == GST_TYPE_FRACTION) {
+ std::optional<double> fraction = parseFractionAsDouble(val);
+
+ if (fraction)
+ metadata.insert(key, *fraction);
}
g_value_unset(&val);
}
+} // namespace
-QGstreamerMetaData QGstreamerMetaData::fromGstTagList(const GstTagList *tags)
+QMediaMetaData taglistToMetaData(const GstTagList *tagList)
{
- QGstreamerMetaData m;
- gst_tag_list_foreach(tags, addTagToMap, &m.data);
+ QMediaMetaData m;
+ if (tagList)
+ gst_tag_list_foreach(tagList, reinterpret_cast<GstTagForeachFunc>(&addTagToMetaData), &m);
return m;
}
-
-void QGstreamerMetaData::setMetaData(GstElement *element) const
+QMediaMetaData taglistToMetaData(const QGstTagListHandle &handle)
{
- if (!GST_IS_TAG_SETTER(element))
- return;
+ return taglistToMetaData(handle.get());
+}
- gst_tag_setter_reset_tags(GST_TAG_SETTER(element));
+static void applyMetaDataToTagSetter(const QMediaMetaData &metadata, GstTagSetter *element)
+{
+ gst_tag_setter_reset_tags(element);
- for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) {
- const char *tagName = keyToTag(it.key());
+ for (QMediaMetaData::Key key : metadata.keys()) {
+ const char *tagName = keyToTag(key);
if (!tagName)
continue;
- const QVariant &tagValue = it.value();
+ const QVariant &tagValue = metadata.value(key);
+
+ auto setTag = [&](const auto &value) {
+ gst_tag_setter_add_tags(element, GST_TAG_MERGE_REPLACE, tagName, value, nullptr);
+ };
switch (tagValue.typeId()) {
- case QMetaType::QString:
- gst_tag_setter_add_tags(GST_TAG_SETTER(element),
- GST_TAG_MERGE_REPLACE,
- tagName,
- tagValue.toString().toUtf8().constData(),
- nullptr);
- break;
- case QMetaType::Int:
- case QMetaType::LongLong:
- gst_tag_setter_add_tags(GST_TAG_SETTER(element),
- GST_TAG_MERGE_REPLACE,
- tagName,
- tagValue.toInt(),
- nullptr);
- break;
- case QMetaType::Double:
- gst_tag_setter_add_tags(GST_TAG_SETTER(element),
- GST_TAG_MERGE_REPLACE,
- tagName,
- tagValue.toDouble(),
- nullptr);
- break;
- case QMetaType::QDate:
- case QMetaType::QDateTime: {
- QDateTime date = tagValue.toDateTime();
- gst_tag_setter_add_tags(GST_TAG_SETTER(element),
- GST_TAG_MERGE_REPLACE,
- tagName,
- gst_date_time_new(date.offsetFromUtc() / 60. / 60.,
- date.date().year(), date.date().month(), date.date().day(),
- date.time().hour(), date.time().minute(), date.time().second()),
- nullptr);
- break;
- }
- default: {
- if (tagValue.typeId() == qMetaTypeId<QLocale::Language>()) {
- QByteArray language = QLocale::languageToCode(tagValue.value<QLocale::Language>(), QLocale::ISO639Part2).toUtf8();
- gst_tag_setter_add_tags(GST_TAG_SETTER(element),
- GST_TAG_MERGE_REPLACE,
- tagName,
- language.constData(),
- nullptr);
- }
-
- break;
+ case QMetaType::QString:
+ setTag(tagValue.toString().toUtf8().constData());
+ break;
+ case QMetaType::Int:
+ case QMetaType::LongLong:
+ setTag(tagValue.toInt());
+ break;
+ case QMetaType::Double:
+ setTag(tagValue.toDouble());
+ break;
+ case QMetaType::QDate:
+ case QMetaType::QDateTime: {
+ QDateTime date = tagValue.toDateTime();
+
+ QGstGstDateTimeHandle dateTime{
+ gst_date_time_new(date.offsetFromUtc() / 60. / 60., date.date().year(),
+ date.date().month(), date.date().day(), date.time().hour(),
+ date.time().minute(), date.time().second()),
+ QGstGstDateTimeHandle::HasRef,
+ };
+
+ setTag(dateTime.get());
+ break;
+ }
+ default: {
+ if (tagValue.typeId() == qMetaTypeId<QLocale::Language>()) {
+ QByteArray language = QLocale::languageToCode(tagValue.value<QLocale::Language>(),
+ QLocale::ISO639Part2)
+ .toUtf8();
+ setTag(language.constData());
}
+
+ break;
+ }
}
}
}
-void QGstreamerMetaData::setMetaData(GstBin *bin) const
+void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstElement &element)
+{
+ GstTagSetter *tagSetter = qGstSafeCast<GstTagSetter>(element.element());
+ if (tagSetter)
+ applyMetaDataToTagSetter(metadata, tagSetter);
+ else
+ qWarning() << "applyMetaDataToTagSetter failed: element not a GstTagSetter"
+ << element.name();
+}
+
+void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstBin &bin)
{
- GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER);
- GValue item = G_VALUE_INIT;
+ GstIterator *elements = gst_bin_iterate_all_by_interface(bin.bin(), GST_TYPE_TAG_SETTER);
+ GValue item = {};
+
while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) {
- GstElement * const element = GST_ELEMENT(g_value_get_object(&item));
- setMetaData(element);
+ GstElement *element = static_cast<GstElement *>(g_value_get_object(&item));
+ if (!element)
+ continue;
+
+ GstTagSetter *tagSetter = qGstSafeCast<GstTagSetter>(element);
+
+ if (tagSetter)
+ applyMetaDataToTagSetter(metadata, tagSetter);
}
+
gst_iterator_free(elements);
}
-
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h
index 53b29f548..d0d7620db 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERMETADATA_H
#define QGSTREAMERMETADATA_H
@@ -52,21 +16,16 @@
//
#include <qmediametadata.h>
-#include <qvariant.h>
-#include <gst/gst.h>
+#include "qgst_p.h"
QT_BEGIN_NAMESPACE
-class QGstreamerMetaData : public QMediaMetaData
-{
-public:
- static QGstreamerMetaData fromGstTagList(const GstTagList *tags);
- GstTagList *toGstTagList() const;
+QMediaMetaData taglistToMetaData(const GstTagList *);
+QMediaMetaData taglistToMetaData(const QGstTagListHandle &);
- void setMetaData(GstBin *bin) const;
- void setMetaData(GstElement *element) const;
-};
+void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstBin &);
+void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstElement &);
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp
index d064f593e..6bc65693a 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp
@@ -1,65 +1,68 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qgstreamervideooutput_p.h>
-#include <qgstreamervideosink_p.h>
-#include <qgstsubtitlesink_p.h>
-#include <qvideosink.h>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtMultimedia/qvideosink.h>
#include <QtCore/qloggingcategory.h>
-#include <qthread.h>
+#include <QtCore/qthread.h>
+
+#include <common/qgstreamervideooutput_p.h>
+#include <common/qgstreamervideosink_p.h>
+#include <common/qgstsubtitlesink_p.h>
-Q_LOGGING_CATEGORY(qLcMediaVideoOutput, "qt.multimedia.videooutput")
+static Q_LOGGING_CATEGORY(qLcMediaVideoOutput, "qt.multimedia.videooutput")
QT_BEGIN_NAMESPACE
-QGstreamerVideoOutput::QGstreamerVideoOutput(QObject *parent)
+QMaybe<QGstreamerVideoOutput *> QGstreamerVideoOutput::create(QObject *parent)
+{
+ QGstElement videoConvert;
+ QGstElement videoScale;
+
+ QGstElementFactoryHandle factory = QGstElementFactoryHandle{
+ gst_element_factory_find("videoconvertscale"),
+ };
+
+ if (factory) { // videoconvertscale is only available in gstreamer 1.20
+ videoConvert = QGstElement::createFromFactory(factory, "videoConvertScale");
+ } else {
+ videoConvert = QGstElement::createFromFactory("videoconvert", "videoConvert");
+ if (!videoConvert)
+ return errorMessageCannotFindElement("videoconvert");
+
+ videoScale = QGstElement::createFromFactory("videoscale", "videoScale");
+ if (!videoScale)
+ return errorMessageCannotFindElement("videoscale");
+ }
+
+ QGstElement videoSink = QGstElement::createFromFactory("fakesink", "fakeVideoSink");
+ if (!videoSink)
+ return errorMessageCannotFindElement("fakesink");
+ videoSink.set("sync", true);
+
+ return new QGstreamerVideoOutput(videoConvert, videoScale, videoSink, parent);
+}
+
+QGstreamerVideoOutput::QGstreamerVideoOutput(QGstElement convert, QGstElement scale,
+ QGstElement sink, QObject *parent)
: QObject(parent),
- gstVideoOutput("videoOutput")
+ gstVideoOutput(QGstBin::create("videoOutput")),
+ videoConvert(std::move(convert)),
+ videoScale(std::move(scale)),
+ videoSink(std::move(sink))
{
- videoQueue = QGstElement("queue", "videoQueue");
- videoConvert = QGstElement("videoconvert", "videoConvert");
- videoSink = QGstElement("fakesink", "fakeVideoSink");
+ videoQueue = QGstElement::createFromFactory("queue", "videoQueue");
+
videoSink.set("sync", true);
- gstVideoOutput.add(videoQueue, videoConvert, videoSink);
- if (!videoQueue.link(videoConvert, videoSink))
- qCDebug(qLcMediaVideoOutput) << ">>>>>> linking failed";
+ videoSink.set("async", false); // no asynchronous state changes
+
+ if (videoScale) {
+ gstVideoOutput.add(videoQueue, videoConvert, videoScale, videoSink);
+ qLinkGstElements(videoQueue, videoConvert, videoScale, videoSink);
+ } else {
+ gstVideoOutput.add(videoQueue, videoConvert, videoSink);
+ qLinkGstElements(videoQueue, videoConvert, videoSink);
+ }
gstVideoOutput.addGhostPad(videoQueue, "sink");
}
@@ -79,38 +82,42 @@ void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink)
m_videoSink->setPipeline({});
m_videoSink = gstVideoSink;
- if (m_videoSink)
+ if (m_videoSink) {
m_videoSink->setPipeline(gstPipeline);
-
+ if (nativeSize.isValid())
+ m_videoSink->setNativeSize(nativeSize);
+ }
QGstElement gstSink;
if (m_videoSink) {
gstSink = m_videoSink->gstSink();
- isFakeSink = false;
} else {
- gstSink = QGstElement("fakesink", "fakevideosink");
+ gstSink = QGstElement::createFromFactory("fakesink", "fakevideosink");
+ Q_ASSERT(gstSink);
gstSink.set("sync", true);
- isFakeSink = true;
+ gstSink.set("async", false); // no asynchronous state changes
}
if (videoSink == gstSink)
return;
- gstPipeline.beginConfig();
- if (!videoSink.isNull()) {
- gstVideoOutput.remove(videoSink);
- videoSink.setStateSync(GST_STATE_NULL);
- }
- videoSink = gstSink;
- gstVideoOutput.add(videoSink);
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (!videoSink.isNull())
+ gstVideoOutput.stopAndRemoveElements(videoSink);
+
+ videoSink = gstSink;
+ gstVideoOutput.add(videoSink);
- videoConvert.link(videoSink);
- GstEvent *event = gst_event_new_reconfigure();
- gst_element_send_event(videoSink.element(), event);
- videoSink.syncStateWithParent();
+ if (videoScale)
+ qLinkGstElements(videoScale, videoSink);
+ else
+ qLinkGstElements(videoConvert, videoSink);
- doLinkSubtitleStream();
+ GstEvent *event = gst_event_new_reconfigure();
+ gst_element_send_event(videoSink.element(), event);
+ videoSink.syncStateWithParent();
- gstPipeline.endConfig();
+ doLinkSubtitleStream();
+ });
qCDebug(qLcMediaVideoOutput) << "sinkChanged" << gstSink.name();
@@ -134,10 +141,10 @@ void QGstreamerVideoOutput::linkSubtitleStream(QGstElement src)
if (src == subtitleSrc)
return;
- gstPipeline.beginConfig();
- subtitleSrc = src;
- doLinkSubtitleStream();
- gstPipeline.endConfig();
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ subtitleSrc = src;
+ doLinkSubtitleStream();
+ });
}
void QGstreamerVideoOutput::unlinkSubtitleStream()
@@ -147,10 +154,10 @@ void QGstreamerVideoOutput::unlinkSubtitleStream()
qCDebug(qLcMediaVideoOutput) << "unlink subtitle stream";
subtitleSrc = {};
if (!subtitleSink.isNull()) {
- gstPipeline.beginConfig();
- gstPipeline.remove(subtitleSink);
- gstPipeline.endConfig();
- subtitleSink.setStateSync(GST_STATE_NULL);
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ gstPipeline.stopAndRemoveElements(subtitleSink);
+ return;
+ });
subtitleSink = {};
}
if (m_videoSink)
@@ -160,8 +167,7 @@ void QGstreamerVideoOutput::unlinkSubtitleStream()
void QGstreamerVideoOutput::doLinkSubtitleStream()
{
if (!subtitleSink.isNull()) {
- gstPipeline.remove(subtitleSink);
- subtitleSink.setStateSync(GST_STATE_NULL);
+ gstPipeline.stopAndRemoveElements(subtitleSink);
subtitleSink = {};
}
if (!m_videoSink || subtitleSrc.isNull())
@@ -170,8 +176,15 @@ void QGstreamerVideoOutput::doLinkSubtitleStream()
subtitleSink = m_videoSink->subtitleSink();
gstPipeline.add(subtitleSink);
}
- if (!subtitleSrc.link(subtitleSink))
- qCDebug(qLcMediaVideoOutput) << "link subtitle stream failed";
+ qLinkGstElements(subtitleSrc, subtitleSink);
+}
+
+void QGstreamerVideoOutput::updateNativeSize()
+{
+ if (!m_videoSink)
+ return;
+
+ m_videoSink->setNativeSize(qRotatedFrameSize(nativeSize, rotation));
}
void QGstreamerVideoOutput::setIsPreview()
@@ -196,4 +209,18 @@ void QGstreamerVideoOutput::flushSubtitles()
}
}
+void QGstreamerVideoOutput::setNativeSize(QSize sz)
+{
+ nativeSize = sz;
+ updateNativeSize();
+}
+
+void QGstreamerVideoOutput::setRotation(QtVideo::Rotation rot)
+{
+ rotation = rot;
+ updateNativeSize();
+}
+
QT_END_NAMESPACE
+
+#include "moc_qgstreamervideooutput_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h
index bf6bc5497..42acb18cc 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERVIDEOOUTPUT_P_H
#define QGSTREAMERVIDEOOUTPUT_P_H
@@ -53,23 +17,24 @@
#include <QtCore/qobject.h>
#include <private/qtmultimediaglobal_p.h>
-#include <qgst_p.h>
-#include <qgstpipeline_p.h>
+#include <private/qmultimediautils_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreamervideosink_p.h>
#include <qwaitcondition.h>
#include <qmutex.h>
#include <qpointer.h>
-#include <qgstreamervideosink_p.h>
QT_BEGIN_NAMESPACE
class QVideoSink;
-class Q_MULTIMEDIA_EXPORT QGstreamerVideoOutput : public QObject
+class QGstreamerVideoOutput : public QObject
{
Q_OBJECT
public:
- QGstreamerVideoOutput(QObject *parent = 0);
+ static QMaybe<QGstreamerVideoOutput *> create(QObject *parent = nullptr);
~QGstreamerVideoOutput();
void setVideoSink(QVideoSink *sink);
@@ -84,11 +49,17 @@ public:
void setIsPreview();
void flushSubtitles();
+ void setNativeSize(QSize);
+ void setRotation(QtVideo::Rotation);
+
private:
+ QGstreamerVideoOutput(QGstElement videoConvert, QGstElement videoScale, QGstElement videoSink,
+ QObject *parent);
+
void doLinkSubtitleStream();
+ void updateNativeSize();
QPointer<QGstreamerVideoSink> m_videoSink;
- bool isFakeSink = true;
// Gst elements
QGstPipeline gstPipeline;
@@ -96,10 +67,14 @@ private:
QGstBin gstVideoOutput;
QGstElement videoQueue;
QGstElement videoConvert;
+ QGstElement videoScale;
QGstElement videoSink;
QGstElement subtitleSrc;
QGstElement subtitleSink;
+
+ QSize nativeSize;
+ QtVideo::Rotation rotation{};
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp
index f83f1d518..6ca23006b 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay.cpp
@@ -1,70 +1,34 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgstreamervideooverlay_p.h"
#include <QtGui/qguiapplication.h>
-#include "qgstutils_p.h"
-#include "qgst_p.h"
-#include "qgstreamermessage_p.h"
-#include "qgstreamervideosink_p.h"
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include <gst/video/videooverlay.h>
+#include <common/qglist_helper_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstreamermessage_p.h>
+#include <common/qgstreamervideosink_p.h>
+#include <common/qgstutils_p.h>
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <gst/video/videooverlay.h>
QT_BEGIN_NAMESPACE
struct ElementMap
{
- const char *qtPlatform;
- const char *gstreamerElement;
+ QStringView qtPlatform;
+ const char *gstreamerElement = nullptr;
};
// Ordered by descending priority
-static constexpr ElementMap elementMap[] =
-{
- { "xcb", "xvimagesink" },
- { "xcb", "ximagesink" },
+static constexpr ElementMap elementMap[] = {
+ { u"xcb", "xvimagesink" },
+ { u"xcb", "ximagesink" },
// wayland
- { "wayland", "waylandsink" }
+ { u"wayland", "waylandsink" },
};
static bool qt_gst_element_is_functioning(QGstElement element)
@@ -80,13 +44,14 @@ static bool qt_gst_element_is_functioning(QGstElement element)
static QGstElement findBestVideoSink()
{
+ using namespace Qt::StringLiterals;
QString platform = QGuiApplication::platformName();
// First, try some known video sinks, depending on the Qt platform plugin in use.
- for (auto i : elementMap) {
- if (platform != QLatin1String(i.qtPlatform))
+ for (const auto &i : elementMap) {
+ if (platform != i.qtPlatform)
continue;
- QGstElement choice(i.gstreamerElement, i.gstreamerElement);
+ QGstElement choice = QGstElement::createFromFactory(i.gstreamerElement, i.gstreamerElement);
if (choice.isNull())
continue;
@@ -96,20 +61,18 @@ static QGstElement findBestVideoSink()
// We need a native window ID to use the GstVideoOverlay interface.
// Bail out if the Qt platform plugin in use cannot provide a sensible WId.
- if (platform != QLatin1String("xcb") && platform != QLatin1String("wayland"))
+ if (platform != QStringView{ u"xcb" } && platform != QStringView{ u"wayland" })
return {};
QGstElement choice;
// If none of the known video sinks are available, try to find one that implements the
// GstVideoOverlay interface and has autoplugging rank.
GList *list = qt_gst_video_sinks();
- for (GList *item = list; item != nullptr; item = item->next) {
- GstElementFactory *f = GST_ELEMENT_FACTORY(item->data);
-
+ for (GstElementFactory *f : QGstUtils::GListRangeAdaptor<GstElementFactory *>(list)) {
if (!gst_element_factory_has_interface(f, "GstVideoOverlay"))
continue;
- choice = QGstElement(gst_element_factory_create(f, nullptr));
+ choice = QGstElement::createFromFactory(f, nullptr);
if (choice.isNull())
continue;
@@ -132,7 +95,7 @@ QGstreamerVideoOverlay::QGstreamerVideoOverlay(QGstreamerVideoSink *parent, cons
{
QGstElement sink;
if (!elementName.isEmpty())
- sink = QGstElement(elementName.constData(), nullptr);
+ sink = QGstElement::createFromFactory(elementName.constData());
else
sink = findBestVideoSink();
@@ -157,7 +120,7 @@ void QGstreamerVideoOverlay::setVideoSink(QGstElement sink)
if (sink.isNull())
return;
- m_videoSink = sink;
+ m_videoSink = std::move(sink);
QGstPad pad = m_videoSink.staticPad("sink");
addProbeToPad(pad.pad());
@@ -220,7 +183,7 @@ void QGstreamerVideoOverlay::applyRenderRect()
void QGstreamerVideoOverlay::probeCaps(GstCaps *caps)
{
- QSize size = QGstCaps(caps).at(0).resolution();
+ QSize size = QGstCaps(caps, QGstCaps::NeedsRef).at(0).resolution();
if (size != m_nativeVideoSize) {
m_nativeVideoSize = size;
m_gstreamerVideoSink->setNativeSize(m_nativeVideoSize);
@@ -244,10 +207,12 @@ void QGstreamerVideoOverlay::setFullScreen(bool fullscreen)
bool QGstreamerVideoOverlay::processSyncMessage(const QGstreamerMessage &message)
{
- if (!gst_is_video_overlay_prepare_window_handle_message(message.rawMessage()))
+ if (!gst_is_video_overlay_prepare_window_handle_message(message.message()))
return false;
gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink.object()), m_windowId);
return true;
}
QT_END_NAMESPACE
+
+#include "moc_qgstreamervideooverlay_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay_p.h
index c58b54d3c..588e8b5e4 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooverlay_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERVIDEOOVERLAY_P_H
#define QGSTREAMERVIDEOOVERLAY_P_H
@@ -51,22 +15,22 @@
// We mean it.
//
-#include <qgstpipeline_p.h>
-#include <qgstreamerbufferprobe_p.h>
-#include <qgst_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreamerbufferprobe_p.h>
+#include <common/qgst_p.h>
#include <QtGui/qwindowdefs.h>
QT_BEGIN_NAMESPACE
class QGstreamerVideoSink;
-class Q_MULTIMEDIA_EXPORT QGstreamerVideoOverlay
- : public QObject
- , public QGstreamerSyncMessageFilter
- , private QGstreamerBufferProbe
+class QGstreamerVideoOverlay : public QObject,
+ public QGstreamerSyncMessageFilter,
+ private QGstreamerBufferProbe
{
Q_OBJECT
public:
- explicit QGstreamerVideoOverlay(QGstreamerVideoSink *parent = 0, const QByteArray &elementName = QByteArray());
+ explicit QGstreamerVideoOverlay(QGstreamerVideoSink *parent = nullptr,
+ const QByteArray &elementName = QByteArray());
virtual ~QGstreamerVideoOverlay();
QGstElement videoSink() const;
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
index dc439f71e..2ed2acb36 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
@@ -1,50 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qgstreamervideosink_p.h"
-#include "qgstvideorenderersink_p.h"
-#include "qgstsubtitlesink_p.h"
-#include <qgstutils_p.h>
-#include <QtGui/private/qrhi_p.h>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <common/qgstreamervideosink_p.h>
+#include <common/qgstvideorenderersink_p.h>
+#include <common/qgstsubtitlesink_p.h>
+#include <common/qgst_debug_p.h>
+#include <common/qgstutils_p.h>
+#include <rhi/qrhi.h>
#if QT_CONFIG(gstreamer_gl)
-#include <QtGui/private/qrhigles2_p.h>
#include <QGuiApplication>
#include <QtGui/qopenglcontext.h>
#include <QWindow>
@@ -70,30 +34,85 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcMediaVideoSink, "qt.multimedia.videosink")
+static Q_LOGGING_CATEGORY(qLcGstVideoSink, "qt.multimedia.gstvideosink");
QGstreamerVideoSink::QGstreamerVideoSink(QVideoSink *parent)
: QPlatformVideoSink(parent)
{
- sinkBin = QGstBin("videoSinkBin");
- // This is a hack for some iMX platforms. Thos require the use of a special video
+ sinkBin = QGstBin::create("videoSinkBin");
+
+ // This is a hack for some iMX and NVidia platforms. These require the use of a special video
// conversion element in the pipeline before the video sink, as they unfortunately
- // output some proprietary format from the decoder even though it's marked as
+ // output some proprietary format from the decoder even though it's sometimes marked as
// a regular supported video/x-raw format.
//
// To fix this, simply insert the element into the pipeline if it's available. Otherwise
// we simply use an identity element.
- gstQueue = QGstElement("queue");
- auto imxVideoConvert = QGstElement("imxvideoconvert_g2d");
- if (!imxVideoConvert.isNull())
- gstPreprocess = imxVideoConvert;
- else
- gstPreprocess = QGstElement("identity");
- sinkBin.add(gstQueue, gstPreprocess);
- gstQueue.link(gstPreprocess);
+ gstQueue = QGstElement::createFromFactory("queue", "videoSinkQueue");
+
+ QGstElementFactoryHandle factory;
+
+ // QT_MULTIMEDIA_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT allows users to override the
+ // conversion element. Ideally we construct the element programatically, though.
+ QByteArray preprocessOverride =
+ qgetenv("QT_MULTIMEDIA_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT");
+ if (!preprocessOverride.isEmpty()) {
+ qCDebug(qLcGstVideoSink) << "requesting conversion element from environment: "
+ << preprocessOverride;
+ factory = QGstElementFactoryHandle{
+ gst_element_factory_find(preprocessOverride.constData()),
+ };
+ }
+
+ if (!factory)
+ factory = QGstElementFactoryHandle{
+ gst_element_factory_find("imxvideoconvert_g2d"),
+ };
+
+ if (!factory)
+ factory = QGstElementFactoryHandle{
+ gst_element_factory_find("nvvidconv"),
+ };
+
+ if (factory) {
+ qCDebug(qLcGstVideoSink) << "instantiating conversion element: "
+ << g_type_name(
+ gst_element_factory_get_element_type(factory.get()));
+
+ gstPreprocess = QGstElement::createFromFactory(factory, "preprocess");
+ }
+
+ bool disablePixelAspectRatio =
+ qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_PIXEL_ASPECT_RATIO");
+ if (disablePixelAspectRatio) {
+ // Enabling the pixel aspect ratio may expose a gstreamer bug on cameras that don't expose a
+ // pixel-aspect-ratio via `VIDIOC_CROPCAP`. This can cause the caps negotiation to fail.
+ // Using the QT_GSTREAMER_DISABLE_PIXEL_ASPECT_RATIO environment variable, one can disable
+ // pixel-aspect-ratio handling
+ //
+ // compare: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6242
+ gstCapsFilter =
+ QGstElement::createFromFactory("identity", "nullPixelAspectRatioCapsFilter");
+ } else {
+ gstCapsFilter = QGstElement::createFromFactory("capsfilter", "pixelAspectRatioCapsFilter");
+ QGstCaps capsFilterCaps{
+ gst_caps_new_simple("video/x-raw", "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL),
+ QGstCaps::HasRef,
+ };
+ g_object_set(gstCapsFilter.element(), "caps", capsFilterCaps.caps(), NULL);
+ }
+
+ if (gstPreprocess) {
+ sinkBin.add(gstQueue, gstPreprocess, gstCapsFilter);
+ qLinkGstElements(gstQueue, gstPreprocess, gstCapsFilter);
+ } else {
+ sinkBin.add(gstQueue, gstCapsFilter);
+ qLinkGstElements(gstQueue, gstCapsFilter);
+ }
sinkBin.addGhostPad(gstQueue, "sink");
- gstSubtitleSink = GST_ELEMENT(QGstSubtitleSink::createSink(this));
+ gstSubtitleSink =
+ QGstElement(GST_ELEMENT(QGstSubtitleSink::createSink(this)), QGstElement::NeedsRef);
}
QGstreamerVideoSink::~QGstreamerVideoSink()
@@ -111,7 +130,7 @@ QGstElement QGstreamerVideoSink::gstSink()
void QGstreamerVideoSink::setPipeline(QGstPipeline pipeline)
{
- gstPipeline = pipeline;
+ gstPipeline = std::move(pipeline);
}
bool QGstreamerVideoSink::inStoppedState() const
@@ -139,7 +158,11 @@ void QGstreamerVideoSink::setRhi(QRhi *rhi)
void QGstreamerVideoSink::createQtSink()
{
- gstQtSink = QGstElement(reinterpret_cast<GstElement *>(QGstVideoRendererSink::createSink(this)));
+ if (gstQtSink)
+ gstQtSink.setStateSync(GST_STATE_NULL);
+
+ gstQtSink = QGstElement(reinterpret_cast<GstElement *>(QGstVideoRendererSink::createSink(this)),
+ QGstElement::NeedsRef);
}
void QGstreamerVideoSink::updateSinkElement()
@@ -152,31 +175,25 @@ void QGstreamerVideoSink::updateSinkElement()
if (newSink == gstVideoSink)
return;
- gstPipeline.beginConfig();
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (!gstVideoSink.isNull())
+ sinkBin.stopAndRemoveElements(gstVideoSink);
- if (!gstVideoSink.isNull()) {
- gstVideoSink.setStateSync(GST_STATE_NULL);
- sinkBin.remove(gstVideoSink);
- }
+ newSink.set("async", false); // no asynchronous state changes
- gstVideoSink = newSink;
- sinkBin.add(gstVideoSink);
- if (!gstPreprocess.link(gstVideoSink))
- qCDebug(qLcMediaVideoSink) << "couldn't link preprocess and sink";
- gstVideoSink.setState(GST_STATE_PAUSED);
+ gstVideoSink = newSink;
+ sinkBin.add(gstVideoSink);
+ qLinkGstElements(gstCapsFilter, gstVideoSink);
+ gstVideoSink.setState(GST_STATE_PAUSED);
+ });
- gstPipeline.endConfig();
gstPipeline.dumpGraph("updateVideoSink");
}
void QGstreamerVideoSink::unrefGstContexts()
{
- if (m_gstGlDisplayContext)
- gst_context_unref(m_gstGlDisplayContext);
- m_gstGlDisplayContext = nullptr;
- if (m_gstGlLocalContext)
- gst_context_unref(m_gstGlLocalContext);
- m_gstGlLocalContext = nullptr;
+ m_gstGlDisplayContext.close();
+ m_gstGlLocalContext.close();
m_eglDisplay = nullptr;
m_eglImageTargetTexture2D = nullptr;
}
@@ -198,13 +215,15 @@ void QGstreamerVideoSink::updateGstContexts()
m_eglDisplay = pni->nativeResourceForIntegration("egldisplay");
// qDebug() << "platform is" << platform << m_eglDisplay;
- GstGLDisplay *gstGlDisplay = nullptr;
+ QGstGLDisplayHandle gstGlDisplay;
+
const char *contextName = "eglcontext";
GstGLPlatform glPlatform = GST_GL_PLATFORM_EGL;
// use the egl display if we have one
if (m_eglDisplay) {
#if GST_GL_HAVE_PLATFORM_EGL
- gstGlDisplay = (GstGLDisplay *)gst_gl_display_egl_new_with_egl_display(m_eglDisplay);
+ gstGlDisplay.reset(
+ GST_GL_DISPLAY_CAST(gst_gl_display_egl_new_with_egl_display(m_eglDisplay)));
m_eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES");
#endif
} else {
@@ -216,13 +235,15 @@ void QGstreamerVideoSink::updateGstContexts()
contextName = "glxcontext";
glPlatform = GST_GL_PLATFORM_GLX;
- gstGlDisplay = (GstGLDisplay *)gst_gl_display_x11_new_with_display((Display *)display);
+ gstGlDisplay.reset(GST_GL_DISPLAY_CAST(
+ gst_gl_display_x11_new_with_display(reinterpret_cast<Display *>(display))));
}
#endif
#if GST_GL_HAVE_WINDOW_WAYLAND && __has_include("wayland-client.h")
if (platform.startsWith(QLatin1String("wayland"))) {
Q_ASSERT(!gstGlDisplay);
- gstGlDisplay = (GstGLDisplay *)gst_gl_display_wayland_new_with_display((struct wl_display *)display);
+ gstGlDisplay.reset(GST_GL_DISPLAY_CAST(gst_gl_display_wayland_new_with_display(
+ reinterpret_cast<struct wl_display *>(display))));
}
#endif
}
@@ -238,33 +259,41 @@ void QGstreamerVideoSink::updateGstContexts()
qWarning() << "Could not find resource for" << contextName;
GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2;
- GstGLContext *appContext = gst_gl_context_new_wrapped(gstGlDisplay, (guintptr)nativeContext, glPlatform, glApi);
+ QGstGLContextHandle appContext{
+ gst_gl_context_new_wrapped(gstGlDisplay.get(), guintptr(nativeContext), glPlatform, glApi),
+ };
if (!appContext)
qWarning() << "Could not create wrappped context for platform:" << glPlatform;
- GstGLContext *displayContext = nullptr;
- GError *error = nullptr;
- gst_gl_display_create_context(gstGlDisplay, appContext, &displayContext, &error);
+ gst_gl_context_activate(appContext.get(), true);
+
+ QUniqueGErrorHandle error;
+ gst_gl_context_fill_info(appContext.get(), &error);
if (error) {
- qWarning() << "Could not create display context:" << error->message;
- g_clear_error(&error);
+ qWarning() << "Could not fill context info:" << error;
+ error = {};
}
- if (appContext)
- gst_object_unref(appContext);
+ QGstGLContextHandle displayContext;
+ gst_gl_display_create_context(gstGlDisplay.get(), appContext.get(), &displayContext, &error);
+ if (error)
+ qWarning() << "Could not create display context:" << error;
- m_gstGlDisplayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, false);
- gst_context_set_gl_display(m_gstGlDisplayContext, gstGlDisplay);
- gst_object_unref(gstGlDisplay);
+ appContext.close();
- m_gstGlLocalContext = gst_context_new("gst.gl.local_context", false);
- GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext);
- gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, displayContext, nullptr);
- gst_object_unref(displayContext);
+ m_gstGlDisplayContext.reset(gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, false));
+ gst_context_set_gl_display(m_gstGlDisplayContext.get(), gstGlDisplay.get());
+
+ m_gstGlLocalContext.reset(gst_context_new("gst.gl.local_context", false));
+ GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext.get());
+ gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, displayContext.get(), nullptr);
+ displayContext.close();
if (!gstPipeline.isNull())
- gst_element_set_context(gstPipeline.element(), m_gstGlLocalContext);
+ gst_element_set_context(gstPipeline.element(), m_gstGlLocalContext.get());
#endif // #if QT_CONFIG(gstreamer_gl)
}
QT_END_NAMESPACE
+
+#include "moc_qgstreamervideosink_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideosink_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink_p.h
index 216dd61ee..132eab557 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamervideosink_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink_p.h
@@ -1,44 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QGSTREAMERVIDEOWINDOW_H
-#define QGSTREAMERVIDEOWINDOW_H
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGSTREAMERVIDEOSINK_H
+#define QGSTREAMERVIDEOSINK_H
//
// W A R N I N G
@@ -54,8 +18,8 @@
#include <private/qtmultimediaglobal_p.h>
#include <private/qplatformvideosink_p.h>
-#include <qgstpipeline_p.h>
-#include <qgstreamervideooverlay_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreamervideooverlay_p.h>
#include <QtGui/qcolor.h>
#include <qvideosink.h>
@@ -67,12 +31,11 @@ QT_BEGIN_NAMESPACE
class QGstreamerVideoRenderer;
class QVideoWindow;
-class Q_MULTIMEDIA_EXPORT QGstreamerVideoSink
- : public QPlatformVideoSink
+class QGstreamerVideoSink : public QPlatformVideoSink
{
Q_OBJECT
public:
- explicit QGstreamerVideoSink(QVideoSink *parent = 0);
+ explicit QGstreamerVideoSink(QVideoSink *parent = nullptr);
~QGstreamerVideoSink();
void setRhi(QRhi *rhi) override;
@@ -84,8 +47,8 @@ public:
void setPipeline(QGstPipeline pipeline);
bool inStoppedState() const;
- GstContext *gstGlDisplayContext() const { return m_gstGlDisplayContext; }
- GstContext *gstGlLocalContext() const { return m_gstGlLocalContext; }
+ GstContext *gstGlDisplayContext() const { return m_gstGlDisplayContext.get(); }
+ GstContext *gstGlLocalContext() const { return m_gstGlLocalContext.get(); }
Qt::HANDLE eglDisplay() const { return m_eglDisplay; }
QFunctionPointer eglImageTargetTexture2D() const { return m_eglImageTargetTexture2D; }
@@ -100,6 +63,7 @@ private:
QGstBin sinkBin;
QGstElement gstQueue;
QGstElement gstPreprocess;
+ QGstElement gstCapsFilter;
QGstElement gstVideoSink;
QGstElement gstQtSink;
QGstElement gstSubtitleSink;
@@ -108,8 +72,9 @@ private:
Qt::HANDLE m_eglDisplay = nullptr;
QFunctionPointer m_eglImageTargetTexture2D = nullptr;
- GstContext *m_gstGlLocalContext = nullptr;
- GstContext *m_gstGlDisplayContext = nullptr;
+
+ QGstContextHandle m_gstGlLocalContext;
+ QGstContextHandle m_gstGlDisplayContext;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp b/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp
index d9b76d57f..c6b230d85 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QDebug>
#include <QThread>
@@ -43,18 +7,17 @@
#include "qgstreamervideosink_p.h"
#include "qgstsubtitlesink_p.h"
-#include "qgstutils_p.h"
QT_BEGIN_NAMESPACE
-static GstBaseSinkClass *sink_parent_class;
-static thread_local QGstreamerVideoSink *current_sink;
+static GstBaseSinkClass *gst_sink_parent_class;
+static thread_local QGstreamerVideoSink *gst_current_sink;
#define ST_SINK(s) QGstSubtitleSink *sink(reinterpret_cast<QGstSubtitleSink *>(s))
QGstSubtitleSink *QGstSubtitleSink::createSink(QGstreamerVideoSink *sink)
{
- current_sink = sink;
+ gst_current_sink = sink;
QGstSubtitleSink *gstSink = reinterpret_cast<QGstSubtitleSink *>(
g_object_new(QGstSubtitleSink::get_type(), nullptr));
@@ -65,30 +28,30 @@ QGstSubtitleSink *QGstSubtitleSink::createSink(QGstreamerVideoSink *sink)
GType QGstSubtitleSink::get_type()
{
- static GType type = 0;
-
- if (type == 0) {
- static const GTypeInfo info =
- {
- sizeof(QGstSubtitleSinkClass), // class_size
- base_init, // base_init
- nullptr, // base_finalize
- class_init, // class_init
- nullptr, // class_finalize
- nullptr, // class_data
- sizeof(QGstSubtitleSink), // instance_size
- 0, // n_preallocs
- instance_init, // instance_init
- nullptr // value_table
- };
-
- type = g_type_register_static(
+ static const GTypeInfo info =
+ {
+ sizeof(QGstSubtitleSinkClass), // class_size
+ base_init, // base_init
+ nullptr, // base_finalize
+ class_init, // class_init
+ nullptr, // class_finalize
+ nullptr, // class_data
+ sizeof(QGstSubtitleSink), // instance_size
+ 0, // n_preallocs
+ instance_init, // instance_init
+ nullptr // value_table
+ };
+
+ static const GType type = []() {
+ const auto result = g_type_register_static(
GST_TYPE_BASE_SINK, "QGstSubtitleSink", &info, GTypeFlags(0));
// Register the sink type to be used in custom piplines.
// When surface is ready the sink can be used.
- gst_element_register(nullptr, "qtsubtitlesink", GST_RANK_PRIMARY, type);
- }
+ gst_element_register(nullptr, "qtsubtitlesink", GST_RANK_PRIMARY, result);
+
+ return result;
+ }();
return type;
}
@@ -97,7 +60,7 @@ void QGstSubtitleSink::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
- sink_parent_class = reinterpret_cast<GstBaseSinkClass *>(g_type_class_peek_parent(g_class));
+ gst_sink_parent_class = reinterpret_cast<GstBaseSinkClass *>(g_type_class_peek_parent(g_class));
GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class);
base_sink_class->render = QGstSubtitleSink::render;
@@ -132,41 +95,41 @@ void QGstSubtitleSink::instance_init(GTypeInstance *instance, gpointer g_class)
Q_UNUSED(g_class);
ST_SINK(instance);
- Q_ASSERT(current_sink);
- sink->sink = current_sink;
- current_sink = nullptr;
+ Q_ASSERT(gst_current_sink);
+ sink->sink = gst_current_sink;
+ gst_current_sink = nullptr;
}
void QGstSubtitleSink::finalize(GObject *object)
{
// Chain up
- G_OBJECT_CLASS(sink_parent_class)->finalize(object);
+ G_OBJECT_CLASS(gst_sink_parent_class)->finalize(object);
}
GstStateChangeReturn QGstSubtitleSink::change_state(GstElement *element, GstStateChange transition)
{
- return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition);
+ return GST_ELEMENT_CLASS(gst_sink_parent_class)->change_state(element, transition);
}
GstCaps *QGstSubtitleSink::get_caps(GstBaseSink *base, GstCaps *filter)
{
- return sink_parent_class->get_caps(base, filter);
+ return gst_sink_parent_class->get_caps(base, filter);
}
gboolean QGstSubtitleSink::set_caps(GstBaseSink *base, GstCaps *caps)
{
- qDebug() << "set_caps:" << QGstCaps(caps).toString();
- return sink_parent_class->set_caps(base, caps);
+ qDebug() << "set_caps:" << caps;
+ return gst_sink_parent_class->set_caps(base, caps);
}
gboolean QGstSubtitleSink::propose_allocation(GstBaseSink *base, GstQuery *query)
{
- return sink_parent_class->propose_allocation(base, query);
+ return gst_sink_parent_class->propose_allocation(base, query);
}
GstFlowReturn QGstSubtitleSink::wait_event(GstBaseSink *base, GstEvent *event)
{
- GstFlowReturn retval = sink_parent_class->wait_event(base, event);
+ GstFlowReturn retval = gst_sink_parent_class->wait_event(base, event);
ST_SINK(base);
if (event->type == GST_EVENT_GAP) {
// qDebug() << "gap, clearing subtitle";
@@ -182,7 +145,7 @@ GstFlowReturn QGstSubtitleSink::render(GstBaseSink *base, GstBuffer *buffer)
GstMapInfo info;
QString subtitle;
if (gst_memory_map(mem, &info, GST_MAP_READ))
- subtitle = QString::fromUtf8(info.data);
+ subtitle = QString::fromUtf8(reinterpret_cast<const char *>(info.data));
gst_memory_unmap(mem, &info);
// qDebug() << "render" << buffer << subtitle;
sink->sink->setSubtitleText(subtitle);
diff --git a/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h b/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h
index 179b02a50..0f515cb99 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstsubtitlesink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTSUBTITLESINK_P_H
#define QGSTSUBTITLESINK_P_H
@@ -58,17 +22,17 @@
#include <QtCore/qqueue.h>
#include <QtCore/qpointer.h>
#include <QtCore/qwaitcondition.h>
-#include <qgst_p.h>
+#include <common/qgst_p.h>
#include <gst/base/gstbasesink.h>
QT_BEGIN_NAMESPACE
class QGstreamerVideoSink;
-class Q_MULTIMEDIA_EXPORT QGstSubtitleSink
+class QGstSubtitleSink
{
public:
- GstBaseSink parent;
+ GstBaseSink parent{};
static QGstSubtitleSink *createSink(QGstreamerVideoSink *sink);
diff --git a/src/plugins/multimedia/gstreamer/common/qgstutils.cpp b/src/plugins/multimedia/gstreamer/common/qgstutils.cpp
index 21173a5cd..40fb4b6f7 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstutils.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstutils.cpp
@@ -1,70 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include <QtMultimedia/private/qtmultimediaglobal_p.h>
-#include "qgstutils_p.h"
+#include <common/qgstutils_p.h>
+#include <common/qgst_p.h>
-#include <QtCore/qdatetime.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qregularexpression.h>
-#include <QtCore/qsize.h>
-#include <QtCore/qset.h>
-#include <QtCore/qstringlist.h>
-#include <QtGui/qimage.h>
-#include <qaudioformat.h>
-#include <QtCore/qelapsedtimer.h>
-#include <QtMultimedia/qvideoframeformat.h>
-#include <private/qmultimediautils_p.h>
+#include <QtMultimedia/qaudioformat.h>
-#include <gst/audio/audio.h>
-#include <gst/video/video.h>
-
-template<typename T, int N> constexpr int lengthOf(const T (&)[N]) { return N; }
+#include <chrono>
QT_BEGIN_NAMESPACE
-
namespace {
-static const char *audioSampleFormatNames[QAudioFormat::NSampleFormats] = {
+const char *audioSampleFormatNames[QAudioFormat::NSampleFormats] = {
nullptr,
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
"U8",
@@ -79,7 +27,7 @@ static const char *audioSampleFormatNames[QAudioFormat::NSampleFormats] = {
#endif
};
-static QAudioFormat::SampleFormat gstSampleFormatToSampleFormat(const char *fmt)
+QAudioFormat::SampleFormat gstSampleFormatToSampleFormat(const char *fmt)
{
if (fmt) {
for (int i = 1; i < QAudioFormat::NSampleFormats; ++i) {
@@ -91,7 +39,7 @@ static QAudioFormat::SampleFormat gstSampleFormatToSampleFormat(const char *fmt)
return QAudioFormat::Unknown;
}
-}
+} // namespace
/*
Returns audio format for a sample \a sample.
@@ -99,13 +47,13 @@ static QAudioFormat::SampleFormat gstSampleFormatToSampleFormat(const char *fmt)
*/
QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample)
{
- QGstCaps caps = gst_sample_get_caps(sample);
+ auto caps = QGstCaps(gst_sample_get_caps(sample), QGstCaps::NeedsRef);
if (caps.isNull())
- return QAudioFormat();
+ return {};
return audioFormatForCaps(caps);
}
-QAudioFormat QGstUtils::audioFormatForCaps(QGstCaps caps)
+QAudioFormat QGstUtils::audioFormatForCaps(const QGstCaps &caps)
{
QAudioFormat format;
QGstStructure s = caps.at(0);
@@ -132,19 +80,21 @@ QAudioFormat QGstUtils::audioFormatForCaps(QGstCaps caps)
\note Caller must unreference GstCaps.
*/
-QGstMutableCaps QGstUtils::capsForAudioFormat(const QAudioFormat &format)
+QGstCaps QGstUtils::capsForAudioFormat(const QAudioFormat &format)
{
if (!format.isValid())
return {};
auto sampleFormat = format.sampleFormat();
- return gst_caps_new_simple(
+ auto caps = gst_caps_new_simple(
"audio/x-raw",
"format" , G_TYPE_STRING, audioSampleFormatNames[sampleFormat],
"rate" , G_TYPE_INT , format.sampleRate(),
"channels", G_TYPE_INT , format.channelCount(),
"layout" , G_TYPE_STRING, "interleaved",
nullptr);
+
+ return QGstCaps(caps, QGstCaps::HasRef);
}
QList<QAudioFormat::SampleFormat> QGValue::getSampleFormats() const
@@ -155,346 +105,37 @@ QList<QAudioFormat::SampleFormat> QGValue::getSampleFormats() const
QList<QAudioFormat::SampleFormat> formats;
guint nFormats = gst_value_list_get_size(value);
for (guint f = 0; f < nFormats; ++f) {
- QGValue v = gst_value_list_get_value(value, f);
+ QGValue v = QGValue{ gst_value_list_get_value(value, f) };
auto *name = v.toString();
QAudioFormat::SampleFormat fmt = gstSampleFormatToSampleFormat(name);
if (fmt == QAudioFormat::Unknown)
- continue;;
+ continue;
formats.append(fmt);
}
return formats;
}
-namespace {
-
-struct VideoFormat
-{
- QVideoFrameFormat::PixelFormat pixelFormat;
- GstVideoFormat gstFormat;
-};
-
-static const VideoFormat qt_videoFormatLookup[] =
-{
- { QVideoFrameFormat::Format_YUV420P, GST_VIDEO_FORMAT_I420 },
- { QVideoFrameFormat::Format_YUV422P, GST_VIDEO_FORMAT_Y42B },
- { QVideoFrameFormat::Format_YV12 , GST_VIDEO_FORMAT_YV12 },
- { QVideoFrameFormat::Format_UYVY , GST_VIDEO_FORMAT_UYVY },
- { QVideoFrameFormat::Format_YUYV , GST_VIDEO_FORMAT_YUY2 },
- { QVideoFrameFormat::Format_NV12 , GST_VIDEO_FORMAT_NV12 },
- { QVideoFrameFormat::Format_NV21 , GST_VIDEO_FORMAT_NV21 },
- { QVideoFrameFormat::Format_AYUV , GST_VIDEO_FORMAT_AYUV },
- { QVideoFrameFormat::Format_Y8 , GST_VIDEO_FORMAT_GRAY8 },
- { QVideoFrameFormat::Format_XRGB8888 , GST_VIDEO_FORMAT_xRGB },
- { QVideoFrameFormat::Format_XBGR8888 , GST_VIDEO_FORMAT_xBGR },
- { QVideoFrameFormat::Format_RGBX8888 , GST_VIDEO_FORMAT_RGBx },
- { QVideoFrameFormat::Format_BGRX8888 , GST_VIDEO_FORMAT_BGRx },
- { QVideoFrameFormat::Format_ARGB8888, GST_VIDEO_FORMAT_ARGB },
- { QVideoFrameFormat::Format_ABGR8888, GST_VIDEO_FORMAT_ABGR },
- { QVideoFrameFormat::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA },
- { QVideoFrameFormat::Format_BGRA8888, GST_VIDEO_FORMAT_BGRA },
-#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- { QVideoFrameFormat::Format_Y16 , GST_VIDEO_FORMAT_GRAY16_LE },
- { QVideoFrameFormat::Format_P010 , GST_VIDEO_FORMAT_P010_10LE },
-#else
- { QVideoFrameFormat::Format_Y16 , GST_VIDEO_FORMAT_GRAY16_BE },
- { QVideoFrameFormat::Format_P010 , GST_VIDEO_FORMAT_P010_10BE },
-#endif
-};
-
-static int indexOfVideoFormat(QVideoFrameFormat::PixelFormat format)
-{
- for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i)
- if (qt_videoFormatLookup[i].pixelFormat == format)
- return i;
-
- return -1;
-}
-
-static int indexOfVideoFormat(GstVideoFormat format)
-{
- for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i)
- if (qt_videoFormatLookup[i].gstFormat == format)
- return i;
-
- return -1;
-}
-
-}
-
-QVideoFrameFormat QGstCaps::formatForCaps(GstVideoInfo *info) const
-{
- GstVideoInfo vidInfo;
- GstVideoInfo *infoPtr = info ? info : &vidInfo;
-
- if (gst_video_info_from_caps(infoPtr, caps)) {
- int index = indexOfVideoFormat(infoPtr->finfo->format);
-
- if (index != -1) {
- QVideoFrameFormat format(
- QSize(infoPtr->width, infoPtr->height),
- qt_videoFormatLookup[index].pixelFormat);
-
- if (infoPtr->fps_d > 0)
- format.setFrameRate(qreal(infoPtr->fps_n) / infoPtr->fps_d);
-
- QVideoFrameFormat::ColorRange range = QVideoFrameFormat::ColorRange_Unknown;
- switch (infoPtr->colorimetry.range) {
- case GST_VIDEO_COLOR_RANGE_UNKNOWN:
- break;
- case GST_VIDEO_COLOR_RANGE_0_255:
- range = QVideoFrameFormat::ColorRange_Full;
- break;
- case GST_VIDEO_COLOR_RANGE_16_235:
- range = QVideoFrameFormat::ColorRange_Video;
- break;
- }
- format.setColorRange(range);
-
- QVideoFrameFormat::ColorSpace colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
- switch (infoPtr->colorimetry.matrix) {
- case GST_VIDEO_COLOR_MATRIX_UNKNOWN:
- case GST_VIDEO_COLOR_MATRIX_RGB:
- case GST_VIDEO_COLOR_MATRIX_FCC:
- break;
- case GST_VIDEO_COLOR_MATRIX_BT709:
- colorSpace = QVideoFrameFormat::ColorSpace_BT709;
- break;
- case GST_VIDEO_COLOR_MATRIX_BT601:
- colorSpace = QVideoFrameFormat::ColorSpace_BT601;
- break;
- case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
- colorSpace = QVideoFrameFormat::ColorSpace_AdobeRgb;
- break;
- case GST_VIDEO_COLOR_MATRIX_BT2020:
- colorSpace = QVideoFrameFormat::ColorSpace_BT2020;
- break;
- }
- format.setColorSpace(colorSpace);
-
- QVideoFrameFormat::ColorTransfer transfer = QVideoFrameFormat::ColorTransfer_Unknown;
- switch (infoPtr->colorimetry.transfer) {
- case GST_VIDEO_TRANSFER_UNKNOWN:
- break;
- case GST_VIDEO_TRANSFER_GAMMA10:
- transfer = QVideoFrameFormat::ColorTransfer_Linear;
- break;
- case GST_VIDEO_TRANSFER_GAMMA22:
- case GST_VIDEO_TRANSFER_SMPTE240M:
- case GST_VIDEO_TRANSFER_SRGB:
- case GST_VIDEO_TRANSFER_ADOBERGB:
- transfer = QVideoFrameFormat::ColorTransfer_Gamma22;
- break;
- case GST_VIDEO_TRANSFER_GAMMA18:
- case GST_VIDEO_TRANSFER_GAMMA20:
- // not quite, but best fit
- case GST_VIDEO_TRANSFER_BT709:
- case GST_VIDEO_TRANSFER_BT2020_12:
- transfer = QVideoFrameFormat::ColorTransfer_BT709;
- break;
- case GST_VIDEO_TRANSFER_GAMMA28:
- transfer = QVideoFrameFormat::ColorTransfer_Gamma28;
- break;
- case GST_VIDEO_TRANSFER_LOG100:
- case GST_VIDEO_TRANSFER_LOG316:
- break;
-#if GST_CHECK_VERSION(1, 18, 0)
- case GST_VIDEO_TRANSFER_SMPTE2084:
- transfer = QVideoFrameFormat::ColorTransfer_ST2084;
- break;
- case GST_VIDEO_TRANSFER_ARIB_STD_B67:
- transfer = QVideoFrameFormat::ColorTransfer_STD_B67;
- break;
- case GST_VIDEO_TRANSFER_BT2020_10:
- transfer = QVideoFrameFormat::ColorTransfer_BT709;
- break;
- case GST_VIDEO_TRANSFER_BT601:
- transfer = QVideoFrameFormat::ColorTransfer_BT601;
- break;
-#endif
- }
- format.setColorTransfer(transfer);
-
- return format;
- }
- }
- return QVideoFrameFormat();
-}
-
-void QGstMutableCaps::addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier)
-{
- GValue list = {};
- g_value_init(&list, GST_TYPE_LIST);
-
- for (QVideoFrameFormat::PixelFormat format : formats) {
- int index = indexOfVideoFormat(format);
- if (index == -1)
- continue;
- GValue item = {};
-
- g_value_init(&item, G_TYPE_STRING);
- g_value_set_string(&item, gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat));
- gst_value_list_append_value(&list, &item);
- g_value_unset(&item);
- }
- QGValue v(&list);
- auto *structure = gst_structure_new("video/x-raw",
- "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1,
- "width" , GST_TYPE_INT_RANGE, 1, INT_MAX,
- "height" , GST_TYPE_INT_RANGE, 1, INT_MAX,
- nullptr);
- gst_structure_set_value(structure, "format", &list);
- gst_caps_append_structure(caps, structure);
- g_value_unset(&list);
-
- if (modifier)
- gst_caps_set_features(caps, size() - 1, gst_caps_features_from_string(modifier));
-}
-
-QGstMutableCaps QGstMutableCaps::fromCameraFormat(const QCameraFormat &format)
+void QGstUtils::setFrameTimeStampsFromBuffer(QVideoFrame *frame, GstBuffer *buffer)
{
- QGstMutableCaps caps;
- caps.create();
-
- QSize size = format.resolution();
- GstStructure *structure = nullptr;
-// int num = 0;
-// int den = 1;
-// if (format.maxFrameRate() > 0)
-// qt_real_to_fraction(1. / format.maxFrameRate(), &num, &den);
-// qDebug() << "fromCameraFormat" << format.maxFrameRate() << num << den;
-
- if (format.pixelFormat() == QVideoFrameFormat::Format_Jpeg) {
- structure = gst_structure_new("image/jpeg",
- "width" , G_TYPE_INT, size.width(),
- "height" , G_TYPE_INT, size.height(),
-// "framerate", GST_TYPE_FRACTION, den, num,
- nullptr);
- } else {
- int index = indexOfVideoFormat(format.pixelFormat());
- if (index < 0)
- return QGstMutableCaps();
- auto gstFormat = qt_videoFormatLookup[index].gstFormat;
- structure = gst_structure_new("video/x-raw",
- "format" , G_TYPE_STRING, gst_video_format_to_string(gstFormat),
- "width" , G_TYPE_INT, size.width(),
- "height" , G_TYPE_INT, size.height(),
-// "framerate", GST_TYPE_FRACTION, den, num,
- nullptr);
- }
- gst_caps_append_structure(caps.caps, structure);
- return caps;
-}
+ using namespace std::chrono;
+ using namespace std::chrono_literals;
-void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer)
-{
// GStreamer uses nanoseconds, Qt uses microseconds
- qint64 startTime = GST_BUFFER_TIMESTAMP(buffer);
- if (startTime >= 0) {
- frame->setStartTime(startTime/G_GINT64_CONSTANT (1000));
-
- qint64 duration = GST_BUFFER_DURATION(buffer);
- if (duration >= 0)
- frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000));
- }
-}
-
-QSize QGstStructure::resolution() const
-{
- QSize size;
+ nanoseconds startTime{ GST_BUFFER_TIMESTAMP(buffer) };
+ if (startTime >= 0ns) {
+ frame->setStartTime(floor<microseconds>(startTime).count());
- int w, h;
- if (structure &&
- gst_structure_get_int(structure, "width", &w) &&
- gst_structure_get_int(structure, "height", &h)) {
- size.rwidth() = w;
- size.rheight() = h;
+ nanoseconds duration{ GST_BUFFER_DURATION(buffer) };
+ if (duration >= 0ns)
+ frame->setEndTime(floor<microseconds>(startTime + duration).count());
}
-
- return size;
-}
-
-QVideoFrameFormat::PixelFormat QGstStructure::pixelFormat() const
-{
- QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid;
-
- if (!structure)
- return pixelFormat;
-
- if (gst_structure_has_name(structure, "video/x-raw")) {
- const gchar *s = gst_structure_get_string(structure, "format");
- if (s) {
- GstVideoFormat format = gst_video_format_from_string(s);
- int index = indexOfVideoFormat(format);
-
- if (index != -1)
- pixelFormat = qt_videoFormatLookup[index].pixelFormat;
- }
- } else if (gst_structure_has_name(structure, "image/jpeg")) {
- pixelFormat = QVideoFrameFormat::Format_Jpeg;
- }
-
- return pixelFormat;
-}
-
-QGRange<float> QGstStructure::frameRateRange() const
-{
- float minRate = 0.;
- float maxRate = 0.;
-
- if (!structure)
- return {0.f, 0.f};
-
- auto extractFraction = [] (const GValue *v) -> float {
- return (float)gst_value_get_fraction_numerator(v)/(float)gst_value_get_fraction_denominator(v);
- };
- auto extractFrameRate = [&] (const GValue *v) {
- auto insert = [&] (float min, float max) {
- if (max > maxRate)
- maxRate = max;
- if (min < minRate)
- minRate = min;
- };
-
- if (GST_VALUE_HOLDS_FRACTION(v)) {
- float rate = extractFraction(v);
- insert(rate, rate);
- } else if (GST_VALUE_HOLDS_FRACTION_RANGE(v)) {
- auto *min = gst_value_get_fraction_range_max(v);
- auto *max = gst_value_get_fraction_range_max(v);
- insert(extractFraction(min), extractFraction(max));
- }
- };
-
- const GValue *gstFrameRates = gst_structure_get_value(structure, "framerate");
- if (gstFrameRates) {
- if (GST_VALUE_HOLDS_LIST(gstFrameRates)) {
- guint nFrameRates = gst_value_list_get_size(gstFrameRates);
- for (guint f = 0; f < nFrameRates; ++f) {
- extractFrameRate(gst_value_list_get_value(gstFrameRates, f));
- }
- } else {
- extractFrameRate(gstFrameRates);
- }
- } else {
- const GValue *min = gst_structure_get_value(structure, "min-framerate");
- const GValue *max = gst_structure_get_value(structure, "max-framerate");
- if (min && max) {
- minRate = extractFraction(min);
- maxRate = extractFraction(max);
- }
- }
-
- return {minRate, maxRate};
}
GList *qt_gst_video_sinks()
{
- GList *list = nullptr;
-
- list = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO,
+ return gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK
+ | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO,
GST_RANK_MARGINAL);
-
- return list;
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstutils_p.h b/src/plugins/multimedia/gstreamer/common/qgstutils_p.h
index 9782486a7..c65fcf090 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstutils_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstutils_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTUTILS_P_H
#define QGSTUTILS_P_H
@@ -51,34 +15,26 @@
// We mean it.
//
-#include <private/qtmultimediaglobal_p.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qset.h>
-#include <qgst_p.h>
-#include <gst/video/video.h>
-#include <qaudioformat.h>
-#include <qcamera.h>
-#include <qvideoframe.h>
-#include <QDebug>
+#include <gst/gstsample.h>
+#include <gst/gstbuffer.h>
+
+#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
-class QSize;
-class QVariant;
-class QByteArray;
-class QImage;
-class QVideoFrameFormat;
+class QAudioFormat;
+class QGstCaps;
+class QVideoFrame;
namespace QGstUtils {
- Q_MULTIMEDIA_EXPORT QAudioFormat audioFormatForSample(GstSample *sample);
- QAudioFormat audioFormatForCaps(QGstCaps caps);
- Q_MULTIMEDIA_EXPORT QGstMutableCaps capsForAudioFormat(const QAudioFormat &format);
+QAudioFormat audioFormatForSample(GstSample *sample);
+QAudioFormat audioFormatForCaps(const QGstCaps &caps);
+QGstCaps capsForAudioFormat(const QAudioFormat &format);
- void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer);
-}
+void setFrameTimeStampsFromBuffer(QVideoFrame *frame, GstBuffer *buffer);
+} // namespace QGstUtils
-Q_MULTIMEDIA_EXPORT GList *qt_gst_video_sinks();
+GList *qt_gst_video_sinks();
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp b/src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp
index 0d22999b2..6552786bb 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstvideobuffer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgstvideobuffer_p.h"
#include "qgstreamervideosink_p.h"
@@ -48,24 +12,24 @@
#include <gst/video/gstvideometa.h>
#include <gst/pbutils/gstpluginsbaseversion.h>
-#include "qgstutils_p.h"
+#include <common/qgstutils_p.h>
#if QT_CONFIG(gstreamer_gl)
-#include <QtGui/private/qrhi_p.h>
-#include <QtGui/private/qrhigles2_p.h>
-#include <QtGui/qopenglcontext.h>
-#include <QtGui/qopenglfunctions.h>
-#include <QtGui/qopengl.h>
-
-#include <gst/gl/gstglconfig.h>
-#include <gst/gl/gstglmemory.h>
-#include <gst/gl/gstglsyncmeta.h>
-#if QT_CONFIG(linux_dmabuf)
-#include <gst/allocators/gstdmabuf.h>
-#endif
+# include <QtGui/rhi/qrhi.h>
+# include <QtGui/qopenglcontext.h>
+# include <QtGui/qopenglfunctions.h>
+# include <QtGui/qopengl.h>
+
+# include <gst/gl/gstglconfig.h>
+# include <gst/gl/gstglmemory.h>
+# include <gst/gl/gstglsyncmeta.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
+# include <EGL/egl.h>
+# include <EGL/eglext.h>
+
+# if QT_CONFIG(linux_dmabuf)
+# include <gst/allocators/gstdmabuf.h>
+# endif
#endif
QT_BEGIN_NAMESPACE
@@ -87,46 +51,32 @@ QT_BEGIN_NAMESPACE
#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
-QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, QGstreamerVideoSink *sink,
- const QVideoFrameFormat &frameFormat,
+QGstVideoBuffer::QGstVideoBuffer(QGstBufferHandle buffer, const GstVideoInfo &info,
+ QGstreamerVideoSink *sink, const QVideoFrameFormat &frameFormat,
QGstCaps::MemoryFormat format)
- : QAbstractVideoBuffer((sink && sink->rhi() && format != QGstCaps::CpuMemory) ?
- QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle, sink ? sink->rhi() : nullptr)
- , memoryFormat(format)
- , m_frameFormat(frameFormat)
- , m_rhi(sink ? sink->rhi() : nullptr)
- , m_videoInfo(info)
- , m_buffer(buffer)
+ : QAbstractVideoBuffer((sink && sink->rhi() && format != QGstCaps::CpuMemory)
+ ? QVideoFrame::RhiTextureHandle
+ : QVideoFrame::NoHandle,
+ sink ? sink->rhi() : nullptr),
+ memoryFormat(format),
+ m_frameFormat(frameFormat),
+ m_rhi(sink ? sink->rhi() : nullptr),
+ m_videoInfo(info),
+ m_buffer(std::move(buffer))
{
- gst_buffer_ref(m_buffer);
if (sink) {
eglDisplay = sink->eglDisplay();
eglImageTargetTexture2D = sink->eglImageTargetTexture2D();
}
+
+#if !QT_CONFIG(gstreamer_gl)
+ Q_UNUSED(memoryFormat);
+#endif
}
QGstVideoBuffer::~QGstVideoBuffer()
{
unmap();
-
- gst_buffer_unref(m_buffer);
- if (m_syncBuffer)
- gst_buffer_unref(m_syncBuffer);
-
- if (m_ownTextures && glContext) {
- int planes = 0;
- for (planes = 0; planes < 3; ++planes) {
- if (m_textures[planes] == 0)
- break;
- }
-#if QT_CONFIG(gstreamer_gl)
- if (m_rhi) {
- m_rhi->makeThreadLocalNativeContextCurrent();
- QOpenGLFunctions functions(glContext);
- functions.glDeleteTextures(planes, m_textures);
- }
-#endif
- }
}
@@ -145,7 +95,7 @@ QAbstractVideoBuffer::MapData QGstVideoBuffer::map(QVideoFrame::MapMode mode)
return mapData;
if (m_videoInfo.finfo->n_planes == 0) { // Encoded
- if (gst_buffer_map(m_buffer, &m_frame.map[0], flags)) {
+ if (gst_buffer_map(m_buffer.get(), &m_frame.map[0], flags)) {
mapData.nPlanes = 1;
mapData.bytesPerLine[0] = -1;
mapData.size[0] = m_frame.map[0].size;
@@ -153,7 +103,7 @@ QAbstractVideoBuffer::MapData QGstVideoBuffer::map(QVideoFrame::MapMode mode)
m_mode = mode;
}
- } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, flags)) {
+ } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer.get(), flags)) {
mapData.nPlanes = GST_VIDEO_FRAME_N_PLANES(&m_frame);
for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES(&m_frame); ++i) {
@@ -171,7 +121,7 @@ void QGstVideoBuffer::unmap()
{
if (m_mode != QVideoFrame::NotMapped) {
if (m_videoInfo.finfo->n_planes == 0)
- gst_buffer_unmap(m_buffer, &m_frame.map[0]);
+ gst_buffer_unmap(m_buffer.get(), &m_frame.map[0]);
else
gst_video_frame_unmap(&m_frame);
}
@@ -264,122 +214,185 @@ fourccFromVideoInfo(const GstVideoInfo * info, int plane)
}
#endif
-void QGstVideoBuffer::mapTextures()
+#if QT_CONFIG(gstreamer_gl)
+struct GlTextures
{
- if (!m_rhi)
- return;
+ uint count = 0;
+ bool owned = false;
+ std::array<guint32, QVideoTextureHelper::TextureDescription::maxPlanes> names{};
+};
-#if QT_CONFIG(gstreamer_gl)
- if (memoryFormat == QGstCaps::GLTexture) {
- auto *mem = GST_GL_BASE_MEMORY_CAST(gst_buffer_peek_memory(m_buffer, 0));
- Q_ASSERT(mem);
- if (!gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, GstMapFlags(GST_MAP_READ|GST_MAP_GL))) {
- qWarning() << "Could not map GL textures";
- } else {
- auto *sync_meta = gst_buffer_get_gl_sync_meta(m_buffer);
-
- if (!sync_meta) {
- m_syncBuffer = gst_buffer_new();
- sync_meta = gst_buffer_add_gl_sync_meta(mem->context, m_syncBuffer);
- }
- gst_gl_sync_meta_set_sync_point (sync_meta, mem->context);
- gst_gl_sync_meta_wait (sync_meta, mem->context);
-
- int nPlanes = m_frame.info.finfo->n_planes;
- for (int i = 0; i < nPlanes; ++i) {
- m_textures[i] = *(guint32 *)m_frame.data[i];
- }
- gst_video_frame_unmap(&m_frame);
+class QGstQVideoFrameTextures : public QVideoFrameTextures
+{
+public:
+ QGstQVideoFrameTextures(QRhi *rhi, QSize size, QVideoFrameFormat::PixelFormat format, GlTextures &textures)
+ : m_rhi(rhi)
+ , m_glTextures(textures)
+ {
+ auto desc = QVideoTextureHelper::textureDescription(format);
+ for (uint i = 0; i < textures.count; ++i) {
+ QSize planeSize(desc->widthForPlane(size.width(), int(i)),
+ desc->heightForPlane(size.height(), int(i)));
+ m_textures[i].reset(rhi->newTexture(desc->textureFormat[i], planeSize, 1, {}));
+ m_textures[i]->createFrom({textures.names[i], 0});
}
}
+
+ ~QGstQVideoFrameTextures()
+ {
+ m_rhi->makeThreadLocalNativeContextCurrent();
+ auto ctx = QOpenGLContext::currentContext();
+ if (m_glTextures.owned && ctx)
+ ctx->functions()->glDeleteTextures(int(m_glTextures.count), m_glTextures.names.data());
+ }
+
+ QRhiTexture *texture(uint plane) const override
+ {
+ return plane < m_glTextures.count ? m_textures[plane].get() : nullptr;
+ }
+
+private:
+ QRhi *m_rhi = nullptr;
+ GlTextures m_glTextures;
+ std::unique_ptr<QRhiTexture> m_textures[QVideoTextureHelper::TextureDescription::maxPlanes];
+};
+
+static GlTextures mapFromGlTexture(const QGstBufferHandle &bufferHandle, GstVideoFrame &frame,
+ GstVideoInfo &videoInfo)
+{
+ GstBuffer *buffer = bufferHandle.get();
+ auto *mem = GST_GL_BASE_MEMORY_CAST(gst_buffer_peek_memory(buffer, 0));
+ if (!mem)
+ return {};
+
+ if (!gst_video_frame_map(&frame, &videoInfo, buffer, GstMapFlags(GST_MAP_READ|GST_MAP_GL))) {
+ qWarning() << "Could not map GL textures";
+ return {};
+ }
+
+ auto *sync_meta = gst_buffer_get_gl_sync_meta(buffer);
+ GstBuffer *sync_buffer = nullptr;
+ if (!sync_meta) {
+ sync_buffer = gst_buffer_new();
+ sync_meta = gst_buffer_add_gl_sync_meta(mem->context, sync_buffer);
+ }
+ gst_gl_sync_meta_set_sync_point (sync_meta, mem->context);
+ gst_gl_sync_meta_wait (sync_meta, mem->context);
+ if (sync_buffer)
+ gst_buffer_unref(sync_buffer);
+
+ GlTextures textures;
+ textures.count = frame.info.finfo->n_planes;
+
+ for (uint i = 0; i < textures.count; ++i)
+ textures.names[i] = *(guint32 *)frame.data[i];
+
+ gst_video_frame_unmap(&frame);
+
+ return textures;
+}
+
#if GST_GL_HAVE_PLATFORM_EGL && QT_CONFIG(linux_dmabuf)
- else if (memoryFormat == QGstCaps::DMABuf) {
- if (m_textures[0])
- return;
- Q_ASSERT(gst_is_dmabuf_memory(gst_buffer_peek_memory(m_buffer, 0)));
- Q_ASSERT(eglDisplay);
- Q_ASSERT(eglImageTargetTexture2D);
-
- auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(m_rhi->nativeHandles());
- glContext = nativeHandles->context;
- if (!glContext) {
- qWarning() << "no GL context";
- return;
- }
+static GlTextures mapFromDmaBuffer(QRhi *rhi, const QGstBufferHandle &bufferHandle,
+ GstVideoFrame &frame, GstVideoInfo &videoInfo,
+ Qt::HANDLE eglDisplay, QFunctionPointer eglImageTargetTexture2D)
+{
+ GstBuffer *buffer = bufferHandle.get();
- if (!gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, GstMapFlags(GST_MAP_READ))) {
- qDebug() << "Couldn't map DMA video frame";
- return;
- }
+ Q_ASSERT(gst_is_dmabuf_memory(gst_buffer_peek_memory(buffer, 0)));
+ Q_ASSERT(eglDisplay);
+ Q_ASSERT(eglImageTargetTexture2D);
- int nPlanes = GST_VIDEO_FRAME_N_PLANES(&m_frame);
-// int width = GST_VIDEO_FRAME_WIDTH(&m_frame);
-// int height = GST_VIDEO_FRAME_HEIGHT(&m_frame);
- Q_ASSERT(GST_VIDEO_FRAME_N_PLANES(&m_frame) == gst_buffer_n_memory(m_buffer));
-
- QOpenGLFunctions functions(glContext);
- functions.glGenTextures(nPlanes, m_textures);
- m_ownTextures = true;
-
-// qDebug() << Qt::hex << "glGenTextures: glerror" << glGetError() << "egl error" << eglGetError();
-// qDebug() << "converting DMA buffer nPlanes=" << nPlanes << m_textures[0] << m_textures[1] << m_textures[2];
-
- for (int i = 0; i < nPlanes; ++i) {
- auto offset = GST_VIDEO_FRAME_PLANE_OFFSET(&m_frame, i);
- auto stride = GST_VIDEO_FRAME_PLANE_STRIDE(&m_frame, i);
- int planeWidth = GST_VIDEO_FRAME_COMP_WIDTH(&m_frame, i);
- int planeHeight = GST_VIDEO_FRAME_COMP_HEIGHT(&m_frame, i);
- auto mem = gst_buffer_peek_memory(m_buffer, i);
- int fd = gst_dmabuf_memory_get_fd(mem);
-
-// qDebug() << " plane" << i << "size" << width << height << "stride" << stride << "offset" << offset << "fd=" << fd;
- // ### do we need to open/close the fd?
- // ### can we convert several planes at once?
- // Get the correct DRM_FORMATs from the texture format in the description
- EGLAttrib const attribute_list[] = {
- EGL_WIDTH, planeWidth,
- EGL_HEIGHT, planeHeight,
- EGL_LINUX_DRM_FOURCC_EXT, fourccFromVideoInfo(&m_videoInfo, i),
- EGL_DMA_BUF_PLANE0_FD_EXT, fd,
- EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLAttrib)offset,
- EGL_DMA_BUF_PLANE0_PITCH_EXT, stride,
- EGL_NONE
- };
- EGLImage image = eglCreateImage(eglDisplay,
- EGL_NO_CONTEXT,
- EGL_LINUX_DMA_BUF_EXT,
- nullptr,
- attribute_list);
- if (image == EGL_NO_IMAGE_KHR) {
- qWarning() << "could not create EGL image for plane" << i << Qt::hex << eglGetError();
- }
-// qDebug() << Qt::hex << "eglCreateImage: glerror" << glGetError() << "egl error" << eglGetError();
- functions.glBindTexture(GL_TEXTURE_2D, m_textures[i]);
-// qDebug() << Qt::hex << "bind texture: glerror" << glGetError() << "egl error" << eglGetError();
- auto EGLImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglImageTargetTexture2D;
- EGLImageTargetTexture2D(GL_TEXTURE_2D, image);
-// qDebug() << Qt::hex << "glerror" << glGetError() << "egl error" << eglGetError();
- eglDestroyImage(eglDisplay, image);
+ auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
+ auto glContext = nativeHandles->context;
+ if (!glContext) {
+ qWarning() << "no GL context";
+ return {};
+ }
+
+ if (!gst_video_frame_map(&frame, &videoInfo, buffer, GstMapFlags(GST_MAP_READ))) {
+ qDebug() << "Couldn't map DMA video frame";
+ return {};
+ }
+
+ GlTextures textures = {};
+ textures.owned = true;
+ textures.count = GST_VIDEO_FRAME_N_PLANES(&frame);
+ // int width = GST_VIDEO_FRAME_WIDTH(&frame);
+ // int height = GST_VIDEO_FRAME_HEIGHT(&frame);
+ Q_ASSERT(GST_VIDEO_FRAME_N_PLANES(&frame) == gst_buffer_n_memory(buffer));
+
+ QOpenGLFunctions functions(glContext);
+ functions.glGenTextures(int(textures.count), textures.names.data());
+
+ // qDebug() << Qt::hex << "glGenTextures: glerror" << glGetError() << "egl error" << eglGetError();
+ // qDebug() << "converting DMA buffer nPlanes=" << nPlanes << m_textures[0] << m_textures[1] << m_textures[2];
+
+ for (int i = 0; i < int(textures.count); ++i) {
+ auto offset = GST_VIDEO_FRAME_PLANE_OFFSET(&frame, i);
+ auto stride = GST_VIDEO_FRAME_PLANE_STRIDE(&frame, i);
+ int planeWidth = GST_VIDEO_FRAME_COMP_WIDTH(&frame, i);
+ int planeHeight = GST_VIDEO_FRAME_COMP_HEIGHT(&frame, i);
+ auto mem = gst_buffer_peek_memory(buffer, i);
+ int fd = gst_dmabuf_memory_get_fd(mem);
+
+ // qDebug() << " plane" << i << "size" << width << height << "stride" << stride << "offset" << offset << "fd=" << fd;
+ // ### do we need to open/close the fd?
+ // ### can we convert several planes at once?
+ // Get the correct DRM_FORMATs from the texture format in the description
+ EGLAttrib const attribute_list[] = {
+ EGL_WIDTH, planeWidth,
+ EGL_HEIGHT, planeHeight,
+ EGL_LINUX_DRM_FOURCC_EXT, fourccFromVideoInfo(&videoInfo, i),
+ EGL_DMA_BUF_PLANE0_FD_EXT, fd,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLAttrib)offset,
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, stride,
+ EGL_NONE
+ };
+ EGLImage image = eglCreateImage(eglDisplay,
+ EGL_NO_CONTEXT,
+ EGL_LINUX_DMA_BUF_EXT,
+ nullptr,
+ attribute_list);
+ if (image == EGL_NO_IMAGE_KHR) {
+ qWarning() << "could not create EGL image for plane" << i << Qt::hex << eglGetError();
}
- gst_video_frame_unmap(&m_frame);
+ // qDebug() << Qt::hex << "eglCreateImage: glerror" << glGetError() << "egl error" << eglGetError();
+ functions.glBindTexture(GL_TEXTURE_2D, textures.names[i]);
+ // qDebug() << Qt::hex << "bind texture: glerror" << glGetError() << "egl error" << eglGetError();
+ auto EGLImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglImageTargetTexture2D;
+ EGLImageTargetTexture2D(GL_TEXTURE_2D, image);
+ // qDebug() << Qt::hex << "glerror" << glGetError() << "egl error" << eglGetError();
+ eglDestroyImage(eglDisplay, image);
}
+ gst_video_frame_unmap(&frame);
+
+ return textures;
+}
#endif
#endif
- m_texturesUploaded = true;
-}
-std::unique_ptr<QRhiTexture> QGstVideoBuffer::texture(int plane) const
+std::unique_ptr<QVideoFrameTextures> QGstVideoBuffer::mapTextures(QRhi *rhi)
{
- auto desc = QVideoTextureHelper::textureDescription(m_frameFormat.pixelFormat());
- if (!m_rhi || !desc || plane >= desc->nplanes)
+ if (!rhi)
return {};
- QSize size(desc->widthForPlane(m_videoInfo.width, plane), desc->heightForPlane(m_videoInfo.height, plane));
- std::unique_ptr<QRhiTexture> tex(m_rhi->newTexture(desc->textureFormat[plane], size, 1, {}));
- if (tex) {
- if (!tex->createFrom({m_textures[plane], 0}))
- return {};
- }
- return tex;
+
+#if QT_CONFIG(gstreamer_gl)
+ GlTextures textures = {};
+ if (memoryFormat == QGstCaps::GLTexture)
+ textures = mapFromGlTexture(m_buffer, m_frame, m_videoInfo);
+
+# if GST_GL_HAVE_PLATFORM_EGL && QT_CONFIG(linux_dmabuf)
+ else if (memoryFormat == QGstCaps::DMABuf)
+ textures = mapFromDmaBuffer(m_rhi, m_buffer, m_frame, m_videoInfo, eglDisplay,
+ eglImageTargetTexture2D);
+
+# endif
+ if (textures.count > 0)
+ return std::make_unique<QGstQVideoFrameTextures>(rhi, QSize{m_videoInfo.width, m_videoInfo.height},
+ m_frameFormat.pixelFormat(), textures);
+#endif
+ return {};
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstvideobuffer_p.h b/src/plugins/multimedia/gstreamer/common/qgstvideobuffer_p.h
index 09dc8b992..151927d3d 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstvideobuffer_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstvideobuffer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTVIDEOBUFFER_P_H
#define QGSTVIDEOBUFFER_P_H
@@ -55,7 +19,7 @@
#include <private/qabstractvideobuffer_p.h>
#include <QtCore/qvariant.h>
-#include <qgst_p.h>
+#include <common/qgst_p.h>
#include <gst/video/video.h>
QT_BEGIN_NAMESPACE
@@ -63,40 +27,30 @@ class QVideoFrameFormat;
class QGstreamerVideoSink;
class QOpenGLContext;
-class Q_MULTIMEDIA_EXPORT QGstVideoBuffer : public QAbstractVideoBuffer
+class QGstVideoBuffer final : public QAbstractVideoBuffer
{
public:
-
- QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, QGstreamerVideoSink *sink,
+ QGstVideoBuffer(QGstBufferHandle buffer, const GstVideoInfo &info, QGstreamerVideoSink *sink,
const QVideoFrameFormat &frameFormat, QGstCaps::MemoryFormat format);
- QGstVideoBuffer(GstBuffer *buffer, const QVideoFrameFormat &format, const GstVideoInfo &info)
- : QGstVideoBuffer(buffer, info, nullptr, format, QGstCaps::CpuMemory)
- {}
~QGstVideoBuffer();
- GstBuffer *buffer() const { return m_buffer; }
QVideoFrame::MapMode mapMode() const override;
MapData map(QVideoFrame::MapMode mode) override;
void unmap() override;
- void mapTextures() override;
- std::unique_ptr<QRhiTexture> texture(int plane) const override;
+ std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *) override;
+
private:
- QGstCaps::MemoryFormat memoryFormat = QGstCaps::CpuMemory;
- QVideoFrameFormat m_frameFormat;
+ const QGstCaps::MemoryFormat memoryFormat = QGstCaps::CpuMemory;
+ const QVideoFrameFormat m_frameFormat;
QRhi *m_rhi = nullptr;
mutable GstVideoInfo m_videoInfo;
- mutable GstVideoFrame m_frame;
- GstBuffer *m_buffer = nullptr;
- GstBuffer *m_syncBuffer = nullptr;
+ mutable GstVideoFrame m_frame{};
+ const QGstBufferHandle m_buffer;
QVideoFrame::MapMode m_mode = QVideoFrame::NotMapped;
- QOpenGLContext *glContext = nullptr;
Qt::HANDLE eglDisplay = nullptr;
QFunctionPointer eglImageTargetTexture2D = nullptr;
- uint m_textures[3] = {};
- bool m_texturesUploaded = false;
- bool m_ownTextures = false;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp b/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp
index 1a8107889..4e9619b11 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink.cpp
@@ -1,64 +1,30 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qvideoframe.h>
-#include <qvideosink.h>
-#include <QDebug>
-#include <QMap>
-#include <QThread>
-#include <QEvent>
-#include <QCoreApplication>
-
-#include <private/qfactoryloader_p.h>
-#include "qgstvideobuffer_p.h"
-#include "qgstreamervideosink_p.h"
+// Copyright (C) 2016 Jolla Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgstvideorenderersink_p.h"
+#include <QtMultimedia/qvideoframe.h>
+#include <QtMultimedia/qvideosink.h>
+#include <QtCore/private/qfactoryloader_p.h>
+#include <QtCore/private/quniquehandle_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qthread.h>
+#include <QtGui/qevent.h>
+
+#include <common/qgstvideobuffer_p.h>
+#include <common/qgstreamervideosink_p.h>
+#include <common/qgst_debug_p.h>
+#include <common/qgstutils_p.h>
+
#include <gst/video/video.h>
#include <gst/video/gstvideometa.h>
-#include <qloggingcategory.h>
-#include <qdebug.h>
-#include "qgstutils_p.h"
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#if QT_CONFIG(gstreamer_gl)
#include <gst/gl/gl.h>
#endif // #if QT_CONFIG(gstreamer_gl)
@@ -68,29 +34,20 @@
#include <gst/allocators/gstdmabuf.h>
#endif
-//#define DEBUG_VIDEO_SURFACE_SINK
-
-Q_LOGGING_CATEGORY(qLcGstVideoRenderer, "qt.multimedia.gstvideorenderer")
+static Q_LOGGING_CATEGORY(qLcGstVideoRenderer, "qt.multimedia.gstvideorenderer")
QT_BEGIN_NAMESPACE
QGstVideoRenderer::QGstVideoRenderer(QGstreamerVideoSink *sink)
- : m_sink(sink)
+ : m_sink(sink), m_surfaceCaps(createSurfaceCaps(sink))
{
- createSurfaceCaps();
}
-QGstVideoRenderer::~QGstVideoRenderer()
-{
-}
+QGstVideoRenderer::~QGstVideoRenderer() = default;
-void QGstVideoRenderer::createSurfaceCaps()
+QGstCaps QGstVideoRenderer::createSurfaceCaps([[maybe_unused]] QGstreamerVideoSink *sink)
{
- QRhi *rhi = m_sink->rhi();
- Q_UNUSED(rhi);
-
- QGstMutableCaps caps;
- caps.create();
+ QGstCaps caps = QGstCaps::create();
// All the formats that both we and gstreamer support
auto formats = QList<QVideoFrameFormat::PixelFormat>()
@@ -115,10 +72,11 @@ void QGstVideoRenderer::createSurfaceCaps()
<< QVideoFrameFormat::Format_Y16
;
#if QT_CONFIG(gstreamer_gl)
+ QRhi *rhi = sink->rhi();
if (rhi && rhi->backend() == QRhi::OpenGLES2) {
caps.addPixelFormats(formats, GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
#if QT_CONFIG(linux_dmabuf)
- if (m_sink->eglDisplay() && m_sink->eglImageTargetTexture2D()) {
+ if (sink->eglDisplay() && sink->eglImageTargetTexture2D()) {
// We currently do not handle planar DMA buffers, as it's somewhat unclear how to
// convert the planar EGLImage into something we can use from OpenGL
auto singlePlaneFormats = QList<QVideoFrameFormat::PixelFormat>()
@@ -142,31 +100,28 @@ void QGstVideoRenderer::createSurfaceCaps()
}
#endif
caps.addPixelFormats(formats);
-
- m_surfaceCaps = caps;
+ return caps;
}
-QGstMutableCaps QGstVideoRenderer::caps()
+const QGstCaps &QGstVideoRenderer::caps()
{
- QMutexLocker locker(&m_mutex);
-
return m_surfaceCaps;
}
-bool QGstVideoRenderer::start(GstCaps *caps)
+bool QGstVideoRenderer::start(const QGstCaps& caps)
{
- qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::start" << QGstCaps(caps).toString();
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::start" << caps;
QMutexLocker locker(&m_mutex);
m_frameMirrored = false;
- m_frameRotationAngle = QVideoFrame::Rotation0;
+ m_frameRotationAngle = QtVideo::Rotation::None;
if (m_active) {
m_flush = true;
m_stop = true;
}
- m_startCaps = QGstMutableCaps(caps, QGstMutableCaps::NeedsRef);
+ m_startCaps = caps;
/*
Waiting for start() to be invoked in the main thread may block
@@ -222,7 +177,7 @@ void QGstVideoRenderer::flush()
QMutexLocker locker(&m_mutex);
m_flush = true;
- m_renderBuffer = nullptr;
+ m_renderBuffer = {};
m_renderCondition.wakeAll();
notify();
@@ -234,11 +189,14 @@ GstFlowReturn QGstVideoRenderer::render(GstBuffer *buffer)
qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::render";
m_renderReturn = GST_FLOW_OK;
- m_renderBuffer = buffer;
+ m_renderBuffer = QGstBufferHandle{
+ buffer,
+ QGstBufferHandle::NeedsRef,
+ };
waitForAsyncEvent(&locker, &m_renderCondition, 300);
- m_renderBuffer = nullptr;
+ m_renderBuffer = {};
return m_renderReturn;
}
@@ -269,15 +227,28 @@ bool QGstVideoRenderer::query(GstQuery *query)
void QGstVideoRenderer::gstEvent(GstEvent *event)
{
- if (GST_EVENT_TYPE(event) != GST_EVENT_TAG)
+ switch (GST_EVENT_TYPE(event)) {
+ case GST_EVENT_TAG:
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::gstEvent: Tag";
+ return gstEventHandleTag(event);
+ case GST_EVENT_EOS:
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::gstEvent: EOS";
+ return gstEventHandleEOS(event);
+
+ default:
+ qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::gstEvent: unhandled event - " << event;
return;
+ }
+}
+void QGstVideoRenderer::gstEventHandleTag(GstEvent *event)
+{
GstTagList *taglist = nullptr;
gst_event_parse_tag(event, &taglist);
if (!taglist)
return;
- gchar *value = nullptr;
+ QGString value;
if (!gst_tag_list_get_string(taglist, GST_TAG_IMAGE_ORIENTATION, &value))
return;
@@ -289,26 +260,40 @@ void QGstVideoRenderer::gstEvent(GstEvent *event)
bool mirrored = false;
int rotationAngle = 0;
- if (!strncmp(rotate, value, rotateLen)) {
- rotationAngle = atoi(value + rotateLen);
- } else if (!strncmp(flipRotate, value, flipRotateLen)) {
+ if (!strncmp(rotate, value.get(), rotateLen)) {
+ rotationAngle = atoi(value.get() + rotateLen);
+ } else if (!strncmp(flipRotate, value.get(), flipRotateLen)) {
// To flip by horizontal axis is the same as to mirror by vertical axis
// and rotate by 180 degrees.
mirrored = true;
- rotationAngle = (180 + atoi(value + flipRotateLen)) % 360;
+ rotationAngle = (180 + atoi(value.get() + flipRotateLen)) % 360;
}
QMutexLocker locker(&m_mutex);
m_frameMirrored = mirrored;
switch (rotationAngle) {
- case 0: m_frameRotationAngle = QVideoFrame::Rotation0; break;
- case 90: m_frameRotationAngle = QVideoFrame::Rotation90; break;
- case 180: m_frameRotationAngle = QVideoFrame::Rotation180; break;
- case 270: m_frameRotationAngle = QVideoFrame::Rotation270; break;
- default: m_frameRotationAngle = QVideoFrame::Rotation0;
+ case 0:
+ m_frameRotationAngle = QtVideo::Rotation::None;
+ break;
+ case 90:
+ m_frameRotationAngle = QtVideo::Rotation::Clockwise90;
+ break;
+ case 180:
+ m_frameRotationAngle = QtVideo::Rotation::Clockwise180;
+ break;
+ case 270:
+ m_frameRotationAngle = QtVideo::Rotation::Clockwise270;
+ break;
+ default:
+ m_frameRotationAngle = QtVideo::Rotation::None;
}
}
+void QGstVideoRenderer::gstEventHandleEOS(GstEvent *)
+{
+ stop();
+}
+
bool QGstVideoRenderer::event(QEvent *event)
{
if (event->type() == QEvent::UpdateRequest) {
@@ -334,6 +319,7 @@ bool QGstVideoRenderer::handleEvent(QMutexLocker<QMutex> *locker)
if (m_sink && !m_flushed)
m_sink->setVideoFrame(QVideoFrame());
m_flushed = true;
+ locker->relock();
}
} else if (m_stop) {
m_stop = false;
@@ -346,13 +332,20 @@ bool QGstVideoRenderer::handleEvent(QMutexLocker<QMutex> *locker)
Q_ASSERT(!m_active);
auto startCaps = m_startCaps;
- m_startCaps = nullptr;
+ m_startCaps = {};
if (m_sink) {
locker->unlock();
m_flushed = true;
- m_format = startCaps.formatForCaps(&m_videoInfo);
+ auto optionalFormatAndVideoInfo = startCaps.formatAndVideoInfo();
+ if (optionalFormatAndVideoInfo) {
+ std::tie(m_format, m_videoInfo) = std::move(*optionalFormatAndVideoInfo);
+ } else {
+ m_format = {};
+ m_videoInfo = {};
+ }
+
memoryFormat = startCaps.memoryFormat();
locker->relock();
@@ -363,19 +356,17 @@ bool QGstVideoRenderer::handleEvent(QMutexLocker<QMutex> *locker)
}
} else if (m_renderBuffer) {
- GstBuffer *buffer = m_renderBuffer;
- m_renderBuffer = nullptr;
+ QGstBufferHandle buffer = std::move(m_renderBuffer);
m_renderReturn = GST_FLOW_ERROR;
qCDebug(qLcGstVideoRenderer) << "QGstVideoRenderer::handleEvent(renderBuffer)" << m_active << m_sink;
if (m_active && m_sink) {
- gst_buffer_ref(buffer);
locker->unlock();
m_flushed = false;
- auto meta = gst_buffer_get_video_crop_meta (buffer);
+ GstVideoCropMeta *meta = gst_buffer_get_video_crop_meta(buffer.get());
if (meta) {
QRect vp(meta->x, meta->y, meta->width, meta->height);
if (m_format.viewport() != vp) {
@@ -391,16 +382,14 @@ bool QGstVideoRenderer::handleEvent(QMutexLocker<QMutex> *locker)
} else {
QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, m_videoInfo, m_sink, m_format, memoryFormat);
QVideoFrame frame(videoBuffer, m_format);
- QGstUtils::setFrameTimeStamps(&frame, buffer);
+ QGstUtils::setFrameTimeStampsFromBuffer(&frame, buffer.get());
frame.setMirrored(m_frameMirrored);
- frame.setRotationAngle(m_frameRotationAngle);
+ frame.setRotation(m_frameRotationAngle);
qCDebug(qLcGstVideoRenderer) << " sending video frame";
m_sink->setVideoFrame(frame);
}
- gst_buffer_unref(buffer);
-
locker->relock();
m_renderReturn = GST_FLOW_OK;
@@ -438,8 +427,8 @@ bool QGstVideoRenderer::waitForAsyncEvent(
return condition->wait(&m_mutex, time);
}
-static GstVideoSinkClass *sink_parent_class;
-static thread_local QGstreamerVideoSink *current_sink;
+static GstVideoSinkClass *gvrs_sink_parent_class;
+static thread_local QGstreamerVideoSink *gvrs_current_sink;
#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast<QGstVideoRendererSink *>(s))
@@ -456,36 +445,27 @@ QGstVideoRendererSink *QGstVideoRendererSink::createSink(QGstreamerVideoSink *si
void QGstVideoRendererSink::setSink(QGstreamerVideoSink *sink)
{
- current_sink = sink;
- get_type();
+ gvrs_current_sink = sink;
}
GType QGstVideoRendererSink::get_type()
{
- static GType type = 0;
-
- if (type == 0) {
- static const GTypeInfo info =
- {
- sizeof(QGstVideoRendererSinkClass), // class_size
- base_init, // base_init
- nullptr, // base_finalize
- class_init, // class_init
- nullptr, // class_finalize
- nullptr, // class_data
- sizeof(QGstVideoRendererSink), // instance_size
- 0, // n_preallocs
- instance_init, // instance_init
- nullptr // value_table
- };
-
- type = g_type_register_static(
- GST_TYPE_VIDEO_SINK, "QGstVideoRendererSink", &info, GTypeFlags(0));
-
- // Register the sink type to be used in custom piplines.
- // When surface is ready the sink can be used.
- gst_element_register(nullptr, "qtvideosink", GST_RANK_PRIMARY, type);
- }
+ static const GTypeInfo info =
+ {
+ sizeof(QGstVideoRendererSinkClass), // class_size
+ base_init, // base_init
+ nullptr, // base_finalize
+ class_init, // class_init
+ nullptr, // class_finalize
+ nullptr, // class_data
+ sizeof(QGstVideoRendererSink), // instance_size
+ 0, // n_preallocs
+ instance_init, // instance_init
+ nullptr // value_table
+ };
+
+ static const GType type = g_type_register_static(GST_TYPE_VIDEO_SINK, "QGstVideoRendererSink",
+ &info, GTypeFlags(0));
return type;
}
@@ -494,7 +474,7 @@ void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data)
{
Q_UNUSED(class_data);
- sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class));
+ gvrs_sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class));
GstVideoSinkClass *video_sink_class = reinterpret_cast<GstVideoSinkClass *>(g_class);
video_sink_class->show_frame = QGstVideoRendererSink::show_frame;
@@ -538,11 +518,11 @@ void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_cl
Q_UNUSED(g_class);
VO_SINK(instance);
- Q_ASSERT(current_sink);
+ Q_ASSERT(gvrs_current_sink);
- sink->renderer = new QGstVideoRenderer(current_sink);
- sink->renderer->moveToThread(current_sink->thread());
- current_sink = nullptr;
+ sink->renderer = new QGstVideoRenderer(gvrs_current_sink);
+ sink->renderer->moveToThread(gvrs_current_sink->thread());
+ gvrs_current_sink = nullptr;
}
void QGstVideoRendererSink::finalize(GObject *object)
@@ -552,7 +532,7 @@ void QGstVideoRendererSink::finalize(GObject *object)
delete sink->renderer;
// Chain up
- G_OBJECT_CLASS(sink_parent_class)->finalize(object);
+ G_OBJECT_CLASS(gvrs_sink_parent_class)->finalize(object);
}
void QGstVideoRendererSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d)
@@ -590,36 +570,33 @@ GstStateChangeReturn QGstVideoRendererSink::change_state(
if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame)
sink->renderer->flush();
- return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition);
+ return GST_ELEMENT_CLASS(gvrs_sink_parent_class)->change_state(element, transition);
}
GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter)
{
VO_SINK(base);
- QGstMutableCaps caps = sink->renderer->caps();
+ QGstCaps caps = sink->renderer->caps();
if (filter)
- caps = gst_caps_intersect(caps.get(), filter);
+ caps = QGstCaps(gst_caps_intersect(caps.caps(), filter), QGstCaps::HasRef);
- gst_caps_ref(caps.get());
- return caps.get();
+ return caps.release();
}
-gboolean QGstVideoRendererSink::set_caps(GstBaseSink *base, GstCaps *caps)
+gboolean QGstVideoRendererSink::set_caps(GstBaseSink *base, GstCaps *gcaps)
{
VO_SINK(base);
+ auto caps = QGstCaps(gcaps, QGstCaps::NeedsRef);
- qCDebug(qLcGstVideoRenderer) << "set_caps:" << QGstCaps(caps).toString();
+ qCDebug(qLcGstVideoRenderer) << "set_caps:" << caps;
- if (!caps) {
+ if (caps.isNull()) {
sink->renderer->stop();
-
return TRUE;
- } else if (sink->renderer->start(caps)) {
- return TRUE;
- } else {
- return FALSE;
}
+
+ return sink->renderer->start(caps);
}
gboolean QGstVideoRendererSink::propose_allocation(GstBaseSink *base, GstQuery *query)
@@ -654,14 +631,14 @@ gboolean QGstVideoRendererSink::query(GstBaseSink *base, GstQuery *query)
if (sink->renderer->query(query))
return TRUE;
- return GST_BASE_SINK_CLASS(sink_parent_class)->query(base, query);
+ return GST_BASE_SINK_CLASS(gvrs_sink_parent_class)->query(base, query);
}
gboolean QGstVideoRendererSink::event(GstBaseSink *base, GstEvent * event)
{
VO_SINK(base);
sink->renderer->gstEvent(event);
- return GST_BASE_SINK_CLASS(sink_parent_class)->event(base, event);
+ return GST_BASE_SINK_CLASS(gvrs_sink_parent_class)->event(base, event);
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink_p.h b/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink_p.h
index 18530f62e..99d1b0ac8 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink_p.h
+++ b/src/plugins/multimedia/gstreamer/common/qgstvideorenderersink_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Jolla Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Jolla Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTVIDEORENDERERSINK_P_H
#define QGSTVIDEORENDERERSINK_P_H
@@ -62,22 +26,21 @@
#include <QtCore/qwaitcondition.h>
#include <qvideoframeformat.h>
#include <qvideoframe.h>
-#include <qgstvideobuffer_p.h>
-#include <qgst_p.h>
+#include <common/qgstvideobuffer_p.h>
+#include <common/qgst_p.h>
QT_BEGIN_NAMESPACE
class QVideoSink;
class QGstVideoRenderer : public QObject
{
- Q_OBJECT
public:
- QGstVideoRenderer(QGstreamerVideoSink *sink);
+ explicit QGstVideoRenderer(QGstreamerVideoSink *sink);
~QGstVideoRenderer();
- QGstMutableCaps caps();
+ const QGstCaps &caps();
- bool start(GstCaps *caps);
+ bool start(const QGstCaps& caps);
void stop();
void unlock();
bool proposeAllocation(GstQuery *query);
@@ -96,7 +59,10 @@ private slots:
private:
void notify();
bool waitForAsyncEvent(QMutexLocker<QMutex> *locker, QWaitCondition *condition, unsigned long time);
- void createSurfaceCaps();
+ static QGstCaps createSurfaceCaps(QGstreamerVideoSink *);
+
+ void gstEventHandleTag(GstEvent *);
+ void gstEventHandleEOS(GstEvent *);
QPointer<QGstreamerVideoSink> m_sink;
@@ -108,28 +74,28 @@ private:
GstFlowReturn m_renderReturn = GST_FLOW_OK;
bool m_active = false;
- QGstMutableCaps m_surfaceCaps;
+ const QGstCaps m_surfaceCaps;
- QGstMutableCaps m_startCaps;
- GstBuffer *m_renderBuffer = nullptr;
+ QGstCaps m_startCaps;
+ QGstBufferHandle m_renderBuffer;
bool m_notified = false;
bool m_stop = false;
bool m_flush = false;
bool m_frameMirrored = false;
- QVideoFrame::RotationAngle m_frameRotationAngle = QVideoFrame::Rotation0;
+ QtVideo::Rotation m_frameRotationAngle = QtVideo::Rotation::None;
// --- only accessed from one thread
QVideoFrameFormat m_format;
- GstVideoInfo m_videoInfo;
+ GstVideoInfo m_videoInfo{};
bool m_flushed = true;
QGstCaps::MemoryFormat memoryFormat = QGstCaps::CpuMemory;
};
-class Q_MULTIMEDIA_EXPORT QGstVideoRendererSink
+class QGstVideoRendererSink
{
public:
- GstVideoSink parent;
+ GstVideoSink parent{};
static QGstVideoRendererSink *createSink(QGstreamerVideoSink *surface);
static void setSink(QGstreamerVideoSink *surface);
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp
index 24f4301ee..887f74b12 100644
--- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp
@@ -1,78 +1,63 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qcameradevice.h>
-
-#include "qgstreamercamera_p.h"
-#include "qgstreamerimagecapture_p.h"
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <mediacapture/qgstreamercamera_p.h>
+
+#include <QtMultimedia/qcameradevice.h>
+#include <QtMultimedia/qmediacapturesession.h>
+#include <QtCore/qdebug.h>
+
+#include <common/qgst_debug_p.h>
#include <qgstreamervideodevices_p.h>
#include <qgstreamerintegration_p.h>
-#include <qmediacapturesession.h>
#if QT_CONFIG(linux_v4l)
#include <linux/videodev2.h>
#include <private/qcore_unix_p.h>
#endif
-#include <QtCore/qdebug.h>
-QGstreamerCamera::QGstreamerCamera(QCamera *camera)
- : QPlatformCamera(camera)
+QT_BEGIN_NAMESPACE
+
+QMaybe<QPlatformCamera *> QGstreamerCamera::create(QCamera *camera)
+{
+ QGstElement videotestsrc = QGstElement::createFromFactory("videotestsrc");
+ if (!videotestsrc)
+ return errorMessageCannotFindElement("videotestsrc");
+
+ QGstElement capsFilter = QGstElement::createFromFactory("capsfilter", "videoCapsFilter");
+ if (!capsFilter)
+ return errorMessageCannotFindElement("capsfilter");
+
+ QGstElement videoconvert = QGstElement::createFromFactory("videoconvert", "videoConvert");
+ if (!videoconvert)
+ return errorMessageCannotFindElement("videoconvert");
+
+ QGstElement videoscale = QGstElement::createFromFactory("videoscale", "videoScale");
+ if (!videoscale)
+ return errorMessageCannotFindElement("videoscale");
+
+ return new QGstreamerCamera(videotestsrc, capsFilter, videoconvert, videoscale, camera);
+}
+
+QGstreamerCamera::QGstreamerCamera(QGstElement videotestsrc, QGstElement capsFilter,
+ QGstElement videoconvert, QGstElement videoscale,
+ QCamera *camera)
+ : QPlatformCamera(camera),
+ gstCamera(std::move(videotestsrc)),
+ gstCapsFilter(std::move(capsFilter)),
+ gstVideoConvert(std::move(videoconvert)),
+ gstVideoScale(std::move(videoscale))
{
- gstCamera = QGstElement("videotestsrc");
- gstCapsFilter = QGstElement("capsfilter", "videoCapsFilter");
- gstDecode = QGstElement("identity");
- gstVideoConvert = QGstElement("videoconvert", "videoConvert");
- gstVideoScale = QGstElement("videoscale", "videoScale");
- gstCameraBin = QGstBin("camerabin");
+ gstDecode = QGstElement::createFromFactory("identity");
+ gstCameraBin = QGstBin::create("camerabin");
gstCameraBin.add(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
- gstCamera.link(gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
+ qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale);
gstCameraBin.addGhostPad(gstVideoScale, "src");
}
QGstreamerCamera::~QGstreamerCamera()
{
-#if QT_CONFIG(linux_v4l)
- if (v4l2FileDescriptor >= 0)
- qt_safe_close(v4l2FileDescriptor);
- v4l2FileDescriptor = -1;
-#endif
gstCameraBin.setStateSync(GST_STATE_NULL);
}
@@ -95,6 +80,8 @@ void QGstreamerCamera::setActive(bool active)
void QGstreamerCamera::setCamera(const QCameraDevice &camera)
{
+ using namespace Qt::Literals;
+
if (m_cameraDevice == camera)
return;
@@ -102,48 +89,45 @@ void QGstreamerCamera::setCamera(const QCameraDevice &camera)
QGstElement gstNewCamera;
if (camera.isNull()) {
- gstNewCamera = QGstElement("videotestsrc");
+ gstNewCamera = QGstElement::createFromFactory("videotestsrc");
} else {
auto *integration = static_cast<QGstreamerIntegration *>(QGstreamerIntegration::instance());
- auto *device = integration->videoDevice(camera.id());
- gstNewCamera = gst_device_create_element(device, "camerasrc");
- QGstStructure properties = gst_device_get_properties(device);
- if (properties.name() == "v4l2deviceprovider")
- m_v4l2Device = QString::fromUtf8(properties["device.path"].toString());
- }
-
- QCameraFormat f = findBestCameraFormat(camera);
- auto caps = QGstMutableCaps::fromCameraFormat(f);
- auto gstNewDecode = QGstElement(f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity");
-
- gstCamera.unlink(gstCapsFilter);
- gstCapsFilter.unlink(gstDecode);
- gstDecode.unlink(gstVideoConvert);
+ GstDevice *device = integration->videoDevice(camera.id());
- gstCameraBin.remove(gstCamera);
- gstCameraBin.remove(gstDecode);
+ if (!device) {
+ emit error(QCamera::Error::CameraError,
+ u"Failed to create GstDevice for camera: "_s
+ + QString::fromUtf8(camera.id()));
+ return;
+ }
- gstCamera.setStateSync(GST_STATE_NULL);
- gstDecode.setStateSync(GST_STATE_NULL);
+ gstNewCamera = QGstElement::createFromDevice(device, "camerasrc");
+ if (QGstStructure properties = gst_device_get_properties(device); !properties.isNull()) {
+ if (properties.name() == "v4l2deviceprovider")
+ m_v4l2DevicePath = QString::fromUtf8(properties["device.path"].toString());
+ properties.free();
+ }
+ }
- gstCapsFilter.set("caps", caps);
+ QCameraFormat f = findBestCameraFormat(camera);
+ auto caps = QGstCaps::fromCameraFormat(f);
+ auto gstNewDecode = QGstElement::createFromFactory(
+ f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity");
- gstCameraBin.add(gstNewCamera, gstNewDecode);
+ QGstPipeline::modifyPipelineWhileNotRunning(gstCamera.getPipeline(), [&] {
+ qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
+ gstCameraBin.stopAndRemoveElements(gstCamera, gstDecode);
- gstNewDecode.link(gstVideoConvert);
- gstCapsFilter.link(gstNewDecode);
+ gstCapsFilter.set("caps", caps);
- if (!gstNewCamera.link(gstCapsFilter))
- qWarning() << "linking camera failed" << gstCamera.name() << caps.toString();
+ gstCamera = std::move(gstNewCamera);
+ gstDecode = std::move(gstNewDecode);
- // Start sending frames once pipeline is linked
- // FIXME: put camera to READY state before linking to decoder as in the NULL state it does not know its true caps
- gstCapsFilter.syncStateWithParent();
- gstNewDecode.syncStateWithParent();
- gstNewCamera.syncStateWithParent();
+ gstCameraBin.add(gstCamera, gstDecode);
+ qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
- gstCamera = gstNewCamera;
- gstDecode = gstNewDecode;
+ gstCameraBin.syncChildrenState();
+ });
updateCameraProperties();
}
@@ -157,29 +141,25 @@ bool QGstreamerCamera::setCameraFormat(const QCameraFormat &format)
if (f.isNull())
f = findBestCameraFormat(m_cameraDevice);
- auto caps = QGstMutableCaps::fromCameraFormat(f);
+ auto caps = QGstCaps::fromCameraFormat(f);
- auto newGstDecode = QGstElement(f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity");
- gstCameraBin.add(newGstDecode);
- newGstDecode.syncStateWithParent();
+ auto newGstDecode = QGstElement::createFromFactory(
+ f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity");
- gstCamera.staticPad("src").doInIdleProbe([&](){
- gstCamera.unlink(gstCapsFilter);
- gstCapsFilter.unlink(gstDecode);
- gstDecode.unlink(gstVideoConvert);
+ QGstPipeline::modifyPipelineWhileNotRunning(gstCamera.getPipeline(), [&] {
+ newGstDecode.syncStateWithParent();
- gstCapsFilter.set("caps", caps);
+ qUnlinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
+ gstCameraBin.stopAndRemoveElements(gstDecode);
- newGstDecode.link(gstVideoConvert);
- gstCapsFilter.link(newGstDecode);
- if (!gstCamera.link(gstCapsFilter))
- qWarning() << "linking filtered camera to decoder failed" << gstCamera.name() << caps.toString();
- });
+ gstCapsFilter.set("caps", caps);
- gstCameraBin.remove(gstDecode);
- gstDecode.setStateSync(GST_STATE_NULL);
+ gstDecode = std::move(newGstDecode);
- gstDecode = newGstDecode;
+ gstCameraBin.add(gstDecode);
+ qLinkGstElements(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert);
+ gstCameraBin.syncChildrenState();
+ });
return true;
}
@@ -618,83 +598,78 @@ void QGstreamerCamera::setColorTemperature(int temperature)
}
#if QT_CONFIG(linux_v4l)
+bool QGstreamerCamera::isV4L2Camera() const
+{
+ return !m_v4l2DevicePath.isEmpty();
+}
+
void QGstreamerCamera::initV4L2Controls()
{
v4l2AutoWhiteBalanceSupported = false;
v4l2ColorTemperatureSupported = false;
- QCamera::Features features;
-
+ QCamera::Features features{};
- const QString deviceName = v4l2Device();
- Q_ASSERT(!deviceName.isEmpty());
+ Q_ASSERT(!m_v4l2DevicePath.isEmpty());
- v4l2FileDescriptor = qt_safe_open(deviceName.toLocal8Bit().constData(), O_RDONLY);
- if (v4l2FileDescriptor == -1) {
- qWarning() << "Unable to open the camera" << deviceName
- << "for read to query the parameter info:" << qt_error_string(errno);
- return;
- }
- struct v4l2_queryctrl queryControl;
- ::memset(&queryControl, 0, sizeof(queryControl));
- queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
+ withV4L2DeviceFileDescriptor([&](int fd) {
+ struct v4l2_queryctrl queryControl = {};
+ queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE;
- if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2AutoWhiteBalanceSupported = true;
- setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, true);
- }
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ v4l2AutoWhiteBalanceSupported = true;
+ setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, true);
+ }
- ::memset(&queryControl, 0, sizeof(queryControl));
- queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
- if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2MinColorTemp = queryControl.minimum;
- v4l2MaxColorTemp = queryControl.maximum;
- v4l2ColorTemperatureSupported = true;
- features |= QCamera::Feature::ColorTemperature;
- }
+ queryControl = {};
+ queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ v4l2MinColorTemp = queryControl.minimum;
+ v4l2MaxColorTemp = queryControl.maximum;
+ v4l2ColorTemperatureSupported = true;
+ features |= QCamera::Feature::ColorTemperature;
+ }
- ::memset(&queryControl, 0, sizeof(queryControl));
- queryControl.id = V4L2_CID_EXPOSURE_AUTO;
- if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2AutoExposureSupported = true;
- }
+ queryControl = {};
+ queryControl.id = V4L2_CID_EXPOSURE_AUTO;
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ v4l2AutoExposureSupported = true;
+ }
- ::memset(&queryControl, 0, sizeof(queryControl));
- queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
- if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2ManualExposureSupported = true;
- v4l2MinExposure = queryControl.minimum;
- v4l2MaxExposure = queryControl.maximum;
- features |= QCamera::Feature::ManualExposureTime;
- }
+ queryControl = {};
+ queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ v4l2ManualExposureSupported = true;
+ v4l2MinExposure = queryControl.minimum;
+ v4l2MaxExposure = queryControl.maximum;
+ features |= QCamera::Feature::ManualExposureTime;
+ }
- ::memset(&queryControl, 0, sizeof(queryControl));
- queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
- if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- v4l2MinExposureAdjustment = queryControl.minimum;
- v4l2MaxExposureAdjustment = queryControl.maximum;
- features |= QCamera::Feature::ExposureCompensation;
- }
+ queryControl = {};
+ queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ v4l2MinExposureAdjustment = queryControl.minimum;
+ v4l2MaxExposureAdjustment = queryControl.maximum;
+ features |= QCamera::Feature::ExposureCompensation;
+ }
- ::memset(&queryControl, 0, sizeof(queryControl));
- queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
- if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- queryControl.id = V4L2_CID_ISO_SENSITIVITY;
- if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) {
- features |= QCamera::Feature::IsoSensitivity;
- minIsoChanged(queryControl.minimum);
- maxIsoChanged(queryControl.minimum);
+ queryControl = {};
+ queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO;
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ queryControl.id = V4L2_CID_ISO_SENSITIVITY;
+ if (::ioctl(fd, VIDIOC_QUERYCTRL, &queryControl) == 0) {
+ features |= QCamera::Feature::IsoSensitivity;
+ minIsoChanged(queryControl.minimum);
+ maxIsoChanged(queryControl.minimum);
+ }
}
- }
+ });
supportedFeaturesChanged(features);
}
int QGstreamerCamera::setV4L2ColorTemperature(int temperature)
{
- struct v4l2_control control;
- ::memset(&control, 0, sizeof(control));
-
if (v4l2AutoWhiteBalanceSupported) {
setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ? true : false);
} else if (temperature == 0) {
@@ -714,22 +689,32 @@ int QGstreamerCamera::setV4L2ColorTemperature(int temperature)
bool QGstreamerCamera::setV4L2Parameter(quint32 id, qint32 value)
{
- struct v4l2_control control{id, value};
- if (::ioctl(v4l2FileDescriptor, VIDIOC_S_CTRL, &control) != 0) {
- qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value << qt_error_string(errno);
- return false;
- }
- return true;
+ return withV4L2DeviceFileDescriptor([&](int fd) {
+ v4l2_control control{ id, value };
+ if (::ioctl(fd, VIDIOC_S_CTRL, &control) != 0) {
+ qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value
+ << qt_error_string(errno);
+ return false;
+ }
+ return true;
+ });
}
int QGstreamerCamera::getV4L2Parameter(quint32 id) const
{
- struct v4l2_control control{id, 0};
- if (::ioctl(v4l2FileDescriptor, VIDIOC_G_CTRL, &control) != 0) {
- qWarning() << "Unable to get the V4L2 Parameter" << Qt::hex << id << qt_error_string(errno);
- return 0;
- }
- return control.value;
+ return withV4L2DeviceFileDescriptor([&](int fd) {
+ v4l2_control control{ id, 0 };
+ if (::ioctl(fd, VIDIOC_G_CTRL, &control) != 0) {
+ qWarning() << "Unable to get the V4L2 Parameter" << Qt::hex << id
+ << qt_error_string(errno);
+ return 0;
+ }
+ return control.value;
+ });
}
#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qgstreamercamera_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h
index ac57eb5ef..74f12f918 100644
--- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera_p.h
@@ -1,42 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERCAMERACONTROL_H
#define QGSTREAMERCAMERACONTROL_H
@@ -52,10 +15,12 @@
// We mean it.
//
-#include <QHash>
#include <private/qplatformcamera_p.h>
-#include "qgstreamermediacapture_p.h"
-#include <qgst_p.h>
+#include <private/qmultimediautils_p.h>
+
+#include <mediacapture/qgstreamermediacapture_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
QT_BEGIN_NAMESPACE
@@ -63,7 +28,8 @@ class QGstreamerCamera : public QPlatformCamera
{
Q_OBJECT
public:
- QGstreamerCamera(QCamera *camera);
+ static QMaybe<QPlatformCamera *> create(QCamera *camera);
+
virtual ~QGstreamerCamera();
bool isActive() const override;
@@ -72,7 +38,7 @@ public:
void setCamera(const QCameraDevice &camera) override;
bool setCameraFormat(const QCameraFormat &format) override;
- QGstElement gstElement() const { return gstCameraBin.element(); }
+ QGstElement gstElement() const { return gstCameraBin; }
#if QT_CONFIG(gstreamer_photography)
GstPhotography *photography() const;
#endif
@@ -96,12 +62,14 @@ public:
void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override;
void setColorTemperature(int temperature) override;
- QString v4l2Device() const { return m_v4l2Device; }
- bool isV4L2Camera() const { return !m_v4l2Device.isEmpty(); }
-
private:
+ QGstreamerCamera(QGstElement videotestsrc, QGstElement capsFilter, QGstElement videoconvert,
+ QGstElement videoscale, QCamera *camera);
+
void updateCameraProperties();
+
#if QT_CONFIG(linux_v4l)
+ bool isV4L2Camera() const;
void initV4L2Controls();
int setV4L2ColorTemperature(int temperature);
bool setV4L2Parameter(quint32 id, qint32 value);
@@ -117,7 +85,29 @@ private:
qint32 v4l2MaxExposure = 0;
qint32 v4l2MinExposureAdjustment = 0;
qint32 v4l2MaxExposureAdjustment = 0;
- int v4l2FileDescriptor = -1;
+
+ template <typename Functor>
+ auto withV4L2DeviceFileDescriptor(Functor &&f) const
+ {
+ using ReturnType = std::invoke_result_t<Functor, int>;
+ Q_ASSERT(isV4L2Camera());
+
+ if (int gstreamerDeviceFd = gstCamera.getInt("device-fd"); gstreamerDeviceFd != -1)
+ return f(gstreamerDeviceFd);
+
+ auto v4l2FileDescriptor = QFileDescriptorHandle{
+ qt_safe_open(m_v4l2DevicePath.toLocal8Bit().constData(), O_RDONLY),
+ };
+ if (!v4l2FileDescriptor) {
+ qWarning() << "Unable to open the camera" << m_v4l2DevicePath
+ << "for read to query the parameter info:" << qt_error_string(errno);
+ if constexpr (std::is_void_v<ReturnType>)
+ return;
+ else
+ return ReturnType{};
+ }
+ return f(v4l2FileDescriptor.get());
+ }
#endif
QCameraDevice m_cameraDevice;
@@ -128,9 +118,10 @@ private:
QGstElement gstDecode;
QGstElement gstVideoConvert;
QGstElement gstVideoScale;
+ QGstPipeline gstPipeline;
bool m_active = false;
- QString m_v4l2Device;
+ QString m_v4l2DevicePath;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp
index 91e48e9c7..2f8a1d776 100644
--- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp
@@ -1,68 +1,56 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgstreamerimagecapture_p.h"
-#include <private/qplatformcamera_p.h>
-#include <private/qplatformimagecapture_p.h>
-#include <qgstvideobuffer_p.h>
-#include <qgstutils_p.h>
-#include <qgstreamermetadata_p.h>
-#include <qvideoframeformat.h>
-#include <private/qmediastoragelocation_p.h>
-#include <QtCore/QDebug>
-#include <QtCore/QDir>
-#include <qstandardpaths.h>
+#include <QtMultimedia/private/qplatformcamera_p.h>
+#include <QtMultimedia/private/qplatformimagecapture_p.h>
+#include <QtMultimedia/qvideoframeformat.h>
+#include <QtMultimedia/private/qmediastoragelocation_p.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qloggingcategory.h>
-#include <qloggingcategory.h>
+#include <common/qgstreamermetadata_p.h>
+#include <common/qgstvideobuffer_p.h>
+#include <common/qgstutils_p.h>
+
+#include <utility>
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcImageCapture, "qt.multimedia.imageCapture")
+static Q_LOGGING_CATEGORY(qLcImageCaptureGst, "qt.multimedia.imageCapture")
+
+QMaybe<QPlatformImageCapture *> QGstreamerImageCapture::create(QImageCapture *parent)
+{
+ QGstElement videoconvert =
+ QGstElement::createFromFactory("videoconvert", "imageCaptureConvert");
+ if (!videoconvert)
+ return errorMessageCannotFindElement("videoconvert");
+
+ QGstElement jpegenc = QGstElement::createFromFactory("jpegenc", "jpegEncoder");
+ if (!jpegenc)
+ return errorMessageCannotFindElement("jpegenc");
+
+ QGstElement jifmux = QGstElement::createFromFactory("jifmux", "jpegMuxer");
+ if (!jifmux)
+ return errorMessageCannotFindElement("jifmux");
+
+ return new QGstreamerImageCapture(videoconvert, jpegenc, jifmux, parent);
+}
-QGstreamerImageCapture::QGstreamerImageCapture(QImageCapture *parent)
- : QPlatformImageCapture(parent),
- QGstreamerBufferProbe(ProbeBuffers)
+QGstreamerImageCapture::QGstreamerImageCapture(QGstElement videoconvert, QGstElement jpegenc,
+ QGstElement jifmux, QImageCapture *parent)
+ : QPlatformImageCapture(parent),
+ QGstreamerBufferProbe(ProbeBuffers),
+ videoConvert(std::move(videoconvert)),
+ encoder(std::move(jpegenc)),
+ muxer(std::move(jifmux))
{
- bin = QGstBin("imageCaptureBin");
+ bin = QGstBin::create("imageCaptureBin");
- queue = QGstElement("queue", "imageCaptureQueue");
+ queue = QGstElement::createFromFactory("queue", "imageCaptureQueue");
// configures the queue to be fast, lightweight and non blocking
queue.set("leaky", 2 /*downstream*/);
queue.set("silent", true);
@@ -70,22 +58,20 @@ QGstreamerImageCapture::QGstreamerImageCapture(QImageCapture *parent)
queue.set("max-size-bytes", uint(0));
queue.set("max-size-time", quint64(0));
- videoConvert = QGstElement("videoconvert", "imageCaptureConvert");
- encoder = QGstElement("jpegenc", "jpegEncoder");
- muxer = QGstElement("jifmux", "jpegMuxer");
- sink = QGstElement("fakesink","imageCaptureSink");
+ sink = QGstElement::createFromFactory("fakesink", "imageCaptureSink");
+ filter = QGstElement::createFromFactory("capsfilter", "filter");
// imageCaptureSink do not wait for a preroll buffer when going READY -> PAUSED
// as no buffer will arrive until capture() is called
sink.set("async", false);
- bin.add(queue, videoConvert, encoder, muxer, sink);
- queue.link(videoConvert, encoder, muxer, sink);
+ bin.add(queue, filter, videoConvert, encoder, muxer, sink);
+ qLinkGstElements(queue, filter, videoConvert, encoder, muxer, sink);
bin.addGhostPad(queue, "sink");
addProbeToPad(queue.staticPad("src").pad(), false);
sink.set("signal-handoffs", true);
- g_signal_connect(sink.object(), "handoff", G_CALLBACK(&QGstreamerImageCapture::saveImageFilter), this);
+ m_handoffConnection = sink.connect("handoff", G_CALLBACK(&saveImageFilter), this);
}
QGstreamerImageCapture::~QGstreamerImageCapture()
@@ -95,12 +81,15 @@ QGstreamerImageCapture::~QGstreamerImageCapture()
bool QGstreamerImageCapture::isReadyForCapture() const
{
+ QMutexLocker guard(&m_mutex);
return m_session && !passImage && cameraActive;
}
int QGstreamerImageCapture::capture(const QString &fileName)
{
- QString path = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg"));
+ using namespace Qt::Literals;
+ QString path = QMediaStorageLocation::generateFileName(
+ fileName, QStandardPaths::PicturesLocation, u"jpg"_s);
return doCapture(path);
}
@@ -111,38 +100,39 @@ int QGstreamerImageCapture::captureToBuffer()
int QGstreamerImageCapture::doCapture(const QString &fileName)
{
- qCDebug(qLcImageCapture) << "do capture";
+ qCDebug(qLcImageCaptureGst) << "do capture";
+
+ // emit error in the next event loop,
+ // so application can associate it with returned request id.
+ auto invokeDeferred = [&](auto &&fn) {
+ QMetaObject::invokeMethod(this, std::forward<decltype(fn)>(fn), Qt::QueuedConnection);
+ };
+
+ QMutexLocker guard(&m_mutex);
if (!m_session) {
- //emit error in the next event loop,
- //so application can associate it with returned request id.
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, -1),
- Q_ARG(int, QImageCapture::ResourceError),
- Q_ARG(QString, QPlatformImageCapture::msgImageCaptureNotSet()));
-
- qCDebug(qLcImageCapture) << "error 1";
+ invokeDeferred([this] {
+ emit error(-1, QImageCapture::ResourceError,
+ QPlatformImageCapture::msgImageCaptureNotSet());
+ });
+
+ qCDebug(qLcImageCaptureGst) << "error 1";
return -1;
}
if (!m_session->camera()) {
- //emit error in the next event loop,
- //so application can associate it with returned request id.
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, -1),
- Q_ARG(int, QImageCapture::ResourceError),
- Q_ARG(QString,tr("No camera available.")));
-
- qCDebug(qLcImageCapture) << "error 2";
+ invokeDeferred([this] {
+ emit error(-1, QImageCapture::ResourceError, tr("No camera available."));
+ });
+
+ qCDebug(qLcImageCaptureGst) << "error 2";
return -1;
}
if (passImage) {
- //emit error in the next event loop,
- //so application can associate it with returned request id.
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(int, -1),
- Q_ARG(int, QImageCapture::NotReadyError),
- Q_ARG(QString, QPlatformImageCapture::msgCameraNotReady()));
-
- qCDebug(qLcImageCapture) << "error 3";
+ invokeDeferred([this] {
+ emit error(-1, QImageCapture::NotReadyError,
+ QPlatformImageCapture::msgCameraNotReady());
+ });
+
+ qCDebug(qLcImageCaptureGst) << "error 3";
return -1;
}
m_lastId++;
@@ -155,24 +145,52 @@ int QGstreamerImageCapture::doCapture(const QString &fileName)
return m_lastId;
}
+void QGstreamerImageCapture::setResolution(const QSize &resolution)
+{
+ QGstCaps padCaps = bin.staticPad("sink").currentCaps();
+ if (padCaps.isNull()) {
+ qDebug() << "Camera not ready";
+ return;
+ }
+ QGstCaps caps = padCaps.copy();
+ if (caps.isNull())
+ return;
+
+ gst_caps_set_simple(caps.caps(), "width", G_TYPE_INT, resolution.width(), "height", G_TYPE_INT,
+ resolution.height(), nullptr);
+ filter.set("caps", caps);
+}
+
bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer)
{
+ QMutexLocker guard(&m_mutex);
+
if (!passImage)
return false;
- qCDebug(qLcImageCapture) << "probe buffer";
+ qCDebug(qLcImageCaptureGst) << "probe buffer";
+
+ QGstBufferHandle bufferHandle{
+ buffer,
+ QGstBufferHandle::NeedsRef,
+ };
passImage = false;
emit readyForCaptureChanged(isReadyForCapture());
- QGstCaps caps = gst_pad_get_current_caps(bin.staticPad("sink").pad());
+ QGstCaps caps = bin.staticPad("sink").currentCaps();
+ auto memoryFormat = caps.memoryFormat();
+
GstVideoInfo previewInfo;
- gst_video_info_from_caps(&previewInfo, caps.get());
+ QVideoFrameFormat fmt;
+ auto optionalFormatAndVideoInfo = caps.formatAndVideoInfo();
+ if (optionalFormatAndVideoInfo)
+ std::tie(fmt, previewInfo) = std::move(*optionalFormatAndVideoInfo);
- auto memoryFormat = caps.memoryFormat();
- auto fmt = QGstCaps(caps).formatForCaps(&previewInfo);
auto *sink = m_session->gstreamerVideoSink();
- auto *gstBuffer = new QGstVideoBuffer(buffer, previewInfo, sink, fmt, memoryFormat);
+ auto *gstBuffer = new QGstVideoBuffer{
+ std::move(bufferHandle), previewInfo, sink, fmt, memoryFormat,
+ };
QVideoFrame frame(gstBuffer, fmt);
QImage img = frame.toImage();
if (img.isNull()) {
@@ -184,20 +202,15 @@ bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer)
emit imageExposed(imageData.id);
- qCDebug(qLcImageCapture) << "Image available!";
+ qCDebug(qLcImageCaptureGst) << "Image available!";
emit imageAvailable(imageData.id, frame);
emit imageCaptured(imageData.id, img);
QMediaMetaData metaData = this->metaData();
- metaData.insert(QMediaMetaData::Date, QDateTime::currentDateTime());
metaData.insert(QMediaMetaData::Resolution, frame.size());
imageData.metaData = metaData;
- // ensure taginject injects this metaData
- const auto &md = static_cast<const QGstreamerMetaData &>(metaData);
- md.setMetaData(muxer.element());
-
emit imageMetadataAvailable(imageData.id, metaData);
return true;
@@ -205,6 +218,7 @@ bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer)
void QGstreamerImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
{
+ QMutexLocker guard(&m_mutex);
QGstreamerMediaCapture *captureSession = static_cast<QGstreamerMediaCapture *>(session);
if (m_session == captureSession)
return;
@@ -225,71 +239,81 @@ void QGstreamerImageCapture::setCaptureSession(QPlatformMediaCaptureSession *ses
return;
}
- connect(m_session, &QPlatformMediaCaptureSession::cameraChanged, this, &QGstreamerImageCapture::onCameraChanged);
+ connect(m_session, &QPlatformMediaCaptureSession::cameraChanged, this,
+ &QGstreamerImageCapture::onCameraChanged);
onCameraChanged();
}
+void QGstreamerImageCapture::setMetaData(const QMediaMetaData &m)
+{
+ {
+ QMutexLocker guard(&m_mutex);
+ QPlatformImageCapture::setMetaData(m);
+ }
+
+ // ensure taginject injects this metaData
+ applyMetaDataToTagSetter(m, muxer);
+}
+
void QGstreamerImageCapture::cameraActiveChanged(bool active)
{
- qCDebug(qLcImageCapture) << "cameraActiveChanged" << cameraActive << active;
+ qCDebug(qLcImageCaptureGst) << "cameraActiveChanged" << cameraActive << active;
if (cameraActive == active)
return;
cameraActive = active;
- qCDebug(qLcImageCapture) << "isReady" << isReadyForCapture();
+ qCDebug(qLcImageCaptureGst) << "isReady" << isReadyForCapture();
emit readyForCaptureChanged(isReadyForCapture());
}
void QGstreamerImageCapture::onCameraChanged()
{
+ QMutexLocker guard(&m_mutex);
if (m_session->camera()) {
cameraActiveChanged(m_session->camera()->isActive());
- connect(m_session->camera(), &QPlatformCamera::activeChanged, this, &QGstreamerImageCapture::cameraActiveChanged);
+ connect(m_session->camera(), &QPlatformCamera::activeChanged, this,
+ &QGstreamerImageCapture::cameraActiveChanged);
} else {
cameraActiveChanged(false);
}
}
-gboolean QGstreamerImageCapture::saveImageFilter(GstElement *element,
- GstBuffer *buffer,
- GstPad *pad,
- void *appdata)
+gboolean QGstreamerImageCapture::saveImageFilter(GstElement *, GstBuffer *buffer, GstPad *,
+ QGstreamerImageCapture *capture)
{
- Q_UNUSED(element);
- Q_UNUSED(pad);
- QGstreamerImageCapture *capture = static_cast<QGstreamerImageCapture *>(appdata);
+ capture->saveBufferToImage(buffer);
+ return true;
+}
- capture->passImage = false;
+void QGstreamerImageCapture::saveBufferToImage(GstBuffer *buffer)
+{
+ QMutexLocker guard(&m_mutex);
+ passImage = false;
- if (capture->pendingImages.isEmpty()) {
- return true;
- }
+ if (pendingImages.isEmpty())
+ return;
- auto imageData = capture->pendingImages.dequeue();
- if (imageData.filename.isEmpty()) {
- return true;
- }
+ auto imageData = pendingImages.dequeue();
+ if (imageData.filename.isEmpty())
+ return;
- qCDebug(qLcImageCapture) << "saving image as" << imageData.filename;
+ qCDebug(qLcImageCaptureGst) << "saving image as" << imageData.filename;
QFile f(imageData.filename);
- if (f.open(QFile::WriteOnly)) {
- GstMapInfo info;
- if (gst_buffer_map(buffer, &info, GST_MAP_READ)) {
- f.write(reinterpret_cast<const char *>(info.data), info.size);
- gst_buffer_unmap(buffer, &info);
- }
- f.close();
-
- static QMetaMethod savedSignal = QMetaMethod::fromSignal(&QGstreamerImageCapture::imageSaved);
- savedSignal.invoke(capture,
- Qt::QueuedConnection,
- Q_ARG(int, imageData.id),
- Q_ARG(QString, imageData.filename));
- } else {
- qCDebug(qLcImageCapture) << " could not open image file for writing";
+ if (!f.open(QFile::WriteOnly)) {
+ qCDebug(qLcImageCaptureGst) << " could not open image file for writing";
+ return;
+ }
+
+ GstMapInfo info;
+ if (gst_buffer_map(buffer, &info, GST_MAP_READ)) {
+ f.write(reinterpret_cast<const char *>(info.data), info.size);
+ gst_buffer_unmap(buffer, &info);
}
+ f.close();
- return TRUE;
+ QMetaObject::invokeMethod(this, [this, imageData = std::move(imageData)]() mutable {
+ imageSaved(imageData.id, imageData.filename);
+ });
}
QImageEncoderSettings QGstreamerImageCapture::imageSettings() const
@@ -300,9 +324,14 @@ QImageEncoderSettings QGstreamerImageCapture::imageSettings() const
void QGstreamerImageCapture::setImageSettings(const QImageEncoderSettings &settings)
{
if (m_settings != settings) {
+ QSize resolution = settings.resolution();
+ if (m_settings.resolution() != resolution && !resolution.isEmpty())
+ setResolution(resolution);
+
m_settings = settings;
- // ###
}
}
QT_END_NAMESPACE
+
+#include "moc_qgstreamerimagecapture_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h
index 1f498fe0b..4b2220d12 100644
--- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h
@@ -1,42 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERIMAGECAPTURECONTROL_H
#define QGSTREAMERIMAGECAPTURECONTROL_H
@@ -52,13 +15,15 @@
// We mean it.
//
-#include <private/qplatformimagecapture_p.h>
-#include "qgstreamermediacapture_p.h"
-#include "qgstreamerbufferprobe_p.h"
+#include <QtMultimedia/private/qplatformimagecapture_p.h>
+#include <QtMultimedia/private/qmultimediautils_p.h>
-#include <qqueue.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qqueue.h>
-#include <qgst_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstreamerbufferprobe_p.h>
+#include <mediacapture/qgstreamermediacapture_p.h>
#include <gst/video/video.h>
QT_BEGIN_NAMESPACE
@@ -68,7 +33,7 @@ class QGstreamerImageCapture : public QPlatformImageCapture, private QGstreamerB
{
Q_OBJECT
public:
- QGstreamerImageCapture(QImageCapture *parent);
+ static QMaybe<QPlatformImageCapture *> create(QImageCapture *parent);
virtual ~QGstreamerImageCapture();
bool isReadyForCapture() const override;
@@ -82,16 +47,27 @@ public:
void setCaptureSession(QPlatformMediaCaptureSession *session);
- QGstElement gstElement() const { return bin.element(); }
+ QGstElement gstElement() const { return bin; }
+
+ void setMetaData(const QMediaMetaData &m) override;
public Q_SLOTS:
void cameraActiveChanged(bool active);
void onCameraChanged();
private:
+ QGstreamerImageCapture(QGstElement videoconvert, QGstElement jpegenc, QGstElement jifmux,
+ QImageCapture *parent);
+
+ void setResolution(const QSize &resolution);
int doCapture(const QString &fileName);
- static gboolean saveImageFilter(GstElement *element, GstBuffer *buffer, GstPad *pad, void *appdata);
+ static gboolean saveImageFilter(GstElement *element, GstBuffer *buffer, GstPad *pad,
+ QGstreamerImageCapture *capture);
+ void saveBufferToImage(GstBuffer *buffer);
+
+ mutable QRecursiveMutex
+ m_mutex; // guard all elements accessed from probeBuffer/saveBufferToImage
QGstreamerMediaCapture *m_session = nullptr;
int m_lastId = 0;
QImageEncoderSettings m_settings;
@@ -106,6 +82,7 @@ private:
QGstBin bin;
QGstElement queue;
+ QGstElement filter;
QGstElement videoConvert;
QGstElement encoder;
QGstElement muxer;
@@ -114,6 +91,8 @@ private:
bool passImage = false;
bool cameraActive = false;
+
+ QGObjectHandlerScopedConnection m_handoffConnection;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp
index 15cd076e9..720ff5603 100644
--- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture.cpp
@@ -1,58 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qgstreamermediacapture_p.h"
-#include "qgstreamermediaencoder_p.h"
-#include "qgstreamerimagecapture_p.h"
-#include "qgstreamercamera_p.h"
-#include <qgstpipeline_p.h>
-
-#include "qgstreameraudioinput_p.h"
-#include "qgstreameraudiooutput_p.h"
-#include "qgstreamervideooutput_p.h"
-
-#include <qloggingcategory.h>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-QT_BEGIN_NAMESPACE
+#include <mediacapture/qgstreamermediacapture_p.h>
+#include <mediacapture/qgstreamermediaencoder_p.h>
+#include <mediacapture/qgstreamerimagecapture_p.h>
+#include <mediacapture/qgstreamercamera_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreameraudioinput_p.h>
+#include <common/qgstreameraudiooutput_p.h>
+#include <common/qgstreamervideooutput_p.h>
-Q_LOGGING_CATEGORY(qLcMediaCapture, "qt.multimedia.capture")
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/private/quniquehandle_p.h>
+QT_BEGIN_NAMESPACE
static void linkTeeToPad(QGstElement tee, QGstPad sink)
{
@@ -74,11 +35,19 @@ static void unlinkTeeFromPad(QGstElement tee, QGstPad sink)
tee.releaseRequestPad(source);
}
+QMaybe<QPlatformMediaCaptureSession *> QGstreamerMediaCapture::create()
+{
+ auto videoOutput = QGstreamerVideoOutput::create();
+ if (!videoOutput)
+ return videoOutput.error();
-QGstreamerMediaCapture::QGstreamerMediaCapture()
- : gstPipeline("pipeline")
+ return new QGstreamerMediaCapture(videoOutput.value());
+}
+
+QGstreamerMediaCapture::QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput)
+ : gstPipeline(QGstPipeline::create("mediaCapturePipeline")), gstVideoOutput(videoOutput)
{
- gstVideoOutput = new QGstreamerVideoOutput(this);
+ gstVideoOutput->setParent(this);
gstVideoOutput->setIsPreview();
gstVideoOutput->setPipeline(gstPipeline);
@@ -86,7 +55,11 @@ QGstreamerMediaCapture::QGstreamerMediaCapture()
// the clock is sourced from the elements (e.g. from an audio source).
// Since the elements are added and removed dynamically the clock would
// also change causing lost of synchronization in the pipeline.
- gst_pipeline_use_clock(gstPipeline.pipeline(), gst_system_clock_obtain());
+
+ QGstClockHandle systemClock{
+ gst_system_clock_obtain(),
+ };
+ gst_pipeline_use_clock(gstPipeline.pipeline(), systemClock.get());
// This is the recording pipeline with only live sources, thus the pipeline
// will be always in the playing state.
@@ -109,52 +82,61 @@ QPlatformCamera *QGstreamerMediaCapture::camera()
return gstCamera;
}
-void QGstreamerMediaCapture::setCamera(QPlatformCamera *camera)
+void QGstreamerMediaCapture::setCamera(QPlatformCamera *platformCamera)
{
- QGstreamerCamera *control = static_cast<QGstreamerCamera *>(camera);
- if (gstCamera == control)
+ QGstreamerCamera *camera = static_cast<QGstreamerCamera *>(platformCamera);
+ if (gstCamera == camera)
return;
if (gstCamera) {
- unlinkTeeFromPad(gstVideoTee, encoderVideoSink);
- unlinkTeeFromPad(gstVideoTee, imageCaptureSink);
+ QObject::disconnect(gstCameraActiveConnection);
+ if (gstVideoTee)
+ setCameraActive(false);
+ }
- auto camera = gstCamera->gstElement();
+ gstCamera = camera;
- gstPipeline.remove(camera);
- gstPipeline.remove(gstVideoTee);
- gstPipeline.remove(gstVideoOutput->gstElement());
+ if (gstCamera) {
+ gstCameraActiveConnection = QObject::connect(camera, &QGstreamerCamera::activeChanged, this,
+ &QGstreamerMediaCapture::setCameraActive);
+ if (gstCamera->isActive())
+ setCameraActive(true);
+ }
- camera.setStateSync(GST_STATE_NULL);
- gstVideoTee.setStateSync(GST_STATE_NULL);
- gstVideoOutput->gstElement().setStateSync(GST_STATE_NULL);
+ emit cameraChanged();
+}
- gstVideoTee = {};
- gstCamera->setCaptureSession(nullptr);
- }
+void QGstreamerMediaCapture::setCameraActive(bool activate)
+{
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (activate) {
+ QGstElement cameraElement = gstCamera->gstElement();
+ gstVideoTee = QGstElement::createFromFactory("tee", "videotee");
+ gstVideoTee.set("allow-not-linked", true);
- gstCamera = control;
- if (gstCamera) {
- QGstElement camera = gstCamera->gstElement();
- gstVideoTee = QGstElement("tee", "videotee");
- gstVideoTee.set("allow-not-linked", true);
+ gstPipeline.add(gstVideoOutput->gstElement(), cameraElement, gstVideoTee);
- gstPipeline.add(gstVideoOutput->gstElement(), camera, gstVideoTee);
+ linkTeeToPad(gstVideoTee, encoderVideoSink);
+ linkTeeToPad(gstVideoTee, gstVideoOutput->gstElement().staticPad("sink"));
+ linkTeeToPad(gstVideoTee, imageCaptureSink);
- linkTeeToPad(gstVideoTee, encoderVideoSink);
- linkTeeToPad(gstVideoTee, gstVideoOutput->gstElement().staticPad("sink"));
- linkTeeToPad(gstVideoTee, imageCaptureSink);
+ qLinkGstElements(cameraElement, gstVideoTee);
- camera.link(gstVideoTee);
+ gstPipeline.syncChildrenState();
+ } else {
+ unlinkTeeFromPad(gstVideoTee, encoderVideoSink);
+ unlinkTeeFromPad(gstVideoTee, imageCaptureSink);
- gstVideoOutput->gstElement().setState(GST_STATE_PLAYING);
- gstVideoTee.setState(GST_STATE_PLAYING);
- camera.setState(GST_STATE_PLAYING);
- }
+ auto camera = gstCamera->gstElement();
- gstPipeline.dumpGraph("camera");
+ gstPipeline.stopAndRemoveElements(camera, gstVideoTee, gstVideoOutput->gstElement());
- emit cameraChanged();
+ gstVideoTee = {};
+ gstCamera->setCaptureSession(nullptr);
+ }
+ });
+
+ gstPipeline.dumpGraph("camera");
}
QPlatformImageCapture *QGstreamerMediaCapture::imageCapture()
@@ -168,22 +150,23 @@ void QGstreamerMediaCapture::setImageCapture(QPlatformImageCapture *imageCapture
if (m_imageCapture == control)
return;
- if (m_imageCapture) {
- unlinkTeeFromPad(gstVideoTee, imageCaptureSink);
- gstPipeline.remove(m_imageCapture->gstElement());
- m_imageCapture->gstElement().setStateSync(GST_STATE_NULL);
- imageCaptureSink = {};
- m_imageCapture->setCaptureSession(nullptr);
- }
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (m_imageCapture) {
+ unlinkTeeFromPad(gstVideoTee, imageCaptureSink);
+ gstPipeline.stopAndRemoveElements(m_imageCapture->gstElement());
+ imageCaptureSink = {};
+ m_imageCapture->setCaptureSession(nullptr);
+ }
- m_imageCapture = control;
- if (m_imageCapture) {
- imageCaptureSink = m_imageCapture->gstElement().staticPad("sink");
- m_imageCapture->gstElement().setState(GST_STATE_PLAYING);
- gstPipeline.add(m_imageCapture->gstElement());
- linkTeeToPad(gstVideoTee, imageCaptureSink);
- m_imageCapture->setCaptureSession(this);
- }
+ m_imageCapture = control;
+ if (m_imageCapture) {
+ imageCaptureSink = m_imageCapture->gstElement().staticPad("sink");
+ gstPipeline.add(m_imageCapture->gstElement());
+ m_imageCapture->gstElement().syncStateWithParent();
+ linkTeeToPad(gstVideoTee, imageCaptureSink);
+ m_imageCapture->setCaptureSession(this);
+ }
+ });
gstPipeline.dumpGraph("imageCapture");
@@ -213,55 +196,59 @@ QPlatformMediaRecorder *QGstreamerMediaCapture::mediaRecorder()
void QGstreamerMediaCapture::linkEncoder(QGstPad audioSink, QGstPad videoSink)
{
- if (!gstVideoTee.isNull() && !videoSink.isNull()) {
- auto caps = gst_pad_get_current_caps(gstVideoTee.sink().pad());
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (!gstVideoTee.isNull() && !videoSink.isNull()) {
+ QGstCaps caps = gstVideoTee.sink().currentCaps();
- encoderVideoCapsFilter = QGstElement("capsfilter", "encoderVideoCapsFilter");
- encoderVideoCapsFilter.set("caps", QGstMutableCaps(caps));
+ encoderVideoCapsFilter =
+ QGstElement::createFromFactory("capsfilter", "encoderVideoCapsFilter");
+ Q_ASSERT(encoderVideoCapsFilter);
+ encoderVideoCapsFilter.set("caps", caps);
- gstPipeline.add(encoderVideoCapsFilter);
+ gstPipeline.add(encoderVideoCapsFilter);
- encoderVideoCapsFilter.src().link(videoSink);
- linkTeeToPad(gstVideoTee, encoderVideoCapsFilter.sink());
- encoderVideoCapsFilter.setState(GST_STATE_PLAYING);
- encoderVideoSink = encoderVideoCapsFilter.sink();
- }
+ encoderVideoCapsFilter.src().link(videoSink);
+ linkTeeToPad(gstVideoTee, encoderVideoCapsFilter.sink());
+ encoderVideoSink = encoderVideoCapsFilter.sink();
+ }
- if (!gstAudioTee.isNull() && !audioSink.isNull()) {
- auto caps = gst_pad_get_current_caps(gstAudioTee.sink().pad());
+ if (!gstAudioTee.isNull() && !audioSink.isNull()) {
+ QGstCaps caps = gstAudioTee.sink().currentCaps();
- encoderAudioCapsFilter = QGstElement("capsfilter", "encoderAudioCapsFilter");
- encoderAudioCapsFilter.set("caps", QGstMutableCaps(caps));
+ encoderAudioCapsFilter =
+ QGstElement::createFromFactory("capsfilter", "encoderAudioCapsFilter");
+ Q_ASSERT(encoderAudioCapsFilter);
+ encoderAudioCapsFilter.set("caps", caps);
- gstPipeline.add(encoderAudioCapsFilter);
+ gstPipeline.add(encoderAudioCapsFilter);
- encoderAudioCapsFilter.src().link(audioSink);
- linkTeeToPad(gstAudioTee, encoderAudioCapsFilter.sink());
- encoderAudioCapsFilter.setState(GST_STATE_PLAYING);
- encoderAudioSink = encoderAudioCapsFilter.sink();
- }
+ encoderAudioCapsFilter.src().link(audioSink);
+ linkTeeToPad(gstAudioTee, encoderAudioCapsFilter.sink());
+ encoderAudioSink = encoderAudioCapsFilter.sink();
+ }
+ });
}
void QGstreamerMediaCapture::unlinkEncoder()
{
- if (!encoderVideoCapsFilter.isNull()) {
- encoderVideoCapsFilter.src().unlinkPeer();
- unlinkTeeFromPad(gstVideoTee, encoderVideoCapsFilter.sink());
- gstPipeline.remove(encoderVideoCapsFilter);
- encoderVideoCapsFilter.setStateSync(GST_STATE_NULL);
- encoderVideoCapsFilter = {};
- }
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (!encoderVideoCapsFilter.isNull()) {
+ encoderVideoCapsFilter.src().unlinkPeer();
+ unlinkTeeFromPad(gstVideoTee, encoderVideoCapsFilter.sink());
+ gstPipeline.stopAndRemoveElements(encoderVideoCapsFilter);
+ encoderVideoCapsFilter = {};
+ }
- if (!encoderAudioCapsFilter.isNull()) {
- encoderAudioCapsFilter.src().unlinkPeer();
- unlinkTeeFromPad(gstAudioTee, encoderAudioCapsFilter.sink());
- gstPipeline.remove(encoderAudioCapsFilter);
- encoderAudioCapsFilter.setStateSync(GST_STATE_NULL);
- encoderAudioCapsFilter = {};
- }
+ if (!encoderAudioCapsFilter.isNull()) {
+ encoderAudioCapsFilter.src().unlinkPeer();
+ unlinkTeeFromPad(gstAudioTee, encoderAudioCapsFilter.sink());
+ gstPipeline.stopAndRemoveElements(encoderAudioCapsFilter);
+ encoderAudioCapsFilter = {};
+ }
- encoderAudioSink = {};
- encoderVideoSink = {};
+ encoderAudioSink = {};
+ encoderVideoSink = {};
+ });
}
void QGstreamerMediaCapture::setAudioInput(QPlatformAudioInput *input)
@@ -269,41 +256,39 @@ void QGstreamerMediaCapture::setAudioInput(QPlatformAudioInput *input)
if (gstAudioInput == input)
return;
- if (gstAudioInput) {
- unlinkTeeFromPad(gstAudioTee, encoderAudioSink);
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (gstAudioInput) {
+ unlinkTeeFromPad(gstAudioTee, encoderAudioSink);
- if (gstAudioOutput) {
- unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
- gstPipeline.remove(gstAudioOutput->gstElement());
- gstAudioOutput->gstElement().setStateSync(GST_STATE_NULL);
- }
+ if (gstAudioOutput) {
+ unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
+ gstPipeline.remove(gstAudioOutput->gstElement());
+ gstAudioOutput->gstElement().setStateSync(GST_STATE_NULL);
+ }
- gstPipeline.remove(gstAudioInput->gstElement());
- gstPipeline.remove(gstAudioTee);
- gstAudioInput->gstElement().setStateSync(GST_STATE_NULL);
- gstAudioTee.setStateSync(GST_STATE_NULL);
- gstAudioTee = {};
- }
+ gstPipeline.stopAndRemoveElements(gstAudioInput->gstElement(), gstAudioTee);
+ gstAudioTee = {};
+ }
- gstAudioInput = static_cast<QGstreamerAudioInput *>(input);
- if (gstAudioInput) {
- Q_ASSERT(gstAudioTee.isNull());
- gstAudioTee = QGstElement("tee", "audiotee");
- gstAudioTee.set("allow-not-linked", true);
- gstPipeline.add(gstAudioInput->gstElement(), gstAudioTee);
- gstAudioInput->gstElement().link(gstAudioTee);
+ gstAudioInput = static_cast<QGstreamerAudioInput *>(input);
+ if (gstAudioInput) {
+ Q_ASSERT(gstAudioTee.isNull());
+ gstAudioTee = QGstElement::createFromFactory("tee", "audiotee");
+ gstAudioTee.set("allow-not-linked", true);
+ gstPipeline.add(gstAudioInput->gstElement(), gstAudioTee);
+ qLinkGstElements(gstAudioInput->gstElement(), gstAudioTee);
- if (gstAudioOutput) {
- gstPipeline.add(gstAudioOutput->gstElement());
- gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
- linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
- }
+ if (gstAudioOutput) {
+ gstPipeline.add(gstAudioOutput->gstElement());
+ gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
+ linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
+ }
- gstAudioTee.setState(GST_STATE_PLAYING);
- gstAudioInput->gstElement().setStateSync(GST_STATE_PLAYING);
+ gstPipeline.syncChildrenState();
- linkTeeToPad(gstAudioTee, encoderAudioSink);
- }
+ linkTeeToPad(gstAudioTee, encoderAudioSink);
+ }
+ });
}
void QGstreamerMediaCapture::setVideoPreview(QVideoSink *sink)
@@ -316,19 +301,20 @@ void QGstreamerMediaCapture::setAudioOutput(QPlatformAudioOutput *output)
if (gstAudioOutput == output)
return;
- if (gstAudioOutput && gstAudioInput) {
- // If audio input is set, the output is in the pipeline
- unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
- gstPipeline.remove(gstAudioOutput->gstElement());
- gstAudioOutput->gstElement().setStateSync(GST_STATE_NULL);
- }
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (gstAudioOutput && gstAudioInput) {
+ // If audio input is set, the output is in the pipeline
+ unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
+ gstPipeline.stopAndRemoveElements(gstAudioOutput->gstElement());
+ }
- gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
- if (gstAudioOutput && gstAudioInput) {
- gstPipeline.add(gstAudioOutput->gstElement());
- gstAudioOutput->gstElement().setState(GST_STATE_PLAYING);
- linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
- }
+ gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output);
+ if (gstAudioOutput && gstAudioInput) {
+ gstPipeline.add(gstAudioOutput->gstElement());
+ gstPipeline.syncChildrenState();
+ linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink"));
+ }
+ });
}
QGstreamerVideoSink *QGstreamerMediaCapture::gstreamerVideoSink() const
@@ -336,5 +322,11 @@ QGstreamerVideoSink *QGstreamerMediaCapture::gstreamerVideoSink() const
return gstVideoOutput ? gstVideoOutput->gstreamerVideoSink() : nullptr;
}
+void *QGstreamerMediaCapture::nativePipeline()
+{
+ return gstPipeline.pipeline();
+}
QT_END_NAMESPACE
+
+#include "moc_qgstreamermediacapture_p.cpp"
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h
index 8a7b03f02..219773413 100644
--- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediacapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERCAPTURESERVICE_H
#define QGSTREAMERCAPTURESERVICE_H
@@ -54,8 +18,8 @@
#include <private/qplatformmediacapture_p.h>
#include <private/qplatformmediaintegration_p.h>
-#include <qgst_p.h>
-#include <qgstpipeline_p.h>
+#include <common/qgst_p.h>
+#include <common/qgstpipeline_p.h>
#include <qtimer.h>
@@ -69,12 +33,12 @@ class QGstreamerAudioOutput;
class QGstreamerVideoOutput;
class QGstreamerVideoSink;
-class QGstreamerMediaCapture : public QPlatformMediaCaptureSession
+class QGstreamerMediaCapture final : public QPlatformMediaCaptureSession
{
Q_OBJECT
public:
- QGstreamerMediaCapture();
+ static QMaybe<QPlatformMediaCaptureSession *> create();
virtual ~QGstreamerMediaCapture();
QPlatformCamera *camera() override;
@@ -99,13 +63,20 @@ public:
QGstreamerVideoSink *gstreamerVideoSink() const;
+ void *nativePipeline() override;
+
private:
+ void setCameraActive(bool activate);
+
+ explicit QGstreamerMediaCapture(QGstreamerVideoOutput *videoOutput);
+
friend QGstreamerMediaEncoder;
// Gst elements
QGstPipeline gstPipeline;
QGstreamerAudioInput *gstAudioInput = nullptr;
QGstreamerCamera *gstCamera = nullptr;
+ QMetaObject::Connection gstCameraActiveConnection;
QGstElement gstAudioTee;
QGstElement gstVideoTee;
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp
index 387ba9a71..4f9d3d364 100644
--- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp
@@ -1,63 +1,31 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qgstreamermediaencoder_p.h"
-#include "qgstreamerintegration_p.h"
-#include "qgstreamerformatinfo_p.h"
-#include "qgstpipeline_p.h"
-#include "qgstreamermessage_p.h"
-#include <private/qplatformcamera_p.h>
-#include "qaudiodevice.h"
-#include <private/qmediastoragelocation_p.h>
-
-#include <qdebug.h>
-#include <qeventloop.h>
-#include <qstandardpaths.h>
-#include <qmimetype.h>
-#include <qloggingcategory.h>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <mediacapture/qgstreamermediaencoder_p.h>
+#include <qgstreamerformatinfo_p.h>
+#include <common/qgstpipeline_p.h>
+#include <common/qgstreamermessage_p.h>
+#include <common/qgst_debug_p.h>
+#include <qgstreamerintegration_p.h>
+
+#include <QtMultimedia/private/qmediastoragelocation_p.h>
+#include <QtMultimedia/private/qplatformcamera_p.h>
+#include <QtMultimedia/qaudiodevice.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qeventloop.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qmimetype.h>
+#include <QtCore/qloggingcategory.h>
#include <gst/gsttagsetter.h>
#include <gst/gstversion.h>
#include <gst/video/video.h>
#include <gst/pbutils/encoding-profile.h>
-Q_LOGGING_CATEGORY(qLcMediaEncoder, "qt.multimedia.encoder")
+static Q_LOGGING_CATEGORY(qLcMediaEncoderGst, "qt.multimedia.encoder")
+
+QT_BEGIN_NAMESPACE
QGstreamerMediaEncoder::QGstreamerMediaEncoder(QMediaRecorder *parent)
: QPlatformMediaRecorder(parent),
@@ -65,7 +33,9 @@ QGstreamerMediaEncoder::QGstreamerMediaEncoder(QMediaRecorder *parent)
videoPauseControl(*this)
{
signalDurationChangedTimer.setInterval(100);
- signalDurationChangedTimer.callOnTimeout([this](){ durationChanged(duration()); });
+ signalDurationChangedTimer.callOnTimeout(&signalDurationChangedTimer, [this]() {
+ durationChanged(duration());
+ });
}
QGstreamerMediaEncoder::~QGstreamerMediaEncoder()
@@ -84,52 +54,60 @@ bool QGstreamerMediaEncoder::isLocationWritable(const QUrl &) const
void QGstreamerMediaEncoder::handleSessionError(QMediaRecorder::Error code, const QString &description)
{
- error(code, description);
+ updateError(code, description);
stop();
}
-bool QGstreamerMediaEncoder::processBusMessage(const QGstreamerMessage &message)
+bool QGstreamerMediaEncoder::processBusMessage(const QGstreamerMessage &msg)
{
- if (message.isNull())
- return false;
- auto msg = message;
-
-// qCDebug(qLcMediaEncoder) << "received event from" << message.source().name() << Qt::hex << message.type();
-// if (message.type() == GST_MESSAGE_STATE_CHANGED) {
-// GstState oldState;
-// GstState newState;
-// GstState pending;
-// gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
-// qCDebug(qLcMediaEncoder) << "received state change from" << message.source().name() << oldState << newState << pending;
-// }
- if (msg.type() == GST_MESSAGE_ELEMENT) {
+ constexpr bool traceStateChange = false;
+ constexpr bool traceAllEvents = false;
+
+ if constexpr (traceAllEvents)
+ qCDebug(qLcMediaEncoderGst) << "received event:" << msg;
+
+ switch (msg.type()) {
+ case GST_MESSAGE_ELEMENT: {
QGstStructure s = msg.structure();
- qCDebug(qLcMediaEncoder) << "received element message from" << msg.source().name() << s.name();
if (s.name() == "GstBinForwarded")
- msg = QGstreamerMessage(s);
- if (msg.isNull())
- return false;
+ return processBusMessage(s.getMessage());
+
+ qCDebug(qLcMediaEncoderGst)
+ << "received element message from" << msg.source().name() << s.name();
+ return false;
}
- if (msg.type() == GST_MESSAGE_EOS) {
- qCDebug(qLcMediaEncoder) << "received EOS from" << msg.source().name();
+ case GST_MESSAGE_EOS: {
+ qCDebug(qLcMediaEncoderGst) << "received EOS from" << msg.source().name();
finalize();
return false;
}
- if (msg.type() == GST_MESSAGE_ERROR) {
- GError *err;
- gchar *debug;
- gst_message_parse_error(msg.rawMessage(), &err, &debug);
- error(QMediaRecorder::ResourceError, QString::fromUtf8(err->message));
- g_error_free(err);
- g_free(debug);
+ case GST_MESSAGE_ERROR: {
+ qCDebug(qLcMediaEncoderGst)
+ << "received error:" << msg.source().name() << QCompactGstMessageAdaptor(msg);
+
+ QUniqueGErrorHandle err;
+ QGString debug;
+ gst_message_parse_error(msg.message(), &err, &debug);
+ updateError(QMediaRecorder::ResourceError, QString::fromUtf8(err.get()->message));
if (!m_finalizing)
stop();
finalize();
+ return false;
}
- return false;
+ case GST_MESSAGE_STATE_CHANGED: {
+ if constexpr (traceStateChange)
+ qCDebug(qLcMediaEncoderGst)
+ << "received state change" << QCompactGstMessageAdaptor(msg);
+
+ return false;
+ }
+
+ default:
+ return false;
+ };
}
qint64 QGstreamerMediaEncoder::duration() const
@@ -142,13 +120,13 @@ static GstEncodingContainerProfile *createContainerProfile(const QMediaEncoderSe
{
auto *formatInfo = QGstreamerIntegration::instance()->gstFormatsInfo();
- QGstMutableCaps caps = formatInfo->formatCaps(settings.fileFormat());
+ auto caps = formatInfo->formatCaps(settings.fileFormat());
- GstEncodingContainerProfile *profile = (GstEncodingContainerProfile *)gst_encoding_container_profile_new(
- "container_profile",
- (gchar *)"custom container profile",
- const_cast<GstCaps *>(caps.get()),
- nullptr); //preset
+ GstEncodingContainerProfile *profile =
+ (GstEncodingContainerProfile *)gst_encoding_container_profile_new(
+ "container_profile", (gchar *)"custom container profile",
+ const_cast<GstCaps *>(caps.caps()),
+ nullptr); // preset
return profile;
}
@@ -156,15 +134,18 @@ static GstEncodingProfile *createVideoProfile(const QMediaEncoderSettings &setti
{
auto *formatInfo = QGstreamerIntegration::instance()->gstFormatsInfo();
- QGstMutableCaps caps = formatInfo->videoCaps(settings.mediaFormat());
+ QGstCaps caps = formatInfo->videoCaps(settings.mediaFormat());
if (caps.isNull())
return nullptr;
- GstEncodingVideoProfile *profile = gst_encoding_video_profile_new(
- const_cast<GstCaps *>(caps.get()),
- nullptr,
- nullptr, //restriction
- 0); //presence
+ QSize videoResolution = settings.videoResolution();
+ if (videoResolution.isValid())
+ caps.setResolution(videoResolution);
+
+ GstEncodingVideoProfile *profile =
+ gst_encoding_video_profile_new(const_cast<GstCaps *>(caps.caps()), nullptr,
+ nullptr, // restriction
+ 0); // presence
gst_encoding_video_profile_set_pass(profile, 0);
gst_encoding_video_profile_set_variableframerate(profile, TRUE);
@@ -180,11 +161,11 @@ static GstEncodingProfile *createAudioProfile(const QMediaEncoderSettings &setti
if (caps.isNull())
return nullptr;
- GstEncodingProfile *profile = (GstEncodingProfile *)gst_encoding_audio_profile_new(
- const_cast<GstCaps *>(caps.get()),
- nullptr, //preset
- nullptr, //restriction
- 0); //presence
+ GstEncodingProfile *profile =
+ (GstEncodingProfile *)gst_encoding_audio_profile_new(const_cast<GstCaps *>(caps.caps()),
+ nullptr, // preset
+ nullptr, // restriction
+ 0); // presence
return profile;
}
@@ -281,7 +262,7 @@ void QGstreamerMediaEncoder::record(QMediaEncoderSettings &settings)
const auto hasAudio = m_session->audioInput() != nullptr;
if (!hasVideo && !hasAudio) {
- error(QMediaRecorder::ResourceError, QMediaRecorder::tr("No camera or audio input"));
+ updateError(QMediaRecorder::ResourceError, QMediaRecorder::tr("No camera or audio input"));
return;
}
@@ -292,16 +273,18 @@ void QGstreamerMediaEncoder::record(QMediaEncoderSettings &settings)
auto location = QMediaStorageLocation::generateFileName(outputLocation().toLocalFile(), primaryLocation, container);
QUrl actualSink = QUrl::fromLocalFile(QDir::currentPath()).resolved(location);
- qCDebug(qLcMediaEncoder) << "recording new video to" << actualSink;
+ qCDebug(qLcMediaEncoderGst) << "recording new video to" << actualSink;
Q_ASSERT(!actualSink.isEmpty());
- gstEncoder = QGstElement("encodebin", "encodebin");
+ gstEncoder = QGstBin::createFromFactory("encodebin", "encodebin");
+ Q_ASSERT(gstEncoder);
auto *encodingProfile = createEncodingProfile(settings);
g_object_set (gstEncoder.object(), "profile", encodingProfile, nullptr);
gst_encoding_profile_unref(encodingProfile);
- gstFileSink = QGstElement("filesink", "filesink");
+ gstFileSink = QGstElement::createFromFactory("filesink", "filesink");
+ Q_ASSERT(gstFileSink);
gstFileSink.set("location", QFile::encodeName(actualSink.toLocalFile()).constData());
gstFileSink.set("async", false);
@@ -327,14 +310,16 @@ void QGstreamerMediaEncoder::record(QMediaEncoderSettings &settings)
videoPauseControl.installOn(videoSink);
}
- gstPipeline.add(gstEncoder, gstFileSink);
- gstEncoder.link(gstFileSink);
- m_metaData.setMetaData(gstEncoder.bin());
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ gstPipeline.add(gstEncoder, gstFileSink);
+ qLinkGstElements(gstEncoder, gstFileSink);
+ applyMetaDataToTagSetter(m_metaData, gstEncoder);
- m_session->linkEncoder(audioSink, videoSink);
+ m_session->linkEncoder(audioSink, videoSink);
- gstEncoder.syncStateWithParent();
- gstFileSink.syncStateWithParent();
+ gstEncoder.syncStateWithParent();
+ gstFileSink.syncStateWithParent();
+ });
signalDurationChangedTimer.start();
gstPipeline.dumpGraph("recording");
@@ -349,6 +334,7 @@ void QGstreamerMediaEncoder::pause()
if (!m_session || m_finalizing || state() != QMediaRecorder::RecordingState)
return;
signalDurationChangedTimer.stop();
+ durationChanged(duration());
gstPipeline.dumpGraph("before-pause");
stateChanged(QMediaRecorder::PausedState);
}
@@ -366,12 +352,13 @@ void QGstreamerMediaEncoder::stop()
{
if (!m_session || m_finalizing || state() == QMediaRecorder::StoppedState)
return;
- qCDebug(qLcMediaEncoder) << "stop";
+ durationChanged(duration());
+ qCDebug(qLcMediaEncoderGst) << "stop";
m_finalizing = true;
m_session->unlinkEncoder();
signalDurationChangedTimer.stop();
- qCDebug(qLcMediaEncoder) << ">>>>>>>>>>>>> sending EOS";
+ qCDebug(qLcMediaEncoderGst) << ">>>>>>>>>>>>> sending EOS";
gstEncoder.sendEos();
}
@@ -380,12 +367,9 @@ void QGstreamerMediaEncoder::finalize()
if (!m_session || gstEncoder.isNull())
return;
- qCDebug(qLcMediaEncoder) << "finalize";
+ qCDebug(qLcMediaEncoderGst) << "finalize";
- gstPipeline.remove(gstEncoder);
- gstPipeline.remove(gstFileSink);
- gstEncoder.setStateSync(GST_STATE_NULL);
- gstFileSink.setStateSync(GST_STATE_NULL);
+ gstPipeline.stopAndRemoveElements(gstEncoder, gstFileSink);
gstFileSink = {};
gstEncoder = {};
m_finalizing = false;
@@ -396,7 +380,7 @@ void QGstreamerMediaEncoder::setMetaData(const QMediaMetaData &metaData)
{
if (!m_session)
return;
- m_metaData = static_cast<const QGstreamerMetaData &>(metaData);
+ m_metaData = metaData;
}
QMediaMetaData QGstreamerMediaEncoder::metaData() const
@@ -414,7 +398,8 @@ void QGstreamerMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *ses
stop();
if (m_finalizing) {
QEventLoop loop;
- loop.connect(mediaRecorder(), SIGNAL(recorderStateChanged(RecorderState)), SLOT(quit()));
+ QObject::connect(mediaRecorder(), &QMediaRecorder::recorderStateChanged, &loop,
+ &QEventLoop::quit);
loop.exec();
}
@@ -430,3 +415,5 @@ void QGstreamerMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *ses
gstPipeline.set("message-forward", true);
gstPipeline.installMessageFilter(this);
}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h
index 554b79207..637fb7264 100644
--- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h
+++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERENCODERCONTROL_H
@@ -52,14 +16,14 @@
// We mean it.
//
-#include <private/qplatformmediarecorder_p.h>
-#include "qgstreamermediacapture_p.h"
-#include "qgstreamermetadata_p.h"
+#include <mediacapture/qgstreamermediacapture_p.h>
+#include <common/qgstreamermetadata_p.h>
+#include <QtMultimedia/private/qplatformmediarecorder_p.h>
#include <QtCore/qurl.h>
#include <QtCore/qdir.h>
-#include <qelapsedtimer.h>
-#include <qtimer.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qtimer.h>
QT_BEGIN_NAMESPACE
@@ -69,7 +33,7 @@ class QGstreamerMessage;
class QGstreamerMediaEncoder : public QPlatformMediaRecorder, QGstreamerBusMessageFilter
{
public:
- QGstreamerMediaEncoder(QMediaRecorder *parent);
+ explicit QGstreamerMediaEncoder(QMediaRecorder *parent);
virtual ~QGstreamerMediaEncoder();
bool isLocationWritable(const QUrl &sink) const override;
@@ -92,7 +56,7 @@ private:
private:
struct PauseControl {
- PauseControl(QPlatformMediaRecorder &encoder) : encoder(encoder) {}
+ explicit PauseControl(QPlatformMediaRecorder &encoder) : encoder(encoder) { }
GstPadProbeReturn processBuffer(QGstPad pad, GstPadProbeInfo *info);
void installOn(QGstPad pad);
@@ -112,7 +76,7 @@ private:
void finalize();
QGstreamerMediaCapture *m_session = nullptr;
- QGstreamerMetaData m_metaData;
+ QMediaMetaData m_metaData;
QTimer signalDurationChangedTimer;
QGstPipeline gstPipeline;
diff --git a/src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp b/src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp
index 8c17ed7e7..86d59a9a8 100644
--- a/src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp
+++ b/src/plugins/multimedia/gstreamer/qgstreamerformatinfo.cpp
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include <common/qglist_helper_p.h>
#include "qgstreamerformatinfo_p.h"
-#include "qgstutils_p.h"
+#include <gst/gst.h>
QT_BEGIN_NAMESPACE
@@ -181,18 +146,13 @@ static QPair<QList<QMediaFormat::AudioCodec>, QList<QMediaFormat::VideoCodec>> g
GList *elementList = gst_element_factory_list_get_elements(decode ? GST_ELEMENT_FACTORY_TYPE_DECODER : GST_ELEMENT_FACTORY_TYPE_ENCODER,
GST_RANK_MARGINAL);
- GList *element = elementList;
- while (element) {
- GstElementFactory *factory = (GstElementFactory *)element->data;
- element = element->next;
-
- const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory);
- while (padTemplates) {
- GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data;
- padTemplates = padTemplates->next;
-
+ for (GstElementFactory *factory :
+ QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
+ for (GstStaticPadTemplate *padTemplate :
+ QGstUtils::GListRangeAdaptor<GstStaticPadTemplate *>(
+ gst_element_factory_get_static_pad_templates(factory))) {
if (padTemplate->direction == padDirection) {
- QGstMutableCaps caps = gst_static_caps_get(&padTemplate->static_caps);
+ auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
for (int i = 0; i < caps.size(); i++) {
QGstStructure structure = caps.at(i);
@@ -219,22 +179,20 @@ QList<QGstreamerFormatInfo::CodecMap> QGstreamerFormatInfo::getMuxerList(bool de
GstPadDirection padDirection = demuxer ? GST_PAD_SINK : GST_PAD_SRC;
- GList *elementList = gst_element_factory_list_get_elements(demuxer ? GST_ELEMENT_FACTORY_TYPE_DEMUXER : GST_ELEMENT_FACTORY_TYPE_MUXER,
- GST_RANK_MARGINAL);
- GList *element = elementList;
- while (element) {
- GstElementFactory *factory = (GstElementFactory *)element->data;
- element = element->next;
+ GList *elementList = gst_element_factory_list_get_elements(
+ demuxer ? GST_ELEMENT_FACTORY_TYPE_DEMUXER : GST_ELEMENT_FACTORY_TYPE_MUXER,
+ GST_RANK_MARGINAL);
+ for (GstElementFactory *factory :
+ QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
QList<QMediaFormat::FileFormat> fileFormats;
- const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory);
- while (padTemplates) {
- GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data;
- padTemplates = padTemplates->next;
+ for (GstStaticPadTemplate *padTemplate :
+ QGstUtils::GListRangeAdaptor<GstStaticPadTemplate *>(
+ gst_element_factory_get_static_pad_templates(factory))) {
if (padTemplate->direction == padDirection) {
- QGstMutableCaps caps = gst_static_caps_get(&padTemplate->static_caps);
+ auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
for (int i = 0; i < caps.size(); i++) {
QGstStructure structure = caps.at(i);
@@ -250,14 +208,13 @@ QList<QGstreamerFormatInfo::CodecMap> QGstreamerFormatInfo::getMuxerList(bool de
QList<QMediaFormat::AudioCodec> audioCodecs;
QList<QMediaFormat::VideoCodec> videoCodecs;
- padTemplates = gst_element_factory_get_static_pad_templates(factory);
- while (padTemplates) {
- GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data;
- padTemplates = padTemplates->next;
+ for (GstStaticPadTemplate *padTemplate :
+ QGstUtils::GListRangeAdaptor<GstStaticPadTemplate *>(
+ gst_element_factory_get_static_pad_templates(factory))) {
// check the other side for supported inputs/outputs
if (padTemplate->direction != padDirection) {
- QGstMutableCaps caps = gst_static_caps_get(&padTemplate->static_caps);
+ auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
bool acceptsRawAudio = false;
for (int i = 0; i < caps.size(); i++) {
@@ -290,7 +247,7 @@ QList<QGstreamerFormatInfo::CodecMap> QGstreamerFormatInfo::getMuxerList(bool de
}
}
if (!audioCodecs.isEmpty() || !videoCodecs.isEmpty()) {
- for (auto f : qAsConst(fileFormats)) {
+ for (auto f : std::as_const(fileFormats)) {
muxers.append({f, audioCodecs, videoCodecs});
if (f == QMediaFormat::MPEG4 && !fileFormats.contains(QMediaFormat::Mpeg4Audio)) {
muxers.append({QMediaFormat::Mpeg4Audio, audioCodecs, {}});
@@ -313,18 +270,14 @@ static QList<QImageCapture::FileFormat> getImageFormatList()
GList *elementList = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_ENCODER,
GST_RANK_MARGINAL);
- GList *element = elementList;
- while (element) {
- GstElementFactory *factory = (GstElementFactory *)element->data;
- element = element->next;
-
- const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory);
- while (padTemplates) {
- GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data;
- padTemplates = padTemplates->next;
+ for (GstElementFactory *factory :
+ QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
+ for (GstStaticPadTemplate *padTemplate :
+ QGstUtils::GListRangeAdaptor<GstStaticPadTemplate *>(
+ gst_element_factory_get_static_pad_templates(factory))) {
if (padTemplate->direction == GST_PAD_SRC) {
- QGstMutableCaps caps = gst_static_caps_get(&padTemplate->static_caps);
+ QGstCaps caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
for (int i = 0; i < caps.size(); i++) {
QGstStructure structure = caps.at(i);
@@ -387,7 +340,7 @@ QGstreamerFormatInfo::QGstreamerFormatInfo()
QGstreamerFormatInfo::~QGstreamerFormatInfo() = default;
-QGstMutableCaps QGstreamerFormatInfo::formatCaps(const QMediaFormat &f) const
+QGstCaps QGstreamerFormatInfo::formatCaps(const QMediaFormat &f) const
{
auto format = f.fileFormat();
Q_ASSERT(format != QMediaFormat::UnspecifiedFormat);
@@ -407,14 +360,14 @@ QGstMutableCaps QGstreamerFormatInfo::formatCaps(const QMediaFormat &f) const
"audio/x-flac", // FLAC
"audio/x-wav" // Wave
};
- return gst_caps_from_string(capsForFormat[format]);
+ return QGstCaps(gst_caps_from_string(capsForFormat[format]), QGstCaps::HasRef);
}
-QGstMutableCaps QGstreamerFormatInfo::audioCaps(const QMediaFormat &f) const
+QGstCaps QGstreamerFormatInfo::audioCaps(const QMediaFormat &f) const
{
auto codec = f.audioCodec();
if (codec == QMediaFormat::AudioCodec::Unspecified)
- return nullptr;
+ return {};
const char *capsForCodec[(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = {
"audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
@@ -429,14 +382,14 @@ QGstMutableCaps QGstreamerFormatInfo::audioCaps(const QMediaFormat &f) const
"audio/x-wma", // WMA
"audio/x-alac", // ALAC
};
- return gst_caps_from_string(capsForCodec[(int)codec]);
+ return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
}
-QGstMutableCaps QGstreamerFormatInfo::videoCaps(const QMediaFormat &f) const
+QGstCaps QGstreamerFormatInfo::videoCaps(const QMediaFormat &f) const
{
auto codec = f.videoCodec();
if (codec == QMediaFormat::VideoCodec::Unspecified)
- return nullptr;
+ return {};
const char *capsForCodec[(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = {
"video/mpeg, mpegversion=(int)1", // MPEG1,
@@ -451,7 +404,7 @@ QGstMutableCaps QGstreamerFormatInfo::videoCaps(const QMediaFormat &f) const
"audio/x-wmv", // WMV
"video/x-jpeg", // MotionJPEG,
};
- return gst_caps_from_string(capsForCodec[(int)codec]);
+ return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/qgstreamerformatinfo_p.h b/src/plugins/multimedia/gstreamer/qgstreamerformatinfo_p.h
index 3cfea9dc5..def42b7ea 100644
--- a/src/plugins/multimedia/gstreamer/qgstreamerformatinfo_p.h
+++ b/src/plugins/multimedia/gstreamer/qgstreamerformatinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERFORMATINFO_H
#define QGSTREAMERFORMATINFO_H
@@ -52,9 +16,8 @@
//
#include <private/qplatformmediaformatinfo_p.h>
-#include <qhash.h>
#include <qlist.h>
-#include <qgstutils_p.h>
+#include <common/qgst_p.h>
QT_BEGIN_NAMESPACE
@@ -64,9 +27,9 @@ public:
QGstreamerFormatInfo();
~QGstreamerFormatInfo();
- QGstMutableCaps formatCaps(const QMediaFormat &f) const;
- QGstMutableCaps audioCaps(const QMediaFormat &f) const;
- QGstMutableCaps videoCaps(const QMediaFormat &f) const;
+ QGstCaps formatCaps(const QMediaFormat &f) const;
+ QGstCaps audioCaps(const QMediaFormat &f) const;
+ QGstCaps videoCaps(const QMediaFormat &f) const;
static QMediaFormat::AudioCodec audioCodecForCaps(QGstStructure structure);
static QMediaFormat::VideoCodec videoCodecForCaps(QGstStructure structure);
diff --git a/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp b/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp
index 8ed2fda03..4ee5b36e8 100644
--- a/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp
+++ b/src/plugins/multimedia/gstreamer/qgstreamerintegration.cpp
@@ -1,150 +1,123 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qgstreamerintegration_p.h"
-#include "qgstreamervideodevices_p.h"
-#include "qgstreamermediaplayer_p.h"
-#include "qgstreamermediacapture_p.h"
-#include "qgstreameraudiodecoder_p.h"
-#include "qgstreamercamera_p.h"
-#include "qgstreamermediaencoder_p.h"
-#include "qgstreamerimagecapture_p.h"
-#include "qgstreamerformatinfo_p.h"
-#include "qgstreamervideosink_p.h"
-#include "qgstreameraudioinput_p.h"
-#include "qgstreameraudiooutput_p.h"
-#include <QtMultimedia/private/qplatformmediaplugin_p.h>
-
-#include <memory>
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qgstreamerintegration_p.h>
+#include <qgstreamerformatinfo_p.h>
+#include <qgstreamervideodevices_p.h>
+#include <audio/qgstreameraudiodecoder_p.h>
+#include <common/qgstreameraudioinput_p.h>
+#include <common/qgstreameraudiooutput_p.h>
+#include <common/qgstreamermediaplayer_p.h>
+#include <common/qgstreamervideosink_p.h>
+#include <mediacapture/qgstreamercamera_p.h>
+#include <mediacapture/qgstreamerimagecapture_p.h>
+#include <mediacapture/qgstreamermediacapture_p.h>
+#include <mediacapture/qgstreamermediaencoder_p.h>
+
+#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
-class QGstreamerMediaPlugin : public QPlatformMediaPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "gstreamer.json")
-
-public:
- QGstreamerMediaPlugin()
- : QPlatformMediaPlugin()
- {}
-
- QPlatformMediaIntegration* create(const QString &name) override
- {
- if (name == QLatin1String("gstreamer"))
- return new QGstreamerIntegration;
- return nullptr;
- }
-};
+Q_LOGGING_CATEGORY(lcGstreamer, "qt.multimedia.gstreamer")
QGstreamerIntegration::QGstreamerIntegration()
+ : QPlatformMediaIntegration(QLatin1String("gstreamer"))
{
gst_init(nullptr, nullptr);
- m_videoDevices = new QGstreamerVideoDevices(this);
- m_formatsInfo = new QGstreamerFormatInfo();
+ qCDebug(lcGstreamer) << "Using gstreamer version: " << gst_version_string();
+
+ if constexpr (!GST_CHECK_VERSION(1, 22, 0)) {
+ GstRegistry* reg = gst_registry_get();
+ const auto pluginNames = {
+ "vaapidecodebin",
+ "vaapih264dec",
+ "vaapih264enc",
+ "vaapih265dec",
+ "vaapijpegdec",
+ "vaapijpegenc",
+ "vaapimpeg2dec",
+ "vaapipostproc",
+ "vaapisink",
+ "vaapivp8dec",
+ "vaapivp9dec"
+ };
+
+ for (auto name : pluginNames) {
+ QGstPluginFeatureHandle pluginFeature {
+ gst_registry_lookup_feature(reg, name),
+ QGstPluginFeatureHandle::HasRef,
+ };
+ if (pluginFeature) {
+ gst_plugin_feature_set_rank(pluginFeature.get(), GST_RANK_PRIMARY - 1);
+ }
+ }
+ }
}
-QGstreamerIntegration::~QGstreamerIntegration()
+QPlatformMediaFormatInfo *QGstreamerIntegration::createFormatInfo()
{
- delete m_formatsInfo;
+ return new QGstreamerFormatInfo();
}
-QPlatformMediaFormatInfo *QGstreamerIntegration::formatInfo()
+QPlatformVideoDevices *QGstreamerIntegration::createVideoDevices()
{
- return m_formatsInfo;
+ return new QGstreamerVideoDevices(this);
}
-const QGstreamerFormatInfo *QGstreamerIntegration::gstFormatsInfo() const
+const QGstreamerFormatInfo *QGstreamerIntegration::gstFormatsInfo()
{
- return m_formatsInfo;
+ return static_cast<const QGstreamerFormatInfo *>(formatInfo());
}
-QPlatformAudioDecoder *QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder)
+QMaybe<QPlatformAudioDecoder *> QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder)
{
- return new QGstreamerAudioDecoder(decoder);
+ return QGstreamerAudioDecoder::create(decoder);
}
-QPlatformMediaCaptureSession *QGstreamerIntegration::createCaptureSession()
+QMaybe<QPlatformMediaCaptureSession *> QGstreamerIntegration::createCaptureSession()
{
- return new QGstreamerMediaCapture();
+ return QGstreamerMediaCapture::create();
}
-QPlatformMediaPlayer *QGstreamerIntegration::createPlayer(QMediaPlayer *player)
+QMaybe<QPlatformMediaPlayer *> QGstreamerIntegration::createPlayer(QMediaPlayer *player)
{
- return new QGstreamerMediaPlayer(player);
+ return QGstreamerMediaPlayer::create(player);
}
-QPlatformCamera *QGstreamerIntegration::createCamera(QCamera *camera)
+QMaybe<QPlatformCamera *> QGstreamerIntegration::createCamera(QCamera *camera)
{
- return new QGstreamerCamera(camera);
+ return QGstreamerCamera::create(camera);
}
-QPlatformMediaRecorder *QGstreamerIntegration::createRecorder(QMediaRecorder *recorder)
+QMaybe<QPlatformMediaRecorder *> QGstreamerIntegration::createRecorder(QMediaRecorder *recorder)
{
return new QGstreamerMediaEncoder(recorder);
}
-QPlatformImageCapture *QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture)
+QMaybe<QPlatformImageCapture *> QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture)
{
- return new QGstreamerImageCapture(imageCapture);
+ return QGstreamerImageCapture::create(imageCapture);
}
-QPlatformVideoSink *QGstreamerIntegration::createVideoSink(QVideoSink *sink)
+QMaybe<QPlatformVideoSink *> QGstreamerIntegration::createVideoSink(QVideoSink *sink)
{
return new QGstreamerVideoSink(sink);
}
-QPlatformAudioInput *QGstreamerIntegration::createAudioInput(QAudioInput *q)
+QMaybe<QPlatformAudioInput *> QGstreamerIntegration::createAudioInput(QAudioInput *q)
{
- return new QGstreamerAudioInput(q);
+ return QGstreamerAudioInput::create(q);
}
-QPlatformAudioOutput *QGstreamerIntegration::createAudioOutput(QAudioOutput *q)
+QMaybe<QPlatformAudioOutput *> QGstreamerIntegration::createAudioOutput(QAudioOutput *q)
{
- return new QGstreamerAudioOutput(q);
+ return QGstreamerAudioOutput::create(q);
}
-GstDevice *QGstreamerIntegration::videoDevice(const QByteArray &id) const
+GstDevice *QGstreamerIntegration::videoDevice(const QByteArray &id)
{
- return m_videoDevices ? static_cast<QGstreamerVideoDevices*>(m_videoDevices)->videoDevice(id) : nullptr;
+ const auto devices = videoDevices();
+ return devices ? static_cast<QGstreamerVideoDevices *>(devices)->videoDevice(id) : nullptr;
}
QT_END_NAMESPACE
-
-#include "qgstreamerintegration.moc"
diff --git a/src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h b/src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h
index 4b8d02efd..9cb84c57b 100644
--- a/src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h
+++ b/src/plugins/multimedia/gstreamer/qgstreamerintegration_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERINTEGRATION_H
#define QGSTREAMERINTEGRATION_H
@@ -62,28 +26,30 @@ class QGstreamerIntegration : public QPlatformMediaIntegration
{
public:
QGstreamerIntegration();
- ~QGstreamerIntegration();
- static QGstreamerIntegration *instance() { return static_cast<QGstreamerIntegration *>(QPlatformMediaIntegration::instance()); }
- QPlatformMediaFormatInfo *formatInfo() override;
+ static QGstreamerIntegration *instance()
+ {
+ return static_cast<QGstreamerIntegration *>(QPlatformMediaIntegration::instance());
+ }
- QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *decoder) override;
- QPlatformMediaCaptureSession *createCaptureSession() override;
- QPlatformMediaPlayer *createPlayer(QMediaPlayer *player) override;
- QPlatformCamera *createCamera(QCamera *) override;
- QPlatformMediaRecorder *createRecorder(QMediaRecorder *) override;
- QPlatformImageCapture *createImageCapture(QImageCapture *) override;
+ QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *decoder) override;
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *player) override;
+ QMaybe<QPlatformCamera *> createCamera(QCamera *) override;
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *) override;
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *) override;
- QPlatformVideoSink *createVideoSink(QVideoSink *sink) override;
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
- QPlatformAudioInput *createAudioInput(QAudioInput *) override;
- QPlatformAudioOutput *createAudioOutput(QAudioOutput *) override;
+ QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *) override;
+ QMaybe<QPlatformAudioOutput *> createAudioOutput(QAudioOutput *) override;
- const QGstreamerFormatInfo *gstFormatsInfo() const;
- GstDevice *videoDevice(const QByteArray &id) const;
+ const QGstreamerFormatInfo *gstFormatsInfo();
+ GstDevice *videoDevice(const QByteArray &id);
-private:
- QGstreamerFormatInfo *m_formatsInfo;
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+ QPlatformVideoDevices *createVideoDevices() override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/qgstreamerplugin.cpp b/src/plugins/multimedia/gstreamer/qgstreamerplugin.cpp
new file mode 100644
index 000000000..66ad7f712
--- /dev/null
+++ b/src/plugins/multimedia/gstreamer/qgstreamerplugin.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtMultimedia/private/qplatformmediaplugin_p.h>
+
+#include <qgstreamerintegration_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGstreamerMediaPlugin : public QPlatformMediaPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "gstreamer.json")
+
+public:
+ QGstreamerMediaPlugin() = default;
+
+ QPlatformMediaIntegration* create(const QString &name) override
+ {
+ if (name == u"gstreamer")
+ return new QGstreamerIntegration;
+ return nullptr;
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "qgstreamerplugin.moc"
diff --git a/src/plugins/multimedia/gstreamer/qgstreamervideodevices.cpp b/src/plugins/multimedia/gstreamer/qgstreamervideodevices.cpp
index e7595b4cc..cceaca621 100644
--- a/src/plugins/multimedia/gstreamer/qgstreamervideodevices.cpp
+++ b/src/plugins/multimedia/gstreamer/qgstreamervideodevices.cpp
@@ -1,201 +1,156 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgstreamervideodevices_p.h"
-#include "qmediadevices.h"
-#include "private/qcameradevice_p.h"
+#include <QtMultimedia/qmediadevices.h>
+#include <QtMultimedia/private/qcameradevice_p.h>
-#include "qgstreameraudiosource_p.h"
-#include "qgstreameraudiosink_p.h"
-#include "qgstreameraudiodevice_p.h"
-#include "qgstutils_p.h"
+#include <common/qgst_p.h>
+#include <common/qgstutils_p.h>
+#include <common/qglist_helper_p.h>
QT_BEGIN_NAMESPACE
-static gboolean deviceMonitor(GstBus *, GstMessage *message, gpointer m)
+static gboolean deviceMonitorCallback(GstBus *, GstMessage *message, gpointer m)
{
auto *manager = static_cast<QGstreamerVideoDevices *>(m);
- GstDevice *device = nullptr;
+ QGstDeviceHandle device;
- switch (GST_MESSAGE_TYPE (message)) {
+ switch (GST_MESSAGE_TYPE(message)) {
case GST_MESSAGE_DEVICE_ADDED:
gst_message_parse_device_added(message, &device);
- manager->addDevice(device);
+ manager->addDevice(std::move(device));
break;
case GST_MESSAGE_DEVICE_REMOVED:
gst_message_parse_device_removed(message, &device);
- manager->removeDevice(device);
+ manager->removeDevice(std::move(device));
break;
default:
break;
}
- if (device)
- gst_object_unref (device);
return G_SOURCE_CONTINUE;
}
QGstreamerVideoDevices::QGstreamerVideoDevices(QPlatformMediaIntegration *integration)
- : QPlatformVideoDevices(integration)
+ : QPlatformVideoDevices(integration),
+ m_deviceMonitor{
+ gst_device_monitor_new(),
+ }
{
- GstDeviceMonitor *monitor;
- GstBus *bus;
+ gst_device_monitor_add_filter(m_deviceMonitor.get(), "Video/Source", nullptr);
- monitor = gst_device_monitor_new();
+ QGstBusHandle bus{
+ gst_device_monitor_get_bus(m_deviceMonitor.get()),
+ };
+ gst_bus_add_watch(bus.get(), deviceMonitorCallback, this);
+ gst_device_monitor_start(m_deviceMonitor.get());
- gst_device_monitor_add_filter (monitor, nullptr, nullptr);
+ GList *devices = gst_device_monitor_get_devices(m_deviceMonitor.get());
- bus = gst_device_monitor_get_bus(monitor);
- gst_bus_add_watch(bus, deviceMonitor, this);
- gst_object_unref(bus);
-
- gst_device_monitor_start(monitor);
+ for (GstDevice *device : QGstUtils::GListRangeAdaptor<GstDevice *>(devices)) {
+ addDevice(QGstDeviceHandle{
+ device,
+ QGstDeviceHandle::HasRef,
+ });
+ }
- auto devices = gst_device_monitor_get_devices(monitor);
+ g_list_free(devices);
+}
- while (devices) {
- GstDevice *device = static_cast<GstDevice *>(devices->data);
- addDevice(device);
- gst_object_unref(device);
- devices = g_list_delete_link(devices, devices);
- }
+QGstreamerVideoDevices::~QGstreamerVideoDevices()
+{
+ gst_device_monitor_stop(m_deviceMonitor.get());
}
QList<QCameraDevice> QGstreamerVideoDevices::videoDevices() const
{
QList<QCameraDevice> devices;
- for (auto *d : qAsConst(m_videoSources)) {
- QGstStructure properties = gst_device_get_properties(d);
- if (!properties.isNull()) {
- QCameraDevicePrivate *info = new QCameraDevicePrivate;
- auto *desc = gst_device_get_display_name(d);
- info->description = QString::fromUtf8(desc);
- g_free(desc);
+ for (const auto &device : m_videoSources) {
+ QCameraDevicePrivate *info = new QCameraDevicePrivate;
- info->id = properties["device.path"].toString();
+ QGString desc{
+ gst_device_get_display_name(device.gstDevice.get()),
+ };
+ info->description = desc.toQString();
+ info->id = device.id;
+
+ if (QGstStructure properties = gst_device_get_properties(device.gstDevice.get());
+ !properties.isNull()) {
auto def = properties["is-default"].toBool();
info->isDefault = def && *def;
- if (def)
- devices.prepend(info->create());
- else
- devices.append(info->create());
properties.free();
- QGstCaps caps = gst_device_get_caps(d);
- if (!caps.isNull()) {
- QList<QCameraFormat> formats;
- QSet<QSize> photoResolutions;
-
- int size = caps.size();
- for (int i = 0; i < size; ++i) {
- auto cap = caps.at(i);
-
- QSize resolution = cap.resolution();
- if (!resolution.isValid())
- continue;
-
- auto pixelFormat = cap.pixelFormat();
- auto frameRate = cap.frameRateRange();
-
- auto *f = new QCameraFormatPrivate{
- QSharedData(),
- pixelFormat,
- resolution,
- frameRate.min,
- frameRate.max
- };
- formats << f->create();
- photoResolutions.insert(resolution);
- }
- info->videoFormats = formats;
- // ### sort resolutions?
- info->photoResolutions = photoResolutions.values();
+ }
+
+ if (info->isDefault)
+ devices.prepend(info->create());
+ else
+ devices.append(info->create());
+
+ auto caps = QGstCaps(gst_device_get_caps(device.gstDevice.get()), QGstCaps::HasRef);
+ if (!caps.isNull()) {
+ QList<QCameraFormat> formats;
+ QSet<QSize> photoResolutions;
+
+ int size = caps.size();
+ for (int i = 0; i < size; ++i) {
+ auto cap = caps.at(i);
+
+ QSize resolution = cap.resolution();
+ if (!resolution.isValid())
+ continue;
+
+ auto pixelFormat = cap.pixelFormat();
+ auto frameRate = cap.frameRateRange();
+
+ auto *f = new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution,
+ frameRate.min, frameRate.max };
+ formats << f->create();
+ photoResolutions.insert(resolution);
}
+ info->videoFormats = formats;
+ // ### sort resolutions?
+ info->photoResolutions = photoResolutions.values();
}
}
return devices;
}
-void QGstreamerVideoDevices::addDevice(GstDevice *device)
+void QGstreamerVideoDevices::addDevice(QGstDeviceHandle device)
{
- gchar *type = gst_device_get_device_class(device);
-// qDebug() << "adding device:" << device << type << gst_device_get_display_name(device) << gst_structure_to_string(gst_device_get_properties(device));
- gst_object_ref(device);
- if (!strcmp(type, "Video/Source") || !strcmp(type, "Source/Video")) {
- m_videoSources.insert(device);
- videoInputsChanged();
- } else {
- gst_object_unref(device);
- }
- g_free(type);
-}
+ Q_ASSERT(gst_device_has_classes(device.get(), "Video/Source"));
-void QGstreamerVideoDevices::removeDevice(GstDevice *device)
-{
-// qDebug() << "removing device:" << device << gst_device_get_display_name(device);
- if (m_videoSources.remove(device))
- videoInputsChanged();
+ auto it = std::find_if(m_videoSources.begin(), m_videoSources.end(),
+ [&](const QGstRecordDevice &a) { return a.gstDevice == device; });
+
+ if (it != m_videoSources.end())
+ return;
- gst_object_unref(device);
+ m_videoSources.push_back(QGstRecordDevice{
+ std::move(device),
+ QByteArray::number(m_idGenerator),
+ });
+ emit videoInputsChanged();
+ m_idGenerator++;
}
-static GstDevice *getDevice(const QSet<GstDevice *> &devices, const char *key, const QByteArray &id)
+void QGstreamerVideoDevices::removeDevice(QGstDeviceHandle device)
{
- GstDevice *gstDevice = nullptr;
- for (auto *d : devices) {
- QGstStructure properties = gst_device_get_properties(d);
- if (!properties.isNull()) {
- auto *name = properties[key].toString();
- if (id == name) {
- gstDevice = d;
- }
- }
- properties.free();
- if (gstDevice)
- break;
+ auto it = std::find_if(m_videoSources.begin(), m_videoSources.end(),
+ [&](const QGstRecordDevice &a) { return a.gstDevice == device; });
+
+ if (it != m_videoSources.end()) {
+ m_videoSources.erase(it);
+ emit videoInputsChanged();
}
- return gstDevice;
}
GstDevice *QGstreamerVideoDevices::videoDevice(const QByteArray &id) const
{
- return getDevice(m_videoSources, "device.path", id);
+ auto it = std::find_if(m_videoSources.begin(), m_videoSources.end(),
+ [&](const QGstRecordDevice &a) { return a.id == id; });
+ return it != m_videoSources.end() ? it->gstDevice.get() : nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/gstreamer/qgstreamervideodevices_p.h b/src/plugins/multimedia/gstreamer/qgstreamervideodevices_p.h
index cd0acc053..a321ae66b 100644
--- a/src/plugins/multimedia/gstreamer/qgstreamervideodevices_p.h
+++ b/src/plugins/multimedia/gstreamer/qgstreamervideodevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QGSTREAMERMEDIADEVICES_H
#define QGSTREAMERMEDIADEVICES_H
@@ -53,24 +17,36 @@
#include <private/qplatformvideodevices_p.h>
#include <gst/gst.h>
-#include <qset.h>
#include <qaudiodevice.h>
+#include <vector>
+
+#include <common/qgst_handle_types_p.h>
QT_BEGIN_NAMESPACE
class QGstreamerVideoDevices : public QPlatformVideoDevices
{
public:
- QGstreamerVideoDevices(QPlatformMediaIntegration *integration);
+ explicit QGstreamerVideoDevices(QPlatformMediaIntegration *integration);
+ ~QGstreamerVideoDevices();
QList<QCameraDevice> videoDevices() const override;
GstDevice *videoDevice(const QByteArray &id) const;
- void addDevice(GstDevice *);
- void removeDevice(GstDevice *);
+ void addDevice(QGstDeviceHandle);
+ void removeDevice(QGstDeviceHandle);
private:
- QSet<GstDevice *> m_videoSources;
+ struct QGstRecordDevice
+ {
+ QGstDeviceHandle gstDevice;
+ QByteArray id;
+ };
+
+ quint64 m_idGenerator = 0;
+ std::vector<QGstRecordDevice> m_videoSources;
+
+ QGstDeviceMonitorHandle m_deviceMonitor;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/CMakeLists.txt b/src/plugins/multimedia/qnx/CMakeLists.txt
index 18d062343..e1ac0ffa3 100644
--- a/src/plugins/multimedia/qnx/CMakeLists.txt
+++ b/src/plugins/multimedia/qnx/CMakeLists.txt
@@ -1,10 +1,13 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_internal_add_plugin(QQnxMediaPlugin
OUTPUT_NAME qnxmediaplugin
PLUGIN_TYPE multimedia
SOURCES
- SOURCES
camera/qqnxcamera.cpp camera/qqnxcamera_p.h
+ camera/qqnxplatformcamera.cpp camera/qqnxplatformcamera_p.h
camera/qqnxcameraframebuffer.cpp camera/qqnxcameraframebuffer_p.h
camera/qqnximagecapture.cpp camera/qqnximagecapture_p.h
common/qqnxaudioinput.cpp common/qqnxaudioinput_p.h
diff --git a/src/plugins/multimedia/qnx/camera/qqnxcamera.cpp b/src/plugins/multimedia/qnx/camera/qqnxcamera.cpp
index 35008be45..6976221bd 100644
--- a/src/plugins/multimedia/qnx/camera/qqnxcamera.cpp
+++ b/src/plugins/multimedia/qnx/camera/qqnxcamera.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxcamera_p.h"
#include "qqnxcameraframebuffer_p.h"
#include "qqnxmediacapturesession_p.h"
@@ -46,21 +10,15 @@
#include <private/qmediastoragelocation_p.h>
-static constexpr camera_focusmode_t qnxFocusMode(QCamera::FocusMode mode)
+QDebug &operator<<(QDebug &d, const QQnxCamera::VideoFormat &f)
{
- switch (mode) {
- default:
- case QCamera::FocusModeAuto:
- case QCamera::FocusModeAutoFar:
- case QCamera::FocusModeInfinity:
- return CAMERA_FOCUSMODE_CONTINUOUS_AUTO;
- case QCamera::FocusModeAutoNear:
- return CAMERA_FOCUSMODE_CONTINUOUS_MACRO;
- case QCamera::FocusModeHyperfocal:
- return CAMERA_FOCUSMODE_EDOF;
- case QCamera::FocusModeManual:
- return CAMERA_FOCUSMODE_MANUAL;
- }
+ d << "VideoFormat - width=" << f.width
+ << "height=" << f.height
+ << "rotation=" << f.rotation
+ << "frameRate=" << f.frameRate
+ << "frameType=" << f.frameType;
+
+ return d;
}
static QString statusToString(camera_devstatus_t status)
@@ -101,10 +59,28 @@ static QString statusToString(camera_devstatus_t status)
QT_BEGIN_NAMESPACE
-QQnxCamera::QQnxCamera(QCamera *parent)
- : QPlatformCamera(parent)
+QQnxCamera::QQnxCamera(camera_unit_t unit, QObject *parent)
+ : QObject(parent)
+ , m_cameraUnit(unit)
{
- setCamera(QMediaDevices::defaultVideoInput());
+ if (!m_handle.open(m_cameraUnit, CAMERA_MODE_RW))
+ qWarning("QQnxCamera: Failed to open camera (0x%x)", m_handle.lastError());
+
+ if (camera_set_vf_mode(m_handle.get(), CAMERA_VFMODE_VIDEO) != CAMERA_EOK) {
+ qWarning("QQnxCamera: unable to configure viewfinder mode");
+ return;
+ }
+
+ if (camera_set_vf_property(m_handle.get(), CAMERA_IMGPROP_CREATEWINDOW, 0,
+ CAMERA_IMGPROP_RENDERTOWINDOW, 0) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to set camera properties");
+ return;
+ }
+
+ updateZoomLimits();
+ updateSupportedWhiteBalanceValues();
+
+ m_valid = true;
}
QQnxCamera::~QQnxCamera()
@@ -112,43 +88,38 @@ QQnxCamera::~QQnxCamera()
stop();
}
-bool QQnxCamera::isActive() const
-{
- return m_handle.isOpen() && m_viewfinderActive;
-}
-
-void QQnxCamera::setActive(bool active)
+camera_unit_t QQnxCamera::unit() const
{
- if (active)
- start();
- else
- stop();
+ return m_cameraUnit;
}
-void QQnxCamera::start()
+QString QQnxCamera::name() const
{
- if (isActive())
- return;
+ char name[CAMERA_LOCATION_NAMELEN];
- updateCameraFeatures();
-
- if (camera_set_vf_property(m_handle.get(), CAMERA_IMGPROP_CREATEWINDOW, 0,
- CAMERA_IMGPROP_RENDERTOWINDOW, 0) != CAMERA_EOK) {
- qWarning("QQnxCamera: failed to set camera properties");
- return;
+ if (camera_get_location_property(m_cameraUnit,
+ CAMERA_LOCATION_NAME, &name, CAMERA_LOCATION_END) != CAMERA_EOK) {
+ qWarning("QQnxCamera: unable to obtain camera name");
+ return {};
}
- constexpr camera_vfmode_t mode = CAMERA_VFMODE_DEFAULT;
+ return QString::fromUtf8(name);
+}
- if (!supportedVfModes().contains(mode)) {
- qWarning("QQnxCamera: unsupported viewfinder mode");
- return;
- }
+bool QQnxCamera::isValid() const
+{
+ return m_valid;
+}
- if (camera_set_vf_mode(m_handle.get(), mode) != CAMERA_EOK) {
- qWarning("QQnxCamera: unable to configure viewfinder mode");
+bool QQnxCamera::isActive() const
+{
+ return m_handle.isOpen() && m_viewfinderActive;
+}
+
+void QQnxCamera::start()
+{
+ if (isActive())
return;
- }
if (camera_start_viewfinder(m_handle.get(), viewfinderCallback,
statusCallback, this) != CAMERA_EOK) {
@@ -157,12 +128,6 @@ void QQnxCamera::start()
}
m_viewfinderActive = true;
-
- if (m_session)
- m_videoSink = m_session->videoSink();
-
- if (isVideoEncodingSupported() && m_outputUrl.isValid())
- startVideoRecording();
}
void QQnxCamera::stop()
@@ -177,35 +142,17 @@ void QQnxCamera::stop()
qWarning("QQnxCamera: Failed to stop camera");
m_viewfinderActive = false;
-
- m_videoSink = nullptr;
}
-void QQnxCamera::setCamera(const QCameraDevice &camera)
-{
- if (m_camera == camera)
- return;
-
- stop();
-
- m_handle = {};
-
- m_camera = camera;
- m_cameraUnit = camera_unit_t(camera.id().toUInt());
-
- if (!m_handle.open(m_cameraUnit, CAMERA_MODE_RO|CAMERA_MODE_PWRITE))
- qWarning("QQnxCamera: Failed to open camera (0x%x)", m_handle.lastError());
-}
-
-bool QQnxCamera::setCameraFormat(const QCameraFormat &format)
+bool QQnxCamera::setCameraFormat(uint32_t width, uint32_t height, double frameRate)
{
if (!m_handle.isOpen())
return false;
const camera_error_t error = camera_set_vf_property(m_handle.get(),
- CAMERA_IMGPROP_WIDTH, format.resolution().width(),
- CAMERA_IMGPROP_HEIGHT, format.resolution().height(),
- CAMERA_IMGPROP_FRAMERATE, format.maxFrameRate());
+ CAMERA_IMGPROP_WIDTH, width,
+ CAMERA_IMGPROP_HEIGHT, height,
+ CAMERA_IMGPROP_FRAMERATE, frameRate);
if (error != CAMERA_EOK) {
qWarning("QQnxCamera: failed to set camera format");
@@ -215,132 +162,264 @@ bool QQnxCamera::setCameraFormat(const QCameraFormat &format)
return true;
}
-void QQnxCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
+bool QQnxCamera::isFocusModeSupported(camera_focusmode_t mode) const
{
- if (m_session == session)
- return;
- m_session = static_cast<QQnxMediaCaptureSession *>(session);
+ return supportedFocusModes().contains(mode);
}
-bool QQnxCamera::isFocusModeSupported(QCamera::FocusMode mode) const
+bool QQnxCamera::setFocusMode(camera_focusmode_t mode)
{
- return supportedFocusModes().contains(::qnxFocusMode(mode));
+ if (!isActive())
+ return false;
+
+ const camera_error_t result = camera_set_focus_mode(m_handle.get(), mode);
+
+ if (result != CAMERA_EOK) {
+ qWarning("QQnxCamera: Unable to set focus mode (0x%x)", result);
+ return false;
+ }
+
+ focusModeChanged(mode);
+
+ return true;
}
-void QQnxCamera::setFocusMode(QCamera::FocusMode mode)
+camera_focusmode_t QQnxCamera::focusMode() const
{
if (!isActive())
- return;
+ return CAMERA_FOCUSMODE_OFF;
+
+ camera_focusmode_t mode;
- const camera_error_t result = camera_set_focus_mode(m_handle.get(), ::qnxFocusMode(mode));
+ const camera_error_t result = camera_get_focus_mode(m_handle.get(), &mode);
if (result != CAMERA_EOK) {
qWarning("QQnxCamera: Unable to set focus mode (0x%x)", result);
- return;
+ return CAMERA_FOCUSMODE_OFF;
}
- focusModeChanged(mode);
+ return mode;
+}
+
+QQnxCamera::VideoFormat QQnxCamera::vfFormat() const
+{
+ VideoFormat f = {};
+
+ if (camera_get_vf_property(m_handle.get(),
+ CAMERA_IMGPROP_WIDTH, &f.width,
+ CAMERA_IMGPROP_HEIGHT, &f.height,
+ CAMERA_IMGPROP_ROTATION, &f.rotation,
+ CAMERA_IMGPROP_FRAMERATE, &f.frameRate,
+ CAMERA_IMGPROP_FORMAT, &f.frameType) != CAMERA_EOK) {
+ qWarning("QQnxCamera: Failed to query video finder frameType");
+ }
+
+ return f;
+}
+
+void QQnxCamera::setVfFormat(const VideoFormat &f)
+{
+ const bool active = isActive();
+
+ if (active)
+ stop();
+
+ if (camera_set_vf_property(m_handle.get(),
+ CAMERA_IMGPROP_WIDTH, f.width,
+ CAMERA_IMGPROP_HEIGHT, f.height,
+ CAMERA_IMGPROP_ROTATION, f.rotation,
+ CAMERA_IMGPROP_FRAMERATE, f.frameRate,
+ CAMERA_IMGPROP_FORMAT, f.frameType) != CAMERA_EOK) {
+ qWarning("QQnxCamera: Failed to set video finder frameType");
+ }
+
+ if (active)
+ start();
+}
+
+QQnxCamera::VideoFormat QQnxCamera::recordingFormat() const
+{
+ VideoFormat f = {};
+
+ if (camera_get_video_property(m_handle.get(),
+ CAMERA_IMGPROP_WIDTH, &f.width,
+ CAMERA_IMGPROP_HEIGHT, &f.height,
+ CAMERA_IMGPROP_ROTATION, &f.rotation,
+ CAMERA_IMGPROP_FRAMERATE, &f.frameRate,
+ CAMERA_IMGPROP_FORMAT, &f.frameType) != CAMERA_EOK) {
+ qWarning("QQnxCamera: Failed to query recording frameType");
+ }
+
+ return f;
+}
+
+void QQnxCamera::setRecordingFormat(const VideoFormat &f)
+{
+ if (camera_set_video_property(m_handle.get(),
+ CAMERA_IMGPROP_WIDTH, f.width,
+ CAMERA_IMGPROP_HEIGHT, f.height,
+ CAMERA_IMGPROP_ROTATION, f.rotation,
+ CAMERA_IMGPROP_FRAMERATE, f.frameRate,
+ CAMERA_IMGPROP_FORMAT, f.frameType) != CAMERA_EOK) {
+ qWarning("QQnxCamera: Failed to set recording frameType");
+ }
}
void QQnxCamera::setCustomFocusPoint(const QPointF &point)
{
- // get the size of the viewfinder
- int width = 0;
- int height = 0;
- auto result = camera_get_vf_property(m_handle.get(),
- CAMERA_IMGPROP_WIDTH, width,
- CAMERA_IMGPROP_HEIGHT, height);
- if (result != CAMERA_EOK)
+ const QSize vfSize = viewFinderSize();
+
+ if (vfSize.isEmpty())
return;
+ const auto toUint32 = [](double value) {
+ return static_cast<uint32_t>(std::max(0.0, value));
+ };
+
// define a 40x40 pixel focus region around the custom focus point
- camera_region_t focusRegion;
- focusRegion.left = qMax(0, static_cast<int>(point.x() * width) - 20);
- focusRegion.top = qMax(0, static_cast<int>(point.y() * height) - 20);
- focusRegion.width = 40;
- focusRegion.height = 40;
+ constexpr int pixelSize = 40;
- result = camera_set_focus_regions(m_handle.get(), 1, &focusRegion);
- if (result != CAMERA_EOK) {
- qWarning("QQnxCamera: Unable to set focus region (0x%x)", result);
- return;
- }
- auto qnxMode = ::qnxFocusMode(focusMode());
- result = camera_set_focus_mode(m_handle.get(), qnxMode);
- if (result != CAMERA_EOK) {
- qWarning("QQnxCamera: Unable to set focus region (0x%x)", result);
+ const auto left = toUint32(point.x() * vfSize.width() - pixelSize / 2);
+ const auto top = toUint32(point.y() * vfSize.height() - pixelSize / 2);
+
+ camera_region_t focusRegion {
+ .left = left,
+ .top = top,
+ .width = pixelSize,
+ .height = pixelSize,
+ .extra = 0
+ };
+
+ if (camera_set_focus_regions(m_handle.get(), 1, &focusRegion) != CAMERA_EOK) {
+ qWarning("QQnxCamera: Unable to set focus region");
return;
}
- customFocusPointChanged(point);
+
+ if (setFocusMode(focusMode()))
+ customFocusPointChanged(point);
}
-void QQnxCamera::setFocusDistance(float distance)
+void QQnxCamera::setManualFocusStep(int step)
{
- if (!isActive() || !isFocusModeSupported(QCamera::FocusModeManual))
+ if (!isActive()) {
+ qWarning("QQnxCamera: Failed to set focus distance - view finder not active");
return;
+ }
- const int maxDistance = maxFocusDistance();
-
- if (maxDistance < 0)
+ if (!isFocusModeSupported(CAMERA_FOCUSMODE_MANUAL)) {
+ qWarning("QQnxCamera: Failed to set focus distance - manual focus mode not supported");
return;
+ }
- const int qnxDistance = maxDistance * std::min(distance, 1.0f);
-
- if (camera_set_manual_focus_step(m_handle.get(), qnxDistance) != CAMERA_EOK)
+ if (camera_set_manual_focus_step(m_handle.get(), step) != CAMERA_EOK)
qWarning("QQnxCamera: Failed to set focus distance");
}
-int QQnxCamera::maxFocusDistance() const
+int QQnxCamera::manualFocusStep() const
{
- if (!isActive() || !isFocusModeSupported(QCamera::FocusModeManual))
- return -1;
+ return focusStep().step;
+}
- int maxstep;
- int step;
+int QQnxCamera::maxFocusStep() const
+{
+ return focusStep().maxStep;
+}
- if (camera_get_manual_focus_step(m_handle.get(), &maxstep, &step) != CAMERA_EOK) {
+QQnxCamera::FocusStep QQnxCamera::focusStep() const
+{
+ constexpr FocusStep invalidStep { -1, -1 };
+
+ if (!isActive()) {
+ qWarning("QQnxCamera: Failed to query max focus distance - view finder not active");
+ return invalidStep;
+ }
+
+ if (!isFocusModeSupported(CAMERA_FOCUSMODE_MANUAL)) {
+ qWarning("QQnxCamera: Failed to query max focus distance - "
+ "manual focus mode not supported");
+ return invalidStep;
+ }
+
+ FocusStep focusStep;
+
+ if (camera_get_manual_focus_step(m_handle.get(),
+ &focusStep.maxStep, &focusStep.step) != CAMERA_EOK) {
qWarning("QQnxCamera: Unable to query camera focus step");
- return -1;
+ return invalidStep;
}
- return maxstep;
+ return focusStep;
}
-void QQnxCamera::zoomTo(float factor, float)
+
+QSize QQnxCamera::viewFinderSize() const
{
- if (!isActive())
- return;
+ // get the size of the viewfinder
+ int width = 0;
+ int height = 0;
- if (maxZoom <= minZoom)
- return;
- // QNX has an integer based API. Interpolate between the levels according to the factor we get
- const float max = maxZoomFactor();
- const float min = minZoomFactor();
- if (max <= min)
- return;
- factor = qBound(min, factor, max) - min;
- uint zoom = minZoom + (uint)qRound(factor*(maxZoom - minZoom)/(max - min));
+ if (camera_get_vf_property(m_handle.get(),
+ CAMERA_IMGPROP_WIDTH, width,
+ CAMERA_IMGPROP_HEIGHT, height) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to query view finder size");
+ return {};
+ }
- auto error = camera_set_vf_property(m_handle.get(), CAMERA_IMGPROP_ZOOMFACTOR, zoom);
- if (error == CAMERA_EOK)
- zoomFactorChanged(factor);
+ return { width, height };
}
-void QQnxCamera::setExposureCompensation(float ev)
+uint32_t QQnxCamera::minimumZoomLevel() const
+{
+ return m_minZoom;
+}
+
+uint32_t QQnxCamera::maximumZoomLevel() const
+{
+ return m_maxZoom;
+}
+
+bool QQnxCamera::isSmoothZoom() const
+{
+ return m_smoothZoom;
+}
+
+double QQnxCamera::zoomRatio(uint32_t zoomLevel) const
+{
+ double ratio;
+
+ if (camera_get_zoom_ratio_from_zoom_level(m_handle.get(), zoomLevel, &ratio) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to query zoom ratio from zoom level");
+ return 0.0;
+ }
+
+ return ratio;
+}
+
+bool QQnxCamera::setZoomFactor(uint32_t factor)
+{
+ if (camera_set_vf_property(m_handle.get(), CAMERA_IMGPROP_ZOOMFACTOR, factor) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to set zoom factor");
+ return false;
+ }
+
+ return true;
+}
+
+void QQnxCamera::setEvOffset(float ev)
{
if (!isActive())
return;
if (camera_set_ev_offset(m_handle.get(), ev) != CAMERA_EOK)
- qWarning("QQnxCamera: Failed to setup exposure compensation");
+ qWarning("QQnxCamera: Failed to set up exposure compensation");
}
-int QQnxCamera::isoSensitivity() const
+uint32_t QQnxCamera::manualIsoSensitivity() const
{
if (!isActive())
return 0;
- unsigned int isoValue;
+ uint32_t isoValue;
if (camera_get_manual_iso(m_handle.get(), &isoValue) != CAMERA_EOK) {
qWarning("QQnxCamera: Failed to query ISO value");
@@ -350,18 +429,16 @@ int QQnxCamera::isoSensitivity() const
return isoValue;
}
-void QQnxCamera::setManualIsoSensitivity(int value)
+void QQnxCamera::setManualIsoSensitivity(uint32_t value)
{
if (!isActive())
return;
- const unsigned int isoValue = std::max(0, value);
-
- if (camera_set_manual_iso(m_handle.get(), isoValue) != CAMERA_EOK)
+ if (camera_set_manual_iso(m_handle.get(), value) != CAMERA_EOK)
qWarning("QQnxCamera: Failed to set ISO value");
}
-void QQnxCamera::setManualExposureTime(float seconds)
+void QQnxCamera::setManualExposureTime(double seconds)
{
if (!isActive())
return;
@@ -370,95 +447,120 @@ void QQnxCamera::setManualExposureTime(float seconds)
qWarning("QQnxCamera: Failed to set exposure time");
}
-float QQnxCamera::exposureTime() const
+double QQnxCamera::manualExposureTime() const
{
if (!isActive())
- return 0;
+ return 0.0;
double shutterSpeed;
if (camera_get_manual_shutter_speed(m_handle.get(), &shutterSpeed) != CAMERA_EOK) {
qWarning("QQnxCamera: Failed to get exposure time");
- return 0;
+ return 0.0;
}
- return static_cast<float>(shutterSpeed);
+ return shutterSpeed;
}
-bool QQnxCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
+bool QQnxCamera::hasFeature(camera_feature_t feature) const
{
- if (!whiteBalanceModesChecked) {
- whiteBalanceModesChecked = true;
- unsigned numWhiteBalanceValues = 0;
- auto error = camera_get_supported_manual_white_balance_values(m_handle.get(), 0, &numWhiteBalanceValues,
- nullptr, &continuousColorTemperatureSupported);
- if (error == CAMERA_EOK) {
- manualColorTemperatureValues.resize(numWhiteBalanceValues);
- auto error = camera_get_supported_manual_white_balance_values(m_handle.get(), numWhiteBalanceValues, &numWhiteBalanceValues,
- manualColorTemperatureValues.data(),
- &continuousColorTemperatureSupported);
+ return camera_has_feature(m_handle.get(), feature);
+}
- minColorTemperature = 1024*1014; // large enough :)
- for (int temp : qAsConst(manualColorTemperatureValues)) {
- minColorTemperature = qMin(minColorTemperature, temp);
- maxColorTemperature = qMax(maxColorTemperature, temp);
- }
- } else {
- maxColorTemperature = 0;
- }
+void QQnxCamera::setWhiteBalanceMode(camera_whitebalancemode_t mode)
+{
+ if (!isActive())
+ return;
+
+ if (camera_set_whitebalance_mode(m_handle.get(), mode) != CAMERA_EOK)
+ qWarning("QQnxCamera: failed to set whitebalance mode");
+}
+
+camera_whitebalancemode_t QQnxCamera::whiteBalanceMode() const
+{
+ if (!isActive())
+ return CAMERA_WHITEBALANCEMODE_OFF;
+
+ camera_whitebalancemode_t mode;
+
+ if (camera_get_whitebalance_mode(m_handle.get(), &mode) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to get white balance mode");
+ return CAMERA_WHITEBALANCEMODE_OFF;
}
- if (maxColorTemperature != 0)
- return true;
- return mode == QCamera::WhiteBalanceAuto;
+ return mode;
}
-void QQnxCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
+void QQnxCamera::setManualWhiteBalance(uint32_t value)
{
- if (mode == QCamera::WhiteBalanceAuto) {
- camera_set_whitebalance_mode(m_handle.get(), CAMERA_WHITEBALANCEMODE_AUTO);
+ if (!isActive())
return;
- }
- camera_set_whitebalance_mode(m_handle.get(), CAMERA_WHITEBALANCEMODE_MANUAL);
- setColorTemperature(colorTemperatureForWhiteBalance(mode));
+
+ if (camera_set_manual_white_balance(m_handle.get(), value) != CAMERA_EOK)
+ qWarning("QQnxCamera: failed to set manual white balance");
}
-void QQnxCamera::setColorTemperature(int temperature)
+uint32_t QQnxCamera::manualWhiteBalance() const
{
+ if (!isActive())
+ return 0;
- if (maxColorTemperature == 0)
- return;
+ uint32_t value;
- unsigned bestTemp = 0;
- if (!continuousColorTemperatureSupported) {
- // find the closest match
- int delta = 1024*1024;
- for (unsigned temp : qAsConst(manualColorTemperatureValues)) {
- int d = qAbs(int(temp) - temperature);
- if (d < delta) {
- bestTemp = temp;
- delta = d;
- }
- }
- } else {
- bestTemp = (unsigned)qBound(minColorTemperature, temperature, maxColorTemperature);
+ if (camera_get_manual_white_balance(m_handle.get(), &value) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to get manual white balance");
+ return 0;
}
- auto error = camera_set_manual_white_balance(m_handle.get(), bestTemp);
+ return value;
}
-void QQnxCamera::startVideoRecording()
+bool QQnxCamera::startVideoRecording(const QString &filename)
{
- const QString container = m_encoderSettings.mimeType().preferredSuffix();
- const QString location = QMediaStorageLocation::generateFileName(m_outputUrl.toLocalFile(),
- QStandardPaths::MoviesLocation, container);
+ // when preview is video, we must ensure that the recording properties
+ // match the view finder properties
+ if (hasFeature(CAMERA_FEATURE_PREVIEWISVIDEO)) {
+ VideoFormat newFormat = vfFormat();
- if (camera_start_video(m_handle.get(), qPrintable(location),
- nullptr, nullptr, nullptr) != CAMERA_EOK) {
- qWarning("QQnxCamera: failed to start video encoding");
- } else {
+ const QList<camera_frametype_t> recordingTypes = supportedRecordingFrameTypes();
+
+ // find a suitable matching frame type in case the current view finder
+ // frametype is not supported
+ if (newFormat.frameType != recordingFormat().frameType
+ && !recordingTypes.contains(newFormat.frameType)) {
+
+ bool found = false;
+
+ for (const camera_frametype_t type : supportedVfFrameTypes()) {
+ if (recordingTypes.contains(type)) {
+ newFormat.frameType = type;
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ m_originalVfFormat = vfFormat();
+
+ // reconfigure and restart the view finder
+ setVfFormat(newFormat);
+ } else {
+ qWarning("QQnxCamera: failed to find suitable frame type for recording - aborting");
+ return false;
+ }
+ }
+
+ setRecordingFormat(newFormat);
+ }
+
+ if (camera_start_video(m_handle.get(), qPrintable(filename),
+ nullptr, nullptr, nullptr) == CAMERA_EOK) {
m_recordingVideo = true;
+ } else {
+ qWarning("QQnxCamera: failed to start video encoding");
}
+
+ return m_recordingVideo;
}
void QQnxCamera::stopVideoRecording()
@@ -467,6 +569,12 @@ void QQnxCamera::stopVideoRecording()
if (camera_stop_video(m_handle.get()) != CAMERA_EOK)
qWarning("QQnxCamera: error when stopping video recording");
+
+ // restore original vf format
+ if (m_originalVfFormat) {
+ setVfFormat(*m_originalVfFormat);
+ m_originalVfFormat.reset();
+ }
}
bool QQnxCamera::isVideoEncodingSupported() const
@@ -477,47 +585,50 @@ bool QQnxCamera::isVideoEncodingSupported() const
return camera_has_feature(m_handle.get(), CAMERA_FEATURE_VIDEO);
}
-void QQnxCamera::setOutputUrl(const QUrl &url)
+camera_handle_t QQnxCamera::handle() const
{
- m_outputUrl = url;
+ return m_handle.get();
}
-void QQnxCamera::setMediaEncoderSettings(const QMediaEncoderSettings &settings)
+void QQnxCamera::updateZoomLimits()
{
- m_encoderSettings = settings;
-}
+ bool smooth;
-camera_handle_t QQnxCamera::handle() const
-{
- return m_handle.get();
+ if (camera_get_zoom_limits(m_handle.get(), &m_minZoom, &m_maxZoom, &smooth) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to update zoom limits - using default values");
+ m_minZoom = m_maxZoom = 0;
+ }
}
-void QQnxCamera::updateCameraFeatures()
+void QQnxCamera::updateSupportedWhiteBalanceValues()
{
- whiteBalanceModesChecked = false;
+ uint32_t numSupported = 0;
- bool smooth;
- const camera_error_t error = camera_get_zoom_limits(m_handle.get(),
- &minZoom, &maxZoom, &smooth);
-
- if (error == CAMERA_EOK) {
- double level;
- camera_get_zoom_ratio_from_zoom_level(m_handle.get(), minZoom, &level);
- minimumZoomFactorChanged(level);
- camera_get_zoom_ratio_from_zoom_level(m_handle.get(), maxZoom, &level);
- maximumZoomFactorChanged(level);
- } else {
- minZoom = maxZoom = 1;
+ const camera_error_t result = camera_get_supported_manual_white_balance_values(
+ m_handle.get(), 0, &numSupported, nullptr, &m_continuousWhiteBalanceValues);
+
+ if (result != CAMERA_EOK) {
+ if (result == CAMERA_EOPNOTSUPP)
+ qWarning("QQnxCamera: white balance not supported");
+ else
+ qWarning("QQnxCamera: unable to query manual white balance value count");
+
+ m_supportedWhiteBalanceValues.clear();
+
+ return;
}
- QCamera::Features features = {};
+ m_supportedWhiteBalanceValues.resize(numSupported);
- if (camera_has_feature(m_handle.get(), CAMERA_FEATURE_REGIONFOCUS))
- features |= QCamera::Feature::CustomFocusPoint;
+ if (camera_get_supported_manual_white_balance_values(m_handle.get(),
+ m_supportedWhiteBalanceValues.size(),
+ &numSupported,
+ m_supportedWhiteBalanceValues.data(),
+ &m_continuousWhiteBalanceValues) != CAMERA_EOK) {
+ qWarning("QQnxCamera: unable to query manual white balance values");
- minimumZoomFactorChanged(minZoom);
- maximumZoomFactorChanged(maxZoom);
- supportedFeaturesChanged(features);
+ m_supportedWhiteBalanceValues.clear();
+ }
}
QList<camera_vfmode_t> QQnxCamera::supportedVfModes() const
@@ -530,19 +641,78 @@ QList<camera_res_t> QQnxCamera::supportedVfResolutions() const
return queryValues(camera_get_supported_vf_resolutions);
}
+QList<camera_frametype_t> QQnxCamera::supportedVfFrameTypes() const
+{
+ return queryValues(camera_get_supported_vf_frame_types);
+}
+
QList<camera_focusmode_t> QQnxCamera::supportedFocusModes() const
{
return queryValues(camera_get_focus_modes);
}
+QList<double> QQnxCamera::specifiedVfFrameRates(camera_frametype_t frameType,
+ camera_res_t resolution) const
+{
+ uint32_t numSupported = 0;
+
+ if (camera_get_specified_vf_framerates(m_handle.get(), frameType, resolution,
+ 0, &numSupported, nullptr, nullptr) != CAMERA_EOK) {
+ qWarning("QQnxCamera: unable to query specified framerates count");
+ return {};
+ }
+
+ QList<double> values(numSupported);
+
+ if (camera_get_specified_vf_framerates(m_handle.get(), frameType, resolution,
+ values.size(), &numSupported, values.data(), nullptr) != CAMERA_EOK) {
+ qWarning("QQnxCamera: unable to query specified framerates values");
+ return {};
+ }
+
+ return values;
+}
+
+QList<camera_frametype_t> QQnxCamera::supportedRecordingFrameTypes() const
+{
+ return queryValues(camera_get_video_frame_types);
+}
+
+QList<uint32_t> QQnxCamera::supportedWhiteBalanceValues() const
+{
+ return m_supportedWhiteBalanceValues;
+}
+
+bool QQnxCamera::hasContinuousWhiteBalanceValues() const
+{
+ return m_continuousWhiteBalanceValues;
+}
+
+QList<camera_unit_t> QQnxCamera::supportedUnits()
+{
+ unsigned int numSupported = 0;
+
+ if (camera_get_supported_cameras(0, &numSupported, nullptr) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to query supported camera unit count");
+ return {};
+ }
+
+ QList<camera_unit_t> cameraUnits(numSupported);
+
+ if (camera_get_supported_cameras(cameraUnits.size(), &numSupported,
+ cameraUnits.data()) != CAMERA_EOK) {
+ qWarning("QQnxCamera: failed to enumerate supported camera units");
+ return {};
+ }
+
+ return cameraUnits;
+}
+
template <typename T, typename U>
QList<T> QQnxCamera::queryValues(QueryFuncPtr<T,U> func) const
{
static_assert(std::is_integral_v<U>, "Parameter U must be of integral type");
- if (!isActive())
- return {};
-
U numSupported = 0;
if (func(m_handle.get(), 0, &numSupported, nullptr) != CAMERA_EOK) {
@@ -562,9 +732,6 @@ QList<T> QQnxCamera::queryValues(QueryFuncPtr<T,U> func) const
void QQnxCamera::handleVfBuffer(camera_buffer_t *buffer)
{
- if (!m_videoSink)
- return;
-
// process the frame on this thread before locking the mutex
auto frame = std::make_unique<QQnxCameraFrameBuffer>(buffer);
@@ -573,9 +740,8 @@ void QQnxCamera::handleVfBuffer(camera_buffer_t *buffer)
m_currentFrame = std::move(frame);
m_currentFrameMutex.unlock();
- QMetaObject::invokeMethod(this, "processFrame", Qt::QueuedConnection);
+ Q_EMIT frameAvailable();
}
-
}
void QQnxCamera::handleVfStatus(camera_devstatus_t status, uint16_t extraData)
@@ -590,34 +756,46 @@ void QQnxCamera::handleStatusChange(camera_devstatus_t status, uint16_t extraDat
Q_UNUSED(extraData);
switch (status) {
- case CAMERA_STATUS_DISCONNECTED:
- case CAMERA_STATUS_POWERDOWN:
- case CAMERA_STATUS_VIDEOVF:
+ case CAMERA_STATUS_BUFFER_UNDERFLOW:
+ case CAMERA_STATUS_CAPTURECOMPLETE:
case CAMERA_STATUS_CAPTURE_ABORTED:
+ case CAMERA_STATUS_CONNECTED:
+ case CAMERA_STATUS_DISCONNECTED:
+ case CAMERA_STATUS_FILESIZE_ERROR:
+ case CAMERA_STATUS_FILESIZE_LIMIT_WARNING:
case CAMERA_STATUS_FILESIZE_WARNING:
+ case CAMERA_STATUS_FLASH_LEVEL_CHANGE:
case CAMERA_STATUS_FOCUS_CHANGE:
- case CAMERA_STATUS_RESOURCENOTAVAIL:
- case CAMERA_STATUS_VIEWFINDER_ERROR:
+ case CAMERA_STATUS_FRAME_DROPPED:
+ case CAMERA_STATUS_LOWLIGHT:
case CAMERA_STATUS_MM_ERROR:
- case CAMERA_STATUS_FILESIZE_ERROR:
case CAMERA_STATUS_NOSPACE_ERROR:
- case CAMERA_STATUS_BUFFER_UNDERFLOW:
- Q_EMIT(status, ::statusToString(status));
- stop();
- break;
- default:
+ case CAMERA_STATUS_PHOTOVF:
+ case CAMERA_STATUS_POWERDOWN:
+ case CAMERA_STATUS_POWERUP:
+ case CAMERA_STATUS_RESOURCENOTAVAIL:
+ case CAMERA_STATUS_UNKNOWN:
+ case CAMERA_STATUS_VIDEOLIGHT_CHANGE:
+ case CAMERA_STATUS_VIDEOLIGHT_LEVEL_CHANGE:
+ case CAMERA_STATUS_VIDEOVF:
+ case CAMERA_STATUS_VIDEO_PAUSE:
+ case CAMERA_STATUS_VIDEO_RESUME:
+ case CAMERA_STATUS_VIEWFINDER_ACTIVE:
+ case CAMERA_STATUS_VIEWFINDER_ERROR:
+ case CAMERA_STATUS_VIEWFINDER_FREEZE:
+ case CAMERA_STATUS_VIEWFINDER_SUSPEND:
+ case CAMERA_STATUS_VIEWFINDER_UNFREEZE:
+ case CAMERA_STATUS_VIEWFINDER_UNSUSPEND:
+ qDebug() << "QQnxCamera:" << ::statusToString(status);
break;
}
}
-void QQnxCamera::processFrame()
+std::unique_ptr<QQnxCameraFrameBuffer> QQnxCamera::takeCurrentFrame()
{
QMutexLocker l(&m_currentFrameMutex);
- const QVideoFrame actualFrame(m_currentFrame.get(),
- QVideoFrameFormat(m_currentFrame->size(), m_currentFrame->pixelFormat()));
-
- m_videoSink->setVideoFrame(actualFrame);
+ return std::move(m_currentFrame);
}
void QQnxCamera::viewfinderCallback(camera_handle_t handle, camera_buffer_t *buffer, void *arg)
@@ -638,3 +816,5 @@ void QQnxCamera::statusCallback(camera_handle_t handle, camera_devstatus_t statu
}
QT_END_NAMESPACE
+
+#include "moc_qqnxcamera_p.cpp"
diff --git a/src/plugins/multimedia/qnx/camera/qqnxcamera_p.h b/src/plugins/multimedia/qnx/camera/qqnxcamera_p.h
index aab112000..a4ddbfed6 100644
--- a/src/plugins/multimedia/qnx/camera/qqnxcamera_p.h
+++ b/src/plugins/multimedia/qnx/camera/qqnxcamera_p.h
@@ -1,43 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QQnxCamera_H
-#define QQnxCamera_H
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQNXCAMERA_H
+#define QQNXCAMERA_H
//
// W A R N I N G
@@ -50,17 +15,18 @@
// We mean it.
//
-#include <private/qplatformcamera_p.h>
-#include <private/qplatformmediarecorder_p.h>
+#include "qqnxcamerahandle_p.h"
#include <QtCore/qlist.h>
#include <QtCore/qmutex.h>
+#include <QtCore/qobject.h>
#include <QtCore/qurl.h>
#include <camera/camera_api.h>
#include <camera/camera_3a.h>
#include <memory>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -68,156 +34,128 @@ class QQnxCameraFrameBuffer;
class QQnxMediaCaptureSession;
class QQnxVideoSink;
-class CameraHandle
+class QQnxCamera : public QObject
{
+ Q_OBJECT
public:
- CameraHandle() = default;
-
- explicit CameraHandle(camera_handle_t h)
- : m_handle (h) {}
-
- explicit CameraHandle(CameraHandle &&other)
- : m_handle(other.m_handle)
- , m_lastError(other.m_lastError)
- {
- other = CameraHandle();
- }
-
- CameraHandle(const CameraHandle&) = delete;
+ explicit QQnxCamera(camera_unit_t unit, QObject *parent = nullptr);
+ ~QQnxCamera();
- CameraHandle& operator=(CameraHandle&& other)
- {
- m_handle = other.m_handle;
- m_lastError = other.m_lastError;
+ camera_unit_t unit() const;
- other = CameraHandle();
+ QString name() const;
- return *this;
- }
+ bool isValid() const;
- ~CameraHandle()
- {
- close();
- }
+ bool isActive() const;
+ void start();
+ void stop();
- bool open(camera_unit_t unit, uint32_t mode)
- {
- if (isOpen()) {
- m_lastError = CAMERA_EALREADY;
- return false;
- }
+ bool startVideoRecording(const QString &filename);
+ void stopVideoRecording();
- return cacheError(camera_open, unit, mode, &m_handle);
- }
+ bool setCameraFormat(uint32_t width, uint32_t height, double frameRate);
- bool close()
- {
- if (!isOpen())
- return true;
+ bool isFocusModeSupported(camera_focusmode_t mode) const;
+ bool setFocusMode(camera_focusmode_t mode);
+ camera_focusmode_t focusMode() const;
- const bool success = cacheError(camera_close, m_handle);
- m_handle = CAMERA_HANDLE_INVALID;
+ void setCustomFocusPoint(const QPointF &point);
- return success;
- }
+ void setManualFocusStep(int step);
+ int manualFocusStep() const;
+ int maxFocusStep() const;
- camera_handle_t get() const
- {
- return m_handle;
- }
+ QSize viewFinderSize() const;
- bool isOpen() const
- {
- return m_handle != CAMERA_HANDLE_INVALID;
- }
+ uint32_t minimumZoomLevel() const;
+ uint32_t maximumZoomLevel() const;
+ bool isSmoothZoom() const;
+ double zoomRatio(uint32_t zoomLevel) const;
+ bool setZoomFactor(uint32_t factor);
- camera_error_t lastError() const
- {
- return m_lastError;
- }
+ void setEvOffset(float ev);
-private:
- template <typename Func, typename ...Args>
- bool cacheError(Func f, Args &&...args)
- {
- m_lastError = f(std::forward<Args>(args)...);
+ uint32_t manualIsoSensitivity() const;
+ void setManualIsoSensitivity(uint32_t value);
+ void setManualExposureTime(double seconds);
+ double manualExposureTime() const;
- return m_lastError == CAMERA_EOK;
- }
+ void setWhiteBalanceMode(camera_whitebalancemode_t mode);
+ camera_whitebalancemode_t whiteBalanceMode() const;
- camera_handle_t m_handle = CAMERA_HANDLE_INVALID;
- camera_error_t m_lastError = CAMERA_EOK;
-};
+ void setManualWhiteBalance(uint32_t value);
+ uint32_t manualWhiteBalance() const;
+ bool hasFeature(camera_feature_t feature) const;
-class QQnxCamera : public QPlatformCamera
-{
- Q_OBJECT
-public:
- explicit QQnxCamera(QCamera *parent);
- ~QQnxCamera();
+ camera_handle_t handle() const;
- bool isActive() const override;
- void setActive(bool active) override;
- void start();
- void stop();
+ QList<camera_vfmode_t> supportedVfModes() const;
+ QList<camera_res_t> supportedVfResolutions() const;
+ QList<camera_frametype_t> supportedVfFrameTypes() const;
+ QList<camera_focusmode_t> supportedFocusModes() const;
+ QList<double> specifiedVfFrameRates(camera_frametype_t frameType,
+ camera_res_t resolution) const;
- void setCamera(const QCameraDevice &camera) override;
+ QList<camera_frametype_t> supportedRecordingFrameTypes() const;
- bool setCameraFormat(const QCameraFormat &format) override;
+ QList<uint32_t> supportedWhiteBalanceValues() const;
- void setCaptureSession(QPlatformMediaCaptureSession *session) override;
+ bool hasContinuousWhiteBalanceValues() const;
- bool isFocusModeSupported(QCamera::FocusMode mode) const override;
- void setFocusMode(QCamera::FocusMode mode) override;
+ static QList<camera_unit_t> supportedUnits();
- void setCustomFocusPoint(const QPointF &point) override;
+ std::unique_ptr<QQnxCameraFrameBuffer> takeCurrentFrame();
- void setFocusDistance(float distance) override;
+Q_SIGNALS:
+ void focusModeChanged(camera_focusmode_t mode);
+ void customFocusPointChanged(const QPointF &point);
+ void minimumZoomFactorChanged(double factor);
- int maxFocusDistance() const;
+ double maximumZoomFactorChanged(double factor);
- void zoomTo(float /*newZoomFactor*/, float /*rate*/ = -1.) override;
+ void frameAvailable();
- void setExposureCompensation(float ev) override;
+private:
+ struct FocusStep
+ {
+ int step; // current step
+ int maxStep; // max supported step
+ };
- int isoSensitivity() const override;
- void setManualIsoSensitivity(int value) override;
- void setManualExposureTime(float seconds) override;
- float exposureTime() const override;
+ FocusStep focusStep() const;
- bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override;
- void setWhiteBalanceMode(QCamera::WhiteBalanceMode /*mode*/) override;
- void setColorTemperature(int /*temperature*/) override;
+ struct VideoFormat
+ {
+ uint32_t width;
+ uint32_t height;
+ uint32_t rotation;
+ double frameRate;
+ camera_frametype_t frameType;
+ };
- void setOutputUrl(const QUrl &url);
- void setMediaEncoderSettings(const QMediaEncoderSettings &settings);
+ friend QDebug &operator<<(QDebug&, const VideoFormat&);
- camera_handle_t handle() const;
+ VideoFormat vfFormat() const;
+ void setVfFormat(const VideoFormat &format);
- QList<camera_vfmode_t> supportedVfModes() const;
- QList<camera_res_t> supportedVfResolutions() const;
- QList<camera_focusmode_t> supportedFocusModes() const;
+ VideoFormat recordingFormat() const;
+ void setRecordingFormat(const VideoFormat &format);
-private:
- void updateCameraFeatures();
+ void updateZoomLimits();
+ void updateSupportedWhiteBalanceValues();
void setColorTemperatureInternal(unsigned temp);
- void startVideoRecording();
- void stopVideoRecording();
-
bool isVideoEncodingSupported() const;
void handleVfBuffer(camera_buffer_t *buffer);
- Q_INVOKABLE void processFrame();
-
// viewfinder callback
void handleVfStatus(camera_devstatus_t status, uint16_t extraData);
// our handler running on main thread
- void handleStatusChange(camera_devstatus_t status, uint16_t extraData);
-
+ Q_INVOKABLE void handleStatusChange(camera_devstatus_t status, uint16_t extraData);
template <typename T, typename U>
using QueryFuncPtr = camera_error_t (*)(camera_handle_t, U, U *, T *);
@@ -225,9 +163,6 @@ private:
template <typename T, typename U>
QList<T> queryValues(QueryFuncPtr<T, U> func) const;
- template <typename ...Args>
- using CamAPIFunc = camera_error_t (*)(camera_handle_t, Args...);
-
static void viewfinderCallback(camera_handle_t handle,
camera_buffer_t *buffer, void *arg);
@@ -235,35 +170,32 @@ private:
uint16_t extraData, void *arg);
QQnxMediaCaptureSession *m_session = nullptr;
- QQnxVideoSink *m_videoSink = nullptr;
- QCameraDevice m_camera;
camera_unit_t m_cameraUnit = CAMERA_UNIT_NONE;
- QUrl m_outputUrl;
-
- QMediaEncoderSettings m_encoderSettings;
-
- CameraHandle m_handle;
+ QQnxCameraHandle m_handle;
- uint minZoom = 1;
- uint maxZoom = 1;
- mutable bool whiteBalanceModesChecked = false;
- mutable bool continuousColorTemperatureSupported = false;
- mutable int minColorTemperature = 0;
- mutable int maxColorTemperature = 0;
- mutable QList<unsigned> manualColorTemperatureValues;
+ uint32_t m_minZoom = 0;
+ uint32_t m_maxZoom = 0;
QMutex m_currentFrameMutex;
+ QList<uint32_t> m_supportedWhiteBalanceValues;
+
std::unique_ptr<QQnxCameraFrameBuffer> m_currentFrame;
+ std::optional<VideoFormat> m_originalVfFormat;
+
bool m_viewfinderActive = false;
bool m_recordingVideo = false;
+ bool m_valid = false;
+ bool m_smoothZoom = false;
+ bool m_continuousWhiteBalanceValues = false;
};
QT_END_NAMESPACE
Q_DECLARE_METATYPE(camera_devstatus_t)
+Q_DECLARE_METATYPE(uint16_t)
#endif
diff --git a/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer.cpp b/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer.cpp
index ba7c7b17f..8ad0894f7 100644
--- a/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer.cpp
+++ b/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer.cpp
@@ -1,46 +1,31 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxcameraframebuffer_p.h"
#include <limits>
+template <typename T>
+static constexpr int toInt(T value)
+{
+ if constexpr (sizeof(T) >= sizeof(int)) {
+ if (std::is_signed_v<T>) {
+ return static_cast<int>(std::clamp<T>(value,
+ std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
+ } else {
+ return static_cast<int>(std::min<T>(value, std::numeric_limits<int>::max()));
+ }
+ } else {
+ return static_cast<int>(value);
+ }
+}
+
+template <typename T>
+static constexpr QSize frameSize(const T &frame)
+{
+ return { toInt(frame.width), toInt(frame.height) };
+}
+
static constexpr QVideoFrameFormat::PixelFormat frameTypeToPixelFormat(camera_frametype_t type)
{
switch (type) {
@@ -122,16 +107,16 @@ static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame,
return {
.nPlanes = 2,
.bytesPerLine = {
- frame.stride,
- frame.uv_stride
+ toInt(frame.stride),
+ toInt(frame.uv_stride)
},
.data = {
baseAddress,
baseAddress + frame.uv_offset
},
.size = {
- frame.stride * frame.height,
- frame.uv_stride * frame.height / 2
+ toInt(frame.stride * frame.height),
+ toInt(frame.uv_stride * frame.height / 2)
}
};
}
@@ -142,13 +127,13 @@ static QAbstractVideoBuffer::MapData mapData(const camera_frame_rgb8888_t &frame
return {
.nPlanes = 1,
.bytesPerLine = {
- frame.stride
+ toInt(frame.stride)
},
.data = {
baseAddress
},
.size = {
- frame.stride * frame.height,
+ toInt(frame.stride * frame.height),
}
};
}
@@ -159,13 +144,13 @@ static QAbstractVideoBuffer::MapData mapData(const camera_frame_gray8_t &frame,
return {
.nPlanes = 1,
.bytesPerLine = {
- frame.stride
+ toInt(frame.stride)
},
.data = {
baseAddress
},
.size = {
- frame.stride * frame.height
+ toInt(frame.stride * frame.height)
}
};
}
@@ -176,13 +161,13 @@ static QAbstractVideoBuffer::MapData mapData(const camera_frame_cbycry_t &frame,
return {
.nPlanes = 1,
.bytesPerLine = {
- frame.stride
+ toInt(frame.stride)
},
.data = {
baseAddress
},
.size = {
- frame.bufsize,
+ toInt(frame.bufsize),
}
};
}
@@ -193,7 +178,7 @@ static QAbstractVideoBuffer::MapData mapData(const camera_frame_ycbcr420p_t &fra
return {
.nPlanes = 3,
.bytesPerLine = {
- frame.y_stride,
+ toInt(frame.y_stride),
frame.cb_stride,
frame.cr_stride,
},
@@ -203,9 +188,9 @@ static QAbstractVideoBuffer::MapData mapData(const camera_frame_ycbcr420p_t &fra
baseAddress + frame.cr_offset,
},
.size = {
- frame.y_stride * frame.height,
- frame.cb_stride * frame.height / 2,
- frame.cr_stride * frame.height / 2
+ toInt(frame.y_stride * frame.height),
+ toInt(frame.cb_stride * frame.height / 2),
+ toInt(frame.cr_stride * frame.height / 2)
}
};
}
@@ -216,13 +201,13 @@ static QAbstractVideoBuffer::MapData mapData(const camera_frame_ycbycr_t &frame,
return {
.nPlanes = 1,
.bytesPerLine = {
- frame.stride
+ toInt(frame.stride)
},
.data = {
baseAddress
},
.size = {
- frame.stride * frame.height
+ toInt(frame.stride * frame.height)
}
};
}
@@ -250,17 +235,6 @@ static QAbstractVideoBuffer::MapData mapData(const camera_buffer_t *buffer,
return {};
}
-static constexpr int toInt(uint32_t value)
-{
- return static_cast<int>(std::min<uint32_t>(value, std::numeric_limits<int>::max()));
-}
-
-template <typename T>
-static constexpr QSize frameSize(const T &frame)
-{
- return { toInt(frame.width), toInt(frame.height) };
-}
-
static constexpr QSize frameSize(const camera_buffer_t *buffer)
{
switch (buffer->frametype) {
diff --git a/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h b/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h
index 7ff603dc0..9fed113a6 100644
--- a/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h
+++ b/src/plugins/multimedia/qnx/camera/qqnxcameraframebuffer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXCAMERAFRAMEBUFFER_H
#define QQNXCAMERAFRAMEBUFFER_H
@@ -74,14 +38,6 @@ public:
MapData map(QVideoFrame::MapMode mode) override;
void unmap() override;
-#if 0
- virtual void mapTextures() {}
- virtual quint64 textureHandle(int /*plane*/) const { return 0; }
- virtual std::unique_ptr<QRhiTexture> texture(int /*plane*/) const;
-
- virtual QMatrix4x4 externalTextureMatrix() const { return {}; }
-#endif
-
QVideoFrameFormat::PixelFormat pixelFormat() const;
QSize size() const;
diff --git a/src/plugins/multimedia/qnx/camera/qqnxcamerahandle_p.h b/src/plugins/multimedia/qnx/camera/qqnxcamerahandle_p.h
new file mode 100644
index 000000000..3d7863dc2
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnxcamerahandle_p.h
@@ -0,0 +1,102 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QQNXCAMERAHANDLE_P_H
+#define QQNXCAMERAHANDLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <camera/camera_api.h>
+
+#include <utility>
+
+class QQnxCameraHandle
+{
+public:
+ QQnxCameraHandle() = default;
+
+ explicit QQnxCameraHandle(camera_handle_t h)
+ : m_handle (h) {}
+
+ explicit QQnxCameraHandle(QQnxCameraHandle &&other)
+ : m_handle(other.m_handle)
+ , m_lastError(other.m_lastError)
+ {
+ other = QQnxCameraHandle();
+ }
+
+ QQnxCameraHandle(const QQnxCameraHandle&) = delete;
+
+ QQnxCameraHandle& operator=(QQnxCameraHandle&& other)
+ {
+ m_handle = other.m_handle;
+ m_lastError = other.m_lastError;
+
+ other = QQnxCameraHandle();
+
+ return *this;
+ }
+
+ ~QQnxCameraHandle()
+ {
+ close();
+ }
+
+ bool open(camera_unit_t unit, uint32_t mode)
+ {
+ if (isOpen()) {
+ m_lastError = CAMERA_EALREADY;
+ return false;
+ }
+
+ return cacheError(camera_open, unit, mode, &m_handle);
+ }
+
+ bool close()
+ {
+ if (!isOpen())
+ return true;
+
+ const bool success = cacheError(camera_close, m_handle);
+ m_handle = CAMERA_HANDLE_INVALID;
+
+ return success;
+ }
+
+ camera_handle_t get() const
+ {
+ return m_handle;
+ }
+
+ bool isOpen() const
+ {
+ return m_handle != CAMERA_HANDLE_INVALID;
+ }
+
+ camera_error_t lastError() const
+ {
+ return m_lastError;
+ }
+
+private:
+ template <typename Func, typename ...Args>
+ bool cacheError(Func f, Args &&...args)
+ {
+ m_lastError = f(std::forward<Args>(args)...);
+
+ return m_lastError == CAMERA_EOK;
+ }
+
+ camera_handle_t m_handle = CAMERA_HANDLE_INVALID;
+ camera_error_t m_lastError = CAMERA_EOK;
+};
+
+#endif
diff --git a/src/plugins/multimedia/qnx/camera/qqnximagecapture.cpp b/src/plugins/multimedia/qnx/camera/qqnximagecapture.cpp
index 6557267b2..3983dddbb 100644
--- a/src/plugins/multimedia/qnx/camera/qqnximagecapture.cpp
+++ b/src/plugins/multimedia/qnx/camera/qqnximagecapture.cpp
@@ -1,49 +1,60 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnximagecapture_p.h"
+#include "qqnxplatformcamera_p.h"
#include "qqnxmediacapturesession_p.h"
#include "qqnxcamera_p.h"
#include "qfile.h"
+#include <private/qmediastoragelocation_p.h>
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qfuture.h>
+#include <QtCore/qpromise.h>
+#include <QtCore/qthread.h>
+
#include <camera/camera_api.h>
+using namespace Qt::Literals::StringLiterals;
+
+static QString formatExtension(QImageCapture::FileFormat format)
+{
+ switch (format) {
+ case QImageCapture::JPEG:
+ return u"jpg"_s;
+ case QImageCapture::PNG:
+ return u"png"_s;
+ case QImageCapture::WebP:
+ case QImageCapture::Tiff:
+ case QImageCapture::UnspecifiedFormat:
+ break;
+ }
+
+ return {};
+}
+
+static QString resolveFileName(const QString &fileName, QImageCapture::FileFormat format)
+{
+ const QString extension = formatExtension(format);
+
+ if (extension.isEmpty())
+ return {};
+
+ if (fileName.isEmpty()) {
+ return QMediaStorageLocation::generateFileName(QString(),
+ QStandardPaths::PicturesLocation, extension);
+ }
+
+ if (fileName.endsWith(extension))
+ return QFileInfo(fileName).canonicalFilePath();
+
+ QString path = fileName;
+ path.append(u".%1"_s.arg(extension));
+
+ return QFileInfo(path).canonicalFilePath();
+}
+
QT_BEGIN_NAMESPACE
QQnxImageCapture::QQnxImageCapture(QImageCapture *parent)
@@ -53,131 +64,130 @@ QQnxImageCapture::QQnxImageCapture(QImageCapture *parent)
bool QQnxImageCapture::isReadyForCapture() const
{
- if (!m_session)
- return false;
- auto *camera = static_cast<QQnxCamera *>(m_session->camera());
- // ### add can take photo
- return camera && camera->isActive();
+ return m_camera && m_camera->isActive();
}
-#if 0
-static void imageCaptureShutterCallback(camera_handle_t handle, void *context)
+int QQnxImageCapture::capture(const QString &fileName)
{
- Q_UNUSED(handle);
+ if (!isReadyForCapture()) {
+ Q_EMIT error(-1, QImageCapture::NotReadyError, QPlatformImageCapture::msgCameraNotReady());
+ return -1;
+ }
- const auto *data = static_cast<QQnxImageCapture::PendingImage *>(context);
+ // default to PNG format if no format has been specified
+ const QImageCapture::FileFormat format =
+ m_settings.format() == QImageCapture::UnspecifiedFormat
+ ? QImageCapture::PNG : m_settings.format();
- // We are inside a worker thread here, so emit imageExposed inside the main thread
- QMetaObject::invokeMethod(data->imageCapture, "imageExposed", Qt::QueuedConnection, Q_ARG(int, data->id));
+ const QString resolvedFileName = resolveFileName(fileName, format);
+
+ if (resolvedFileName.isEmpty()) {
+ const QString errorMessage = (u"Invalid file format: %1"_s).arg(
+ QImageCapture::fileFormatName(format));
+
+ Q_EMIT error(-1, QImageCapture::NotSupportedFeatureError, errorMessage);
+ return -1;
+ }
+
+ const int id = m_lastId++;
+
+ auto callback = [this, id, fn=std::move(resolvedFileName)](const QVideoFrame &frame) {
+ saveFrame(id, frame, fn);
+ };
+
+ m_camera->requestVideoFrame(std::move(callback));
+
+ return id;
}
-static void imageCaptureImageCallback(camera_handle_t handle, camera_buffer_t *buffer, void *context)
+int QQnxImageCapture::captureToBuffer()
{
- Q_UNUSED(handle);
+ if (!isReadyForCapture()) {
+ Q_EMIT error(-1, QImageCapture::NotReadyError, QPlatformImageCapture::msgCameraNotReady());
+ return -1;
+ }
- auto *data = static_cast<QQnxImageCapture::PendingImage *>(context);
+ const int id = m_lastId++;
- if (buffer->frametype != CAMERA_FRAMETYPE_JPEG) {
- // ### Fix this, we can support other formats as well!
+ auto callback = [this, id](const QVideoFrame &frame) { decodeFrame(id, frame); };
- // We are inside a worker thread here, so emit error signal inside the main thread
- QMetaObject::invokeMethod(data->imageCapture, "error", Qt::QueuedConnection,
- Q_ARG(int, data->id),
- Q_ARG(QImageCapture::Error, QImageCapture::FormatError),
- Q_ARG(QString, QCamera::tr("Camera provides image in unsupported format")));
- return;
- }
+ m_camera->requestVideoFrame(std::move(callback));
- const QByteArray rawData = QByteArray::fromRawData((const char*)buffer->framebuf, buffer->framedesc.jpeg.bufsize);
-
- QImage image;
- const bool ok = image.loadFromData(rawData, "JPG");
- if (!ok) {
- const QString errorMessage = QCamera::tr("Could not load JPEG data from frame");
- // We are inside a worker thread here, so emit error signal inside the main thread
- QMetaObject::invokeMethod(data->imageCapture, "error", Qt::QueuedConnection,
- Q_ARG(int, data->id),
- Q_ARG(QImageCapture::Error, QImageCapture::FormatError),
- Q_ARG(QString, errorMessage));
- return;
+ return id;
+}
+
+QFuture<QImage> QQnxImageCapture::decodeFrame(int id, const QVideoFrame &frame)
+{
+ if (!frame.isValid()) {
+ Q_EMIT error(id, QImageCapture::NotReadyError, u"Invalid frame"_s);
+ return {};
}
+ QPromise<QImage> promise;
+ QFuture<QImage> future = promise.future();
- // We are inside a worker thread here, so invoke imageCaptured inside the main thread
- QMetaObject::invokeMethod(data->imageCapture, "imageCaptured", Qt::QueuedConnection,
- Q_ARG(int, data->id),
- Q_ARG(QImage, image));
-
- if (!data->filename.isEmpty()) {
- QFile file(data->filename);
- if (file.exists()) {
- const QString errorMessage = QCamera::tr("Could not overwrite existing file");
- // We are inside a worker thread here, so emit error signal inside the main thread
- QMetaObject::invokeMethod(data->imageCapture, "error", Qt::QueuedConnection,
- Q_ARG(int, data->id),
- Q_ARG(QImageCapture::Error, QImageCapture::ResourceError),
- Q_ARG(QString, errorMessage));
- return;
- }
- if (!file.open(QFile::WriteOnly)) {
- const QString errorMessage = QCamera::tr("Could not write image to file");
- // We are inside a worker thread here, so emit error signal inside the main thread
- QMetaObject::invokeMethod(data->imageCapture, "error", Qt::QueuedConnection,
- Q_ARG(int, data->id),
- Q_ARG(QImageCapture::Error, QImageCapture::ResourceError),
- Q_ARG(QString, errorMessage));
- return;
+ // converting a QVideoFrame to QImage is an expensive operation
+ // run it on a background thread to prevent it from stalling the UI
+ auto runner = [frame, promise=std::move(promise)]() mutable {
+ promise.start();
+ promise.addResult(frame.toImage());
+ promise.finish();
+ };
+
+ auto *worker = QThread::create(std::move(runner));
+
+ auto onFinished = [this, worker, id, future]() mutable {
+ worker->deleteLater();
+
+ if (future.isValid()) {
+ Q_EMIT imageCaptured(id, future.result());
+ } else {
+ qWarning("QQnxImageCapture: failed to capture image to buffer");
}
- file.write(rawData);
- file.close();
- QMetaObject::invokeMethod(data->imageCapture, "imageSaved", Qt::QueuedConnection,
- Q_ARG(int, data->id),
- Q_ARG(QString, data->filename));
- }
- delete data;
-}
-#endif
+ };
+ connect(worker, &QThread::finished, this, std::move(onFinished));
-int QQnxImageCapture::capture(const QString &fileName)
-{
- auto *camera = static_cast<QQnxCamera *>(m_session->camera());
- // ### add can take photo
- if (!camera || !camera->isActive()) {
- emit error(-1, QImageCapture::NotReadyError, QPlatformImageCapture::msgCameraNotReady());
- return -1;
- }
+ Q_EMIT imageExposed(id);
- // prepare context object for callback
- PendingImage *pending = new PendingImage;
- pending->id = m_lastId;
- pending->filename = fileName;
- pending->imageCapture = this;
- m_lastId++;
-
-#if 0
- // ### camera_take_photo doesn't exist anymore afaict
- const camera_error_t result = camera_take_photo(camera->handle(),
- imageCaptureShutterCallback,
- 0,
- 0,
- imageCaptureImageCallback,
- pending, false);
-
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to take photo:" << result;
- emit error(-1, QImageCapture::NotReadyError, QPlatformImageCapture::msgCameraNotReady());
- return -1;
- }
-#endif
+ worker->start();
- return m_lastId;
+ return future;
}
-int QQnxImageCapture::captureToBuffer()
+void QQnxImageCapture::saveFrame(int id, const QVideoFrame &frame, const QString &fileName)
{
- // ### implement me
- return -1;
+ QFuture<QImage> decodeFuture = decodeFrame(id, frame);
+
+ if (decodeFuture.isCanceled())
+ return;
+
+ QPromise<bool> promise;
+ QFuture<bool> saveFuture = promise.future();
+
+ // writing a QImage to disk is a _very_ expensive operation
+ // run it on a background thread to prevent it from stalling the UI
+ auto runner = [future=std::move(decodeFuture),
+ promise=std::move(promise), fileName]() mutable {
+ promise.start();
+ promise.addResult(future.result().save(fileName));
+ promise.finish();
+ };
+
+ auto *worker = QThread::create(std::move(runner));
+
+ auto onFinished = [this, worker, id, future=std::move(saveFuture), fn=std::move(fileName)]() {
+ worker->deleteLater();
+
+ if (future.isValid() && future.result())
+ Q_EMIT imageSaved(id, fn);
+ else
+ Q_EMIT error(id, QImageCapture::NotSupportedFeatureError, u"Failed to save image"_s);
+ };
+
+ connect(worker, &QThread::finished, this, std::move(onFinished));
+
+ worker->start();
}
QImageEncoderSettings QQnxImageCapture::imageSettings() const
@@ -195,22 +205,53 @@ void QQnxImageCapture::setCaptureSession(QQnxMediaCaptureSession *captureSession
if (m_session == captureSession)
return;
- bool oldReadyForCapture = isReadyForCapture();
+ if (m_session)
+ m_session->disconnect(this);
+
+ m_session = captureSession;
+
if (m_session) {
- disconnect(m_session, nullptr, this, nullptr);
+ connect(m_session, &QQnxMediaCaptureSession::cameraChanged,
+ this, &QQnxImageCapture::onCameraChanged);
}
- m_session = captureSession;
+ onCameraChanged();
+}
- bool readyForCapture = isReadyForCapture();
- if (readyForCapture != oldReadyForCapture)
- emit readyForCaptureChanged(readyForCapture);
+void QQnxImageCapture::onCameraChanged()
+{
+ if (m_camera)
+ m_camera->disconnect(this);
+
+ m_camera = m_session ? static_cast<QQnxPlatformCamera*>(m_session->camera()) : nullptr;
- if (!m_session)
+ if (m_camera) {
+ connect(m_camera, &QQnxPlatformCamera::activeChanged,
+ this, &QQnxImageCapture::onCameraChanged);
+ }
+
+ updateReadyForCapture();
+}
+
+void QQnxImageCapture::onCameraActiveChanged(bool active)
+{
+ Q_UNUSED(active);
+
+ updateReadyForCapture();
+}
+
+void QQnxImageCapture::updateReadyForCapture()
+{
+ const bool readyForCapture = isReadyForCapture();
+
+ if (m_lastReadyForCapture == readyForCapture)
return;
-// connect(m_session, &QPlatformMediaCaptureSession::cameraChanged, this, &QGstreamerImageCapture::onCameraChanged);
-// onCameraChanged();
+ m_lastReadyForCapture = readyForCapture;
+
+ Q_EMIT readyForCaptureChanged(m_lastReadyForCapture);
}
QT_END_NAMESPACE
+
+#include "moc_qqnximagecapture_p.cpp"
diff --git a/src/plugins/multimedia/qnx/camera/qqnximagecapture_p.h b/src/plugins/multimedia/qnx/camera/qqnximagecapture_p.h
index b4cc13057..832039654 100644
--- a/src/plugins/multimedia/qnx/camera/qqnximagecapture_p.h
+++ b/src/plugins/multimedia/qnx/camera/qqnximagecapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQnxImageCapture_H
#define QQnxImageCapture_H
@@ -51,11 +15,15 @@
//
#include <private/qplatformimagecapture_p.h>
-#include <qqueue.h>
+
+#include <QtCore/qfuture.h>
QT_BEGIN_NAMESPACE
class QQnxMediaCaptureSession;
+class QQnxPlatformCamera;
+
+class QThread;
class QQnxImageCapture : public QPlatformImageCapture
{
@@ -73,18 +41,21 @@ public:
void setCaptureSession(QQnxMediaCaptureSession *session);
- struct PendingImage {
- int id;
- QString filename;
- QMediaMetaData metaData;
- QQnxImageCapture *imageCapture;
- };
-
private:
+ QFuture<QImage> decodeFrame(int id, const QVideoFrame &frame);
+ void saveFrame(int id, const QVideoFrame &frame, const QString &fileName);
+
+ void onCameraChanged();
+ void onCameraActiveChanged(bool active);
+ void updateReadyForCapture();
+
QQnxMediaCaptureSession *m_session = nullptr;
+ QQnxPlatformCamera *m_camera = nullptr;
int m_lastId = 0;
QImageEncoderSettings m_settings;
+
+ bool m_lastReadyForCapture = false;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/camera/qqnxplatformcamera.cpp b/src/plugins/multimedia/qnx/camera/qqnxplatformcamera.cpp
new file mode 100644
index 000000000..fdf69618e
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnxplatformcamera.cpp
@@ -0,0 +1,427 @@
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#undef QT_NO_CONTEXTLESS_CONNECT // Remove after porting connect() calls
+
+#include "qqnxplatformcamera_p.h"
+#include "qqnxcameraframebuffer_p.h"
+#include "qqnxmediacapturesession_p.h"
+#include "qqnxvideosink_p.h"
+
+#include <qcameradevice.h>
+#include <qmediadevices.h>
+
+#include <private/qmediastoragelocation_p.h>
+
+#include <camera/camera_api.h>
+#include <camera/camera_3a.h>
+
+#include <algorithm>
+#include <array>
+
+#include <dlfcn.h>
+
+struct FocusModeMapping
+{
+ QCamera::FocusMode qt;
+ camera_focusmode_t qnx;
+};
+
+constexpr std::array<FocusModeMapping, 6> focusModes {{
+ { QCamera::FocusModeAuto, CAMERA_FOCUSMODE_CONTINUOUS_AUTO },
+ { QCamera::FocusModeAutoFar, CAMERA_FOCUSMODE_CONTINUOUS_AUTO },
+ { QCamera::FocusModeInfinity, CAMERA_FOCUSMODE_CONTINUOUS_AUTO },
+ { QCamera::FocusModeAutoNear, CAMERA_FOCUSMODE_CONTINUOUS_MACRO },
+ { QCamera::FocusModeHyperfocal, CAMERA_FOCUSMODE_EDOF },
+ { QCamera::FocusModeManual, CAMERA_FOCUSMODE_MANUAL },
+}};
+
+template <typename Mapping, typename From, typename To, size_t N>
+static constexpr To convert(const std::array<Mapping, N> &mapping,
+ From Mapping::* from, To Mapping::* to, From value, To defaultValue)
+{
+ for (const Mapping &m : mapping) {
+ const auto fromValue = m.*from;
+ const auto toValue = m.*to;
+
+ if (value == fromValue)
+ return toValue;
+ }
+
+ return defaultValue;
+}
+
+static constexpr camera_focusmode_t qnxFocusMode(QCamera::FocusMode mode)
+{
+ return convert(focusModes, &FocusModeMapping::qt,
+ &FocusModeMapping::qnx, mode, CAMERA_FOCUSMODE_CONTINUOUS_AUTO);
+}
+
+static constexpr QCamera::FocusMode qtFocusMode(camera_focusmode_t mode)
+{
+ return convert(focusModes, &FocusModeMapping::qnx,
+ &FocusModeMapping::qt, mode, QCamera::FocusModeAuto);
+}
+
+QT_BEGIN_NAMESPACE
+
+QQnxPlatformCamera::QQnxPlatformCamera(QCamera *parent)
+ : QPlatformCamera(parent)
+{
+ if (parent)
+ setCamera(parent->cameraDevice());
+ else
+ setCamera(QMediaDevices::defaultVideoInput());
+}
+
+QQnxPlatformCamera::~QQnxPlatformCamera()
+{
+ stop();
+}
+
+bool QQnxPlatformCamera::isActive() const
+{
+ return m_qnxCamera && m_qnxCamera->isActive();
+}
+
+void QQnxPlatformCamera::setActive(bool active)
+{
+ if (active)
+ start();
+ else
+ stop();
+}
+
+void QQnxPlatformCamera::start()
+{
+ if (!m_qnxCamera || isActive())
+ return;
+
+ if (m_session)
+ m_videoSink = m_session->videoSink();
+
+ m_qnxCamera->start();
+
+ Q_EMIT activeChanged(true);
+}
+
+void QQnxPlatformCamera::stop()
+{
+ if (!m_qnxCamera)
+ return;
+
+ m_qnxCamera->stop();
+
+ m_videoSink = nullptr;
+
+ Q_EMIT activeChanged(false);
+}
+
+void QQnxPlatformCamera::setCamera(const QCameraDevice &camera)
+{
+ if (m_cameraDevice == camera)
+ return;
+
+ const auto cameraUnit = static_cast<camera_unit_t>(camera.id().toUInt());
+
+ m_qnxCamera = std::make_unique<QQnxCamera>(cameraUnit);
+
+ connect(m_qnxCamera.get(), &QQnxCamera::focusModeChanged,
+ [this](camera_focusmode_t mode) { Q_EMIT focusModeChanged(qtFocusMode(mode)); });
+ connect(m_qnxCamera.get(), &QQnxCamera::customFocusPointChanged,
+ this, &QQnxPlatformCamera::customFocusPointChanged);
+ connect(m_qnxCamera.get(), &QQnxCamera::frameAvailable,
+ this, &QQnxPlatformCamera::onFrameAvailable, Qt::QueuedConnection);
+
+ m_cameraDevice = camera;
+
+ updateCameraFeatures();
+}
+
+bool QQnxPlatformCamera::setCameraFormat(const QCameraFormat &format)
+{
+ const QSize resolution = format.resolution();
+
+ if (resolution.isEmpty()) {
+ qWarning("QQnxPlatformCamera: invalid resolution requested");
+ return false;
+ }
+
+ return m_qnxCamera->setCameraFormat(resolution.width(),
+ resolution.height(), format.maxFrameRate());
+}
+
+void QQnxPlatformCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ if (m_session == session)
+ return;
+
+ m_session = static_cast<QQnxMediaCaptureSession *>(session);
+}
+
+bool QQnxPlatformCamera::isFocusModeSupported(QCamera::FocusMode mode) const
+{
+ if (!m_qnxCamera)
+ return false;
+
+ return m_qnxCamera->supportedFocusModes().contains(::qnxFocusMode(mode));
+}
+
+void QQnxPlatformCamera::setFocusMode(QCamera::FocusMode mode)
+{
+ if (!m_qnxCamera)
+ return;
+
+ m_qnxCamera->setFocusMode(::qnxFocusMode(mode));
+}
+
+void QQnxPlatformCamera::setCustomFocusPoint(const QPointF &point)
+{
+ if (!m_qnxCamera)
+ return;
+
+ m_qnxCamera->setCustomFocusPoint(point);
+}
+
+void QQnxPlatformCamera::setFocusDistance(float distance)
+{
+ if (!m_qnxCamera)
+ return;
+
+ const int maxDistance = m_qnxCamera->maxFocusStep();
+
+ if (maxDistance < 0)
+ return;
+
+ const int qnxDistance = maxDistance * std::min(distance, 1.0f);
+
+ m_qnxCamera->setManualFocusStep(qnxDistance);
+}
+
+void QQnxPlatformCamera::zoomTo(float factor, float)
+{
+ if (!m_qnxCamera)
+ return;
+
+ const uint32_t minZoom = m_qnxCamera->minimumZoomLevel();
+ const uint32_t maxZoom = m_qnxCamera->maximumZoomLevel();
+
+ if (maxZoom <= minZoom)
+ return;
+
+ // QNX has an integer based API. Interpolate between the levels according to the factor we get
+ const float max = maxZoomFactor();
+ const float min = minZoomFactor();
+
+ if (max <= min)
+ return;
+
+ factor = qBound(min, factor, max) - min;
+
+ const uint32_t zoom = minZoom
+ + static_cast<uint32_t>(qRound(factor*(maxZoom - minZoom)/(max - min)));
+
+ if (m_qnxCamera->setZoomFactor(zoom))
+ zoomFactorChanged(factor);
+}
+
+void QQnxPlatformCamera::setExposureCompensation(float ev)
+{
+ if (!m_qnxCamera)
+ return;
+
+ m_qnxCamera->setEvOffset(ev);
+}
+
+int QQnxPlatformCamera::isoSensitivity() const
+{
+ if (!m_qnxCamera)
+ return 0;
+
+ return m_qnxCamera->manualIsoSensitivity();
+}
+
+void QQnxPlatformCamera::setManualIsoSensitivity(int value)
+{
+ if (!m_qnxCamera)
+ return;
+
+ const uint32_t isoValue = std::max(0, value);
+
+ m_qnxCamera->setManualIsoSensitivity(isoValue);
+}
+
+void QQnxPlatformCamera::setManualExposureTime(float seconds)
+{
+ if (!m_qnxCamera)
+ return;
+
+ m_qnxCamera->setManualExposureTime(seconds);
+}
+
+float QQnxPlatformCamera::exposureTime() const
+{
+ if (!m_qnxCamera)
+ return 0.0;
+
+ return static_cast<float>(m_qnxCamera->manualExposureTime());
+}
+
+bool QQnxPlatformCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
+{
+ if (m_maxColorTemperature != 0)
+ return true;
+
+ return mode == QCamera::WhiteBalanceAuto;
+}
+
+void QQnxPlatformCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
+{
+ if (!m_qnxCamera)
+ return;
+
+ if (mode == QCamera::WhiteBalanceAuto) {
+ m_qnxCamera->setWhiteBalanceMode(CAMERA_WHITEBALANCEMODE_AUTO);
+ } else {
+ m_qnxCamera->setWhiteBalanceMode(CAMERA_WHITEBALANCEMODE_MANUAL);
+ setColorTemperature(colorTemperatureForWhiteBalance(mode));
+ }
+}
+
+void QQnxPlatformCamera::setColorTemperature(int temperature)
+{
+ if (!m_qnxCamera)
+ return;
+
+ const auto normalizedTemp = std::clamp<uint32_t>(std::max(0, temperature),
+ m_minColorTemperature, m_maxColorTemperature);
+
+ if (m_qnxCamera->hasContinuousWhiteBalanceValues()) {
+ m_qnxCamera->setManualWhiteBalance(normalizedTemp);
+ } else {
+ uint32_t delta = std::numeric_limits<uint32_t>::max();
+ uint32_t closestTemp = 0;
+
+ for (uint32_t value : m_qnxCamera->supportedWhiteBalanceValues()) {
+ const auto &[min, max] = std::minmax(value, normalizedTemp);
+ const uint32_t currentDelta = max - min;
+
+ if (currentDelta < delta) {
+ closestTemp = value;
+ delta = currentDelta;
+ }
+ }
+
+ m_qnxCamera->setManualWhiteBalance(closestTemp);
+ }
+}
+
+bool QQnxPlatformCamera::startVideoRecording()
+{
+ if (!m_qnxCamera) {
+ qWarning("QQnxPlatformCamera: cannot start video recording - no no camera assigned");
+ return false;
+ }
+
+ if (!isVideoEncodingSupported()) {
+ qWarning("QQnxPlatformCamera: cannot start video recording - not supported");
+ return false;
+ }
+
+ if (!m_qnxCamera->isActive()) {
+ qWarning("QQnxPlatformCamera: cannot start video recording - camera not started");
+ return false;
+ }
+
+ const QString container = m_encoderSettings.mimeType().preferredSuffix();
+ const QString location = QMediaStorageLocation::generateFileName(m_outputUrl.toLocalFile(),
+ QStandardPaths::MoviesLocation, container);
+
+#if 0
+ {
+ static void *libScreen = nullptr;
+
+ if (!libScreen)
+ libScreen = dlopen("/usr/lib/libscreen.so.1", RTLD_GLOBAL);
+ }
+#endif
+
+ qDebug() << "Recording to" << location;
+ return m_qnxCamera->startVideoRecording(location);
+}
+
+void QQnxPlatformCamera::requestVideoFrame(VideoFrameCallback cb)
+{
+ m_videoFrameRequests.emplace_back(std::move(cb));
+}
+
+bool QQnxPlatformCamera::isVideoEncodingSupported() const
+{
+ return m_qnxCamera && m_qnxCamera->hasFeature(CAMERA_FEATURE_VIDEO);
+}
+
+void QQnxPlatformCamera::setOutputUrl(const QUrl &url)
+{
+ m_outputUrl = url;
+}
+
+void QQnxPlatformCamera::setMediaEncoderSettings(const QMediaEncoderSettings &settings)
+{
+ m_encoderSettings = settings;
+}
+
+void QQnxPlatformCamera::updateCameraFeatures()
+{
+ if (!m_qnxCamera)
+ return;
+
+ QCamera::Features features = {};
+
+ if (m_qnxCamera->hasFeature(CAMERA_FEATURE_REGIONFOCUS))
+ features |= QCamera::Feature::CustomFocusPoint;
+
+ supportedFeaturesChanged(features);
+
+ minimumZoomFactorChanged(m_qnxCamera->minimumZoomLevel());
+ maximumZoomFactorChanged(m_qnxCamera->maximumZoomLevel());
+
+ const QList<uint32_t> wbValues = m_qnxCamera->supportedWhiteBalanceValues();
+
+ if (wbValues.isEmpty()) {
+ m_minColorTemperature = m_maxColorTemperature = 0;
+ } else {
+ const auto &[minTemp, maxTemp] = std::minmax_element(wbValues.begin(), wbValues.end());
+
+ m_minColorTemperature = *minTemp;
+ m_maxColorTemperature = *maxTemp;
+ }
+}
+
+void QQnxPlatformCamera::onFrameAvailable()
+{
+ if (!m_videoSink)
+ return;
+
+ std::unique_ptr<QQnxCameraFrameBuffer> currentFrame = m_qnxCamera->takeCurrentFrame();
+
+ if (!currentFrame)
+ return;
+
+ const QVideoFrame actualFrame(currentFrame.get(),
+ QVideoFrameFormat(currentFrame->size(), currentFrame->pixelFormat()));
+
+ currentFrame.release(); // QVideoFrame has taken ownership of the internal
+ // buffer
+
+ m_videoSink->setVideoFrame(actualFrame);
+
+ if (!m_videoFrameRequests.empty()) {
+ VideoFrameCallback cb = std::move(m_videoFrameRequests.front());
+ m_videoFrameRequests.pop_front();
+ cb(actualFrame);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qqnxplatformcamera_p.cpp"
diff --git a/src/plugins/multimedia/qnx/camera/qqnxplatformcamera_p.h b/src/plugins/multimedia/qnx/camera/qqnxplatformcamera_p.h
new file mode 100644
index 000000000..3cbd17a4f
--- /dev/null
+++ b/src/plugins/multimedia/qnx/camera/qqnxplatformcamera_p.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QQNXPLATFORMCAMERA_H
+#define QQNXPLATFORMCAMERA_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqnxcamera_p.h"
+
+#include <private/qplatformcamera_p.h>
+#include <private/qplatformmediarecorder_p.h>
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qurl.h>
+
+#include <deque>
+#include <functional>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+class QQnxPlatformCameraFrameBuffer;
+class QQnxMediaCaptureSession;
+class QQnxVideoSink;
+class QQnxCameraFrameBuffer;
+
+class QQnxPlatformCamera : public QPlatformCamera
+{
+ Q_OBJECT
+public:
+ explicit QQnxPlatformCamera(QCamera *parent);
+ ~QQnxPlatformCamera();
+
+ bool isActive() const override;
+ void setActive(bool active) override;
+ void start();
+ void stop();
+
+ void setCamera(const QCameraDevice &camera) override;
+
+ bool setCameraFormat(const QCameraFormat &format) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session) override;
+
+ bool isFocusModeSupported(QCamera::FocusMode mode) const override;
+ void setFocusMode(QCamera::FocusMode mode) override;
+
+ void setCustomFocusPoint(const QPointF &point) override;
+
+ void setFocusDistance(float distance) override;
+
+ void zoomTo(float newZoomFactor, float rate = -1.) override;
+
+ void setExposureCompensation(float ev) override;
+
+ int isoSensitivity() const override;
+ void setManualIsoSensitivity(int value) override;
+ void setManualExposureTime(float seconds) override;
+ float exposureTime() const override;
+
+ bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override;
+ void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override;
+ void setColorTemperature(int temperature) override;
+
+ void setOutputUrl(const QUrl &url);
+ void setMediaEncoderSettings(const QMediaEncoderSettings &settings);
+
+ bool startVideoRecording();
+
+ using VideoFrameCallback = std::function<void(const QVideoFrame&)>;
+ void requestVideoFrame(VideoFrameCallback cb);
+
+private:
+ void updateCameraFeatures();
+ void setColorTemperatureInternal(unsigned temp);
+
+ bool isVideoEncodingSupported() const;
+
+ void onFrameAvailable();
+
+ QQnxMediaCaptureSession *m_session = nullptr;
+ QQnxVideoSink *m_videoSink = nullptr;
+
+ QCameraDevice m_cameraDevice;
+
+ QUrl m_outputUrl;
+
+ QMediaEncoderSettings m_encoderSettings;
+
+ uint32_t m_minColorTemperature = 0;
+ uint32_t m_maxColorTemperature = 0;
+
+ QMutex m_currentFrameMutex;
+
+ std::unique_ptr<QQnxCamera> m_qnxCamera;
+ std::unique_ptr<QQnxCameraFrameBuffer> m_currentFrame;
+
+ std::deque<VideoFrameCallback> m_videoFrameRequests;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder.cpp b/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder.cpp
index e5e1f6849..00a20bbd7 100644
--- a/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder.cpp
+++ b/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxaudiorecorder_p.h"
#include "qqnxmediaeventthread_p.h"
@@ -48,8 +12,6 @@
#include <sys/stat.h>
#include <sys/strm.h>
-static int idCounter = 0;
-
static QByteArray buildDevicePath(const QByteArray &deviceId, const QMediaEncoderSettings &settings)
{
QByteArray devicePath = QByteArrayLiteral("snd:/dev/snd/") + deviceId + QByteArrayLiteral("?");
@@ -79,6 +41,8 @@ QQnxAudioRecorder::~QQnxAudioRecorder()
void QQnxAudioRecorder::openConnection()
{
+ static int idCounter = 0;
+
m_connection = ConnectionUniquePtr { mmr_connect(nullptr) };
if (!m_connection) {
@@ -316,3 +280,5 @@ void QQnxAudioRecorder::handleMmEventError(const mmr_event_t *event)
}
QT_END_NAMESPACE
+
+#include "moc_qqnxaudiorecorder_p.cpp"
diff --git a/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder_p.h b/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder_p.h
index 4b29e0ff9..f343cee14 100644
--- a/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder_p.h
+++ b/src/plugins/multimedia/qnx/capture/qqnxaudiorecorder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXAUDIORECORDER_H
#define QQNXAUDIORECORDER_H
@@ -56,7 +20,7 @@
#include <QUrl>
#include <QtCore/qobject.h>
-#include <QtCore/qtnamespacemacros.h>
+#include <QtCore/qtconfigmacros.h>
#include <QtMultimedia/qmediarecorder.h>
diff --git a/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp b/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp
index c38dcfd80..d73ca7e54 100644
--- a/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp
+++ b/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession.cpp
@@ -1,45 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxmediacapturesession_p.h"
#include "qqnxaudioinput_p.h"
-#include "qqnxcamera_p.h"
+#include "qqnxplatformcamera_p.h"
#include "qqnximagecapture_p.h"
#include "qqnxmediarecorder_p.h"
#include "qqnxvideosink_p.h"
@@ -65,7 +29,15 @@ void QQnxMediaCaptureSession::setCamera(QPlatformCamera *camera)
{
if (camera == m_camera)
return;
- m_camera = static_cast<QQnxCamera *>(camera);
+
+ if (m_camera)
+ m_camera->setCaptureSession(nullptr);
+
+ m_camera = static_cast<QQnxPlatformCamera *>(camera);
+
+ if (m_camera)
+ m_camera->setCaptureSession(this);
+
emit cameraChanged();
}
@@ -78,7 +50,15 @@ void QQnxMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCaptur
{
if (m_imageCapture == imageCapture)
return;
+
+ if (m_imageCapture)
+ m_imageCapture->setCaptureSession(nullptr);
+
m_imageCapture = static_cast<QQnxImageCapture *>(imageCapture);
+
+ if (m_imageCapture)
+ m_imageCapture->setCaptureSession(this);
+
emit imageCaptureChanged();
}
@@ -137,3 +117,5 @@ QQnxVideoSink * QQnxMediaCaptureSession::videoSink() const
}
QT_END_NAMESPACE
+
+#include "moc_qqnxmediacapturesession_p.cpp"
diff --git a/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession_p.h b/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession_p.h
index ec15d80ba..551416a61 100644
--- a/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession_p.h
+++ b/src/plugins/multimedia/qnx/capture/qqnxmediacapturesession_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXMEDIACAPTURESESSION_H
#define QQNXMEDIACAPTURESESSION_H
@@ -57,7 +21,7 @@
QT_BEGIN_NAMESPACE
class QQnxAudioInput;
-class QQnxCamera;
+class QQnxPlatformCamera;
class QQnxImageCapture;
class QQnxMediaRecorder;
class QQnxVideoSink;
@@ -90,7 +54,7 @@ public:
QQnxVideoSink *videoSink() const;
private:
- QQnxCamera *m_camera = nullptr;
+ QQnxPlatformCamera *m_camera = nullptr;
QQnxImageCapture *m_imageCapture = nullptr;
QQnxMediaRecorder *m_mediaRecorder = nullptr;
QQnxAudioInput *m_audioInput = nullptr;
diff --git a/src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp b/src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp
index 6aa0c686b..62ac030db 100644
--- a/src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp
+++ b/src/plugins/multimedia/qnx/capture/qqnxmediarecorder.cpp
@@ -1,44 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#undef QT_NO_CONTEXTLESS_CONNECT // Remove after porting connect() calls
+
#include "qqnxmediarecorder_p.h"
+#include "qqnxplatformcamera_p.h"
#include "qqnxaudioinput_p.h"
#include "qqnxcamera_p.h"
#include "qqnxmediacapturesession_p.h"
@@ -118,11 +86,13 @@ void QQnxMediaRecorder::startVideoRecording(QMediaEncoderSettings &settings)
if (!hasCamera())
return;
- auto *camera = static_cast<QQnxCamera*>(m_session->camera());
+ auto *camera = static_cast<QQnxPlatformCamera*>(m_session->camera());
camera->setMediaEncoderSettings(settings);
camera->setOutputUrl(outputLocation());
- camera->start();
+
+ if (camera->startVideoRecording())
+ stateChanged(QMediaRecorder::RecordingState);
}
void QQnxMediaRecorder::stopVideoRecording()
@@ -130,10 +100,11 @@ void QQnxMediaRecorder::stopVideoRecording()
if (!hasCamera())
return;
- auto *camera = static_cast<QQnxCamera*>(m_session->camera());
+ auto *camera = static_cast<QQnxPlatformCamera*>(m_session->camera());
camera->stop();
+ stateChanged(QMediaRecorder::StoppedState);
}
bool QQnxMediaRecorder::hasCamera() const
diff --git a/src/plugins/multimedia/qnx/capture/qqnxmediarecorder_p.h b/src/plugins/multimedia/qnx/capture/qqnxmediarecorder_p.h
index 20444488c..8b3ea21d3 100644
--- a/src/plugins/multimedia/qnx/capture/qqnxmediarecorder_p.h
+++ b/src/plugins/multimedia/qnx/capture/qqnxmediarecorder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXMEDIARECORDER_H
#define QQNXMEDIARECORDER_H
diff --git a/src/plugins/multimedia/qnx/common/mmrenderertypes.h b/src/plugins/multimedia/qnx/common/mmrenderertypes.h
index f08154cc6..f1d498388 100644
--- a/src/plugins/multimedia/qnx/common/mmrenderertypes.h
+++ b/src/plugins/multimedia/qnx/common/mmrenderertypes.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MMRENDERERTYPES_H
#define MMRENDERERTYPES_H
diff --git a/src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp b/src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp
index 2aca652ab..fff3cf1eb 100644
--- a/src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp
+++ b/src/plugins/multimedia/qnx/common/qqnxaudioinput.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxaudioinput_p.h"
diff --git a/src/plugins/multimedia/qnx/common/qqnxaudioinput_p.h b/src/plugins/multimedia/qnx/common/qqnxaudioinput_p.h
index 2a115f2f0..62a573cc1 100644
--- a/src/plugins/multimedia/qnx/common/qqnxaudioinput_p.h
+++ b/src/plugins/multimedia/qnx/common/qqnxaudioinput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXAUDIOINPUT_P_H
#define QQNXAUDIOINPUT_P_H
diff --git a/src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp b/src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp
index 598f55026..76f8fbafd 100644
--- a/src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp
+++ b/src/plugins/multimedia/qnx/common/qqnxaudiooutput.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxaudiooutput_p.h"
@@ -46,7 +10,7 @@
#include <QtCore/qloggingcategory.h>
-Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput")
+static Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput")
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h b/src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h
index db186689d..2ae5844e6 100644
--- a/src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h
+++ b/src/plugins/multimedia/qnx/common/qqnxaudiooutput_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXAUDIOOUTPUT_P_H
#define QQNXAUDIOOUTPUT_P_H
diff --git a/src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp b/src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp
index 1487ebc98..f0cc9b1c0 100644
--- a/src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp
+++ b/src/plugins/multimedia/qnx/common/qqnxmediaeventthread.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 QNX Software Systems. All rights reserved.
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxmediaeventthread_p.h"
@@ -52,6 +16,8 @@ int mmr_event_arm(mmr_context_t *ctxt,
struct sigevent const *sev);
}
+QT_BEGIN_NAMESPACE
+
static const int c_mmrCode = _PULSE_CODE_MINAVAIL + 0;
static const int c_readCode = _PULSE_CODE_MINAVAIL + 1;
static const int c_quitCode = _PULSE_CODE_MINAVAIL + 2;
@@ -126,3 +92,7 @@ void QQnxMediaEventThread::shutdown()
// block until thread terminates
wait();
}
+
+QT_END_NAMESPACE
+
+#include "moc_qqnxmediaeventthread_p.cpp"
diff --git a/src/plugins/multimedia/qnx/common/qqnxmediaeventthread_p.h b/src/plugins/multimedia/qnx/common/qqnxmediaeventthread_p.h
index febc154ca..a622fcb62 100644
--- a/src/plugins/multimedia/qnx/common/qqnxmediaeventthread_p.h
+++ b/src/plugins/multimedia/qnx/common/qqnxmediaeventthread_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 QNX Software Systems. All rights reserved.
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 QNX Software Systems. All rights reserved.
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXMEDIAEVENTTHREAD_P_H
#define QQNXMEDIAEVENTTHREAD_P_H
diff --git a/src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp b/src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp
index 76231c0c3..28f16b70a 100644
--- a/src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp
+++ b/src/plugins/multimedia/qnx/common/qqnxwindowgrabber.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxwindowgrabber_p.h"
@@ -49,8 +13,7 @@
#include <QOpenGLContext>
#include <QOpenGLFunctions>
-#include <QtGui/private/qrhi_p.h>
-#include <QtGui/private/qrhigles2_p.h>
+#include <rhi/qrhi.h>
#include <cstring>
@@ -442,8 +405,12 @@ QQnxWindowGrabber::BufferView QQnxWindowGrabberImage::getBuffer(
GLuint QQnxWindowGrabberImage::getTexture(screen_window_t window, const QSize &size)
{
if (size != m_size) {
- if (!m_glTexture)
- glGenTextures(1, &m_glTexture);
+ // create a brand new texture to be the KHR image sibling, as
+ // previously used textures cannot be reused with new KHR image
+ // sources - note that glDeleteTextures handles nullptr gracefully
+ glDeleteTextures(1, &m_glTexture);
+ glGenTextures(1, &m_glTexture);
+
glBindTexture(GL_TEXTURE_2D, m_glTexture);
if (m_eglImage) {
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, 0);
@@ -464,3 +431,5 @@ GLuint QQnxWindowGrabberImage::getTexture(screen_window_t window, const QSize &s
}
QT_END_NAMESPACE
+
+#include "moc_qqnxwindowgrabber_p.cpp"
diff --git a/src/plugins/multimedia/qnx/common/qqnxwindowgrabber_p.h b/src/plugins/multimedia/qnx/common/qqnxwindowgrabber_p.h
index 05143217b..1ffd96b63 100644
--- a/src/plugins/multimedia/qnx/common/qqnxwindowgrabber_p.h
+++ b/src/plugins/multimedia/qnx/common/qqnxwindowgrabber_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQnxWindowGrabber_H
#define QQnxWindowGrabber_H
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp
index f5ede6c4e..fcd535814 100644
--- a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxmediametadata_p.h"
#include <QtCore/qdebug.h>
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h
index 7037d96a2..db7639dc5 100644
--- a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediametadata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQnxMediaMetaData_H
#define QQnxMediaMetaData_H
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp
index 802b5bc59..1c28c3b14 100644
--- a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxmediaplayer_p.h"
#include "qqnxvideosink_p.h"
#include "qqnxmediautil_p.h"
@@ -120,7 +84,7 @@ public:
return {};
}
- quint64 textureHandle(int plane) const override
+ quint64 textureHandle(QRhi *, int plane) const override
{
if (plane != 0)
return 0;
@@ -176,8 +140,6 @@ private:
QQnxWindowGrabber::BufferView buffer;
};
-static int idCounter = 0;
-
QT_BEGIN_NAMESPACE
QQnxMediaPlayer::QQnxMediaPlayer(QMediaPlayer *parent)
@@ -204,6 +166,8 @@ QQnxMediaPlayer::~QQnxMediaPlayer()
void QQnxMediaPlayer::openConnection()
{
+ static int idCounter = 0;
+
m_connection = mmr_connect(nullptr);
if (!m_connection) {
emitPError(QString::fromLatin1("Unable to connect to the multimedia renderer"));
@@ -926,3 +890,5 @@ void QQnxMediaPlayer::readEvents()
}
QT_END_NAMESPACE
+
+#include "moc_qqnxmediaplayer_p.cpp"
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer_p.h b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer_p.h
index 321c13cc8..c570a6334 100644
--- a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer_p.h
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediaplayer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQnxMediaPlayer_H
#define QQnxMediaPlayer_H
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp
index 3aeadb51b..074989642 100644
--- a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxmediautil_p.h"
#include <QDebug>
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h
index e1f972840..7b709142f 100644
--- a/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxmediautil_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MMRENDERERUTIL_H
#define MMRENDERERUTIL_H
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp b/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp
index e2f682dd1..18d4d1828 100644
--- a/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxvideosink_p.h"
@@ -58,3 +22,5 @@ QRhi *QQnxVideoSink::rhi() const
}
QT_END_NAMESPACE
+
+#include "moc_qqnxvideosink_p.cpp"
diff --git a/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink_p.h b/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink_p.h
index 6064c40bc..2cc7990db 100644
--- a/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink_p.h
+++ b/src/plugins/multimedia/qnx/mediaplayer/qqnxvideosink_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion
-** Copyright (C) 2021 The Qt Company
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXVIDFEOSINK_P_H
#define QQNXVIDFEOSINK_P_H
diff --git a/src/plugins/multimedia/qnx/qqnxformatinfo.cpp b/src/plugins/multimedia/qnx/qqnxformatinfo.cpp
index 9579e98b3..77492e80d 100644
--- a/src/plugins/multimedia/qnx/qqnxformatinfo.cpp
+++ b/src/plugins/multimedia/qnx/qqnxformatinfo.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxformatinfo_p.h"
diff --git a/src/plugins/multimedia/qnx/qqnxformatinfo_p.h b/src/plugins/multimedia/qnx/qqnxformatinfo_p.h
index 8874c2550..aae3a026a 100644
--- a/src/plugins/multimedia/qnx/qqnxformatinfo_p.h
+++ b/src/plugins/multimedia/qnx/qqnxformatinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXFORMATINFO_H
#define QQNXFORMATINFO_H
diff --git a/src/plugins/multimedia/qnx/qqnxmediaintegration.cpp b/src/plugins/multimedia/qnx/qqnxmediaintegration.cpp
index 14d403b64..8567a69fd 100644
--- a/src/plugins/multimedia/qnx/qqnxmediaintegration.cpp
+++ b/src/plugins/multimedia/qnx/qqnxmediaintegration.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxmediaintegration_p.h"
#include "qqnxmediacapturesession_p.h"
@@ -44,6 +8,8 @@
#include "qqnxvideodevices_p.h"
#include "qqnxvideosink_p.h"
#include "qqnxmediaplayer_p.h"
+#include "qqnximagecapture_p.h"
+#include "qqnxplatformcamera_p.h"
#include <QtMultimedia/private/qplatformmediaplugin_p.h>
QT_BEGIN_NAMESPACE
@@ -60,49 +26,54 @@ public:
QPlatformMediaIntegration* create(const QString &name) override
{
- if (name == QLatin1String("qnx"))
+ if (name == u"qnx")
return new QQnxMediaIntegration;
return nullptr;
}
};
-QQnxMediaIntegration::QQnxMediaIntegration()
-{
- m_videoDevices = new QQnxVideoDevices(this);
-}
+QQnxMediaIntegration::QQnxMediaIntegration() : QPlatformMediaIntegration(QLatin1String("qnx")) { }
-QQnxMediaIntegration::~QQnxMediaIntegration()
+QPlatformMediaFormatInfo *QQnxMediaIntegration::createFormatInfo()
{
- delete m_formatInfo;
+ return new QQnxFormatInfo;
}
-QPlatformMediaFormatInfo *QQnxMediaIntegration::formatInfo()
+QPlatformVideoDevices *QQnxMediaIntegration::createVideoDevices()
{
- if (!m_formatInfo)
- m_formatInfo = new QQnxFormatInfo();
- return m_formatInfo;
+ return new QQnxVideoDevices(this);
}
-QPlatformVideoSink *QQnxMediaIntegration::createVideoSink(QVideoSink *sink)
+QMaybe<QPlatformVideoSink *> QQnxMediaIntegration::createVideoSink(QVideoSink *sink)
{
return new QQnxVideoSink(sink);
}
-QPlatformMediaPlayer *QQnxMediaIntegration::createPlayer(QMediaPlayer *parent)
+QMaybe<QPlatformMediaPlayer *> QQnxMediaIntegration::createPlayer(QMediaPlayer *parent)
{
return new QQnxMediaPlayer(parent);
}
-QPlatformMediaCaptureSession *QQnxMediaIntegration::createCaptureSession()
+QMaybe<QPlatformMediaCaptureSession *> QQnxMediaIntegration::createCaptureSession()
{
return new QQnxMediaCaptureSession();
}
-QPlatformMediaRecorder *QQnxMediaIntegration::createRecorder(QMediaRecorder *parent)
+QMaybe<QPlatformMediaRecorder *> QQnxMediaIntegration::createRecorder(QMediaRecorder *parent)
{
return new QQnxMediaRecorder(parent);
}
+QMaybe<QPlatformCamera *> QQnxMediaIntegration::createCamera(QCamera *parent)
+{
+ return new QQnxPlatformCamera(parent);
+}
+
+QMaybe<QPlatformImageCapture *> QQnxMediaIntegration::createImageCapture(QImageCapture *parent)
+{
+ return new QQnxImageCapture(parent);
+}
+
QT_END_NAMESPACE
#include "qqnxmediaintegration.moc"
diff --git a/src/plugins/multimedia/qnx/qqnxmediaintegration_p.h b/src/plugins/multimedia/qnx/qqnxmediaintegration_p.h
index a480841a5..60fafc246 100644
--- a/src/plugins/multimedia/qnx/qqnxmediaintegration_p.h
+++ b/src/plugins/multimedia/qnx/qqnxmediaintegration_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQnxMediaIntegration_H
#define QQnxMediaIntegration_H
@@ -62,19 +26,23 @@ class QQnxMediaIntegration : public QPlatformMediaIntegration
{
public:
QQnxMediaIntegration();
- ~QQnxMediaIntegration();
- QPlatformMediaFormatInfo *formatInfo() override;
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
- QPlatformVideoSink *createVideoSink(QVideoSink *sink) override;
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *parent) override;
- QPlatformMediaPlayer *createPlayer(QMediaPlayer *parent) override;
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
- QPlatformMediaCaptureSession *createCaptureSession() override;
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *parent) override;
- QPlatformMediaRecorder *createRecorder(QMediaRecorder *parent) override;
+ QMaybe<QPlatformCamera *> createCamera(QCamera *parent) override;
- QQnxFormatInfo *m_formatInfo = nullptr;
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *parent) override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+
+ QPlatformVideoDevices *createVideoDevices() override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/qnx/qqnxvideodevices.cpp b/src/plugins/multimedia/qnx/qqnxvideodevices.cpp
index 5414990f9..ea0cfd956 100644
--- a/src/plugins/multimedia/qnx/qqnxvideodevices.cpp
+++ b/src/plugins/multimedia/qnx/qqnxvideodevices.cpp
@@ -1,51 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqnxvideodevices_p.h"
+#include "qqnxcamera_p.h"
#include "private/qcameradevice_p.h"
#include "qcameradevice.h"
-#include <camera/camera_api.h>
-
#include <qdir.h>
#include <qdebug.h>
+#include <optional>
+
QT_BEGIN_NAMESPACE
static QVideoFrameFormat::PixelFormat fromCameraFrametype(camera_frametype_t type)
@@ -71,77 +36,51 @@ static QVideoFrameFormat::PixelFormat fromCameraFrametype(camera_frametype_t typ
}
}
-static QList<QCameraDevice> enumerateCameras()
+static std::optional<QCameraDevice> createCameraDevice(camera_unit_t unit, bool isDefault)
{
+ const QQnxCamera camera(unit);
- camera_unit_t cameraUnits[64];
-
- unsigned int knownCameras = 0;
- const camera_error_t result = camera_get_supported_cameras(64, &knownCameras, cameraUnits);
- if (result != CAMERA_EOK) {
- qWarning() << "Unable to retrieve supported camera types:" << result;
+ if (!camera.isValid()) {
+ qWarning() << "Invalid camera unit:" << unit;
return {};
}
- QList<QCameraDevice> cameras;
- for (unsigned int i = 0; i < knownCameras; ++i) {
- QCameraDevicePrivate *p = new QCameraDevicePrivate;
- p->id = QByteArray::number(cameraUnits[i]);
-
- char name[CAMERA_LOCATION_NAMELEN];
- camera_get_location_property(cameraUnits[i], CAMERA_LOCATION_NAME, &name, CAMERA_LOCATION_END);
- p->description = QString::fromUtf8(name);
-
- if (i == 0)
- p->isDefault = true;
-
- camera_handle_t handle;
- if (camera_open(cameraUnits[i], CAMERA_MODE_PREAD, &handle) == CAMERA_EOK) {
- // query camera properties
-
- uint32_t nResolutions = 0;
- camera_get_supported_vf_resolutions(handle, 0, &nResolutions, nullptr);
- QVarLengthArray<camera_res_t> resolutions(nResolutions);
- camera_get_supported_vf_resolutions(handle, nResolutions, &nResolutions, resolutions.data());
-
- uint32_t nFrameTypes;
- camera_get_supported_vf_frame_types(handle, 0, &nFrameTypes, nullptr);
- QVarLengthArray<camera_frametype_t> frameTypes(nFrameTypes);
- camera_get_supported_vf_frame_types(handle, nFrameTypes, &nFrameTypes, frameTypes.data());
-
- for (auto res : resolutions) {
- QSize resolution(res.width, res.height);
- p->photoResolutions.append(resolution);
-
- for (auto frameType : frameTypes) {
- auto pixelFormat = fromCameraFrametype(frameType);
- if (pixelFormat == QVideoFrameFormat::Format_Invalid)
- continue;
-
- uint32_t nFrameRates;
- camera_get_specified_vf_framerates(handle, frameType, res, 0, &nFrameRates, nullptr, nullptr);
- QVarLengthArray<double> frameRates(nFrameRates);
- bool continuous = false;
- camera_get_specified_vf_framerates(handle, frameType, res, nFrameRates, &nFrameRates, frameRates.data(), &continuous);
-
- QCameraFormatPrivate *f = new QCameraFormatPrivate;
- f->resolution = resolution;
- f->pixelFormat = pixelFormat;
- f->minFrameRate = 1.e10;
- for (auto fr : frameRates) {
- if (fr < f->minFrameRate)
- f->minFrameRate = fr;
- if (fr > f->maxFrameRate)
- f->maxFrameRate = fr;
- }
- p->videoFormats.append(f->create());
- }
+ auto *p = new QCameraDevicePrivate;
+
+ p->id = QByteArray::number(camera.unit());
+ p->description = camera.name();
+ p->isDefault = isDefault;
+
+ const QList<camera_frametype_t> frameTypes = camera.supportedVfFrameTypes();
+
+ for (camera_res_t res : camera.supportedVfResolutions()) {
+ const QSize resolution(res.width, res.height);
+
+ p->photoResolutions.append(resolution);
+
+ for (camera_frametype_t frameType : camera.supportedVfFrameTypes()) {
+ const QVideoFrameFormat::PixelFormat pixelFormat = fromCameraFrametype(frameType);
+
+ if (pixelFormat == QVideoFrameFormat::Format_Invalid)
+ continue;
+
+ auto *f = new QCameraFormatPrivate;
+ p->videoFormats.append(f->create());
+
+ f->resolution = resolution;
+ f->pixelFormat = pixelFormat;
+ f->minFrameRate = 1.e10;
+
+ for (double fr : camera.specifiedVfFrameRates(frameType, res)) {
+ if (fr < f->minFrameRate)
+ f->minFrameRate = fr;
+ if (fr > f->maxFrameRate)
+ f->maxFrameRate = fr;
}
}
-
- cameras.append(p->create());
}
- return cameras;
+
+ return p->create();
}
QQnxVideoDevices::QQnxVideoDevices(QPlatformMediaIntegration *integration)
@@ -151,10 +90,21 @@ QQnxVideoDevices::QQnxVideoDevices(QPlatformMediaIntegration *integration)
QList<QCameraDevice> QQnxVideoDevices::videoDevices() const
{
- if (!camerasChecked) {
- camerasChecked = true;
- cameras = enumerateCameras();
+ QList<QCameraDevice> cameras;
+
+ bool isDefault = true;
+
+ for (const camera_unit_t cameraUnit : QQnxCamera::supportedUnits()) {
+ const std::optional<QCameraDevice> cameraDevice = createCameraDevice(cameraUnit, isDefault);
+
+ if (!cameraDevice)
+ continue;
+
+ cameras.append(*cameraDevice);
+
+ isDefault = false;
}
+
return cameras;
}
diff --git a/src/plugins/multimedia/qnx/qqnxvideodevices_p.h b/src/plugins/multimedia/qnx/qqnxvideodevices_p.h
index 8900319c1..cc2284e57 100644
--- a/src/plugins/multimedia/qnx/qqnxvideodevices_p.h
+++ b/src/plugins/multimedia/qnx/qqnxvideodevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQNXVIDEODEVICES_H
#define QQNXVIDEODEVICES_H
@@ -52,20 +16,15 @@
//
#include <private/qplatformvideodevices_p.h>
-#include <qcameradevice.h>
QT_BEGIN_NAMESPACE
class QQnxVideoDevices : public QPlatformVideoDevices
{
public:
- QQnxVideoDevices(QPlatformMediaIntegration *integration);
+ explicit QQnxVideoDevices(QPlatformMediaIntegration *integration);
QList<QCameraDevice> videoDevices() const override;
-
-private:
- mutable bool camerasChecked = false;
- mutable QList<QCameraDevice> cameras;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/wasm/CMakeLists.txt b/src/plugins/multimedia/wasm/CMakeLists.txt
new file mode 100644
index 000000000..21f3e3472
--- /dev/null
+++ b/src/plugins/multimedia/wasm/CMakeLists.txt
@@ -0,0 +1,25 @@
+qt_internal_add_plugin(QWasmMediaPlugin
+ OUTPUT_NAME wasmmediaplugin
+ PLUGIN_TYPE multimedia
+ SOURCES
+ qwasmmediaintegration.cpp qwasmmediaintegration_p.h
+ mediaplayer/qwasmmediaplayer.cpp mediaplayer/qwasmmediaplayer_p.h
+ mediaplayer/qwasmvideosink.cpp mediaplayer/qwasmvideosink_p.h
+ common/qwasmvideooutput.cpp common/qwasmvideooutput_p.h
+ common/qwasmaudiooutput.cpp common/qwasmaudiooutput_p.h
+ common/qwasmaudioinput.cpp common/qwasmaudioinput_p.h
+ mediacapture/qwasmmediacapturesession.cpp mediacapture/qwasmmediacapturesession_p.h
+ mediacapture/qwasmmediarecorder.cpp mediacapture/qwasmmediarecorder_p.h
+ mediacapture/qwasmcamera.cpp mediacapture/qwasmcamera_p.h
+ mediacapture/qwasmimagecapture.cpp mediacapture/qwasmimagecapture_p.h
+ INCLUDE_DIRECTORIES
+ common
+ mediaplayer
+ mediacapture
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::CorePrivate
+ openal
+)
+
+target_link_libraries(QWasmMediaPlugin PUBLIC embind)
diff --git a/src/plugins/multimedia/wasm/common/qwasmaudioinput.cpp b/src/plugins/multimedia/wasm/common/qwasmaudioinput.cpp
new file mode 100644
index 000000000..a0418c5c2
--- /dev/null
+++ b/src/plugins/multimedia/wasm/common/qwasmaudioinput.cpp
@@ -0,0 +1,107 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmaudioinput_p.h"
+
+#include <qaudioinput.h>
+#include <private/qstdweb_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qWasmAudioInput, "qt.multimedia.wasm.audioinput")
+
+QWasmAudioInput::QWasmAudioInput(QAudioInput *parent)
+ : QObject(parent), QPlatformAudioInput(parent)
+{
+ m_wasMuted = false;
+ setDeviceSourceStream("");
+}
+
+QWasmAudioInput::~QWasmAudioInput()
+{
+}
+
+void QWasmAudioInput::setMuted(bool muted)
+{
+ qCDebug(qWasmAudioInput) << Q_FUNC_INFO << muted;
+ if (muted == m_wasMuted)
+ return;
+ if (m_mediaStream.isNull() || m_mediaStream.isUndefined())
+ return;
+ emscripten::val audioTracks = m_mediaStream.call<emscripten::val>("getAudioTracks");
+ if (audioTracks.isNull() || audioTracks.isUndefined())
+ return;
+ if (audioTracks["length"].as<int>() < 1)
+ return;
+ audioTracks[0].set("muted", muted);
+
+ emit mutedChanged(muted);
+ m_wasMuted = muted;
+
+}
+
+bool QWasmAudioInput::isMuted() const
+{
+ return m_wasMuted;
+}
+
+void QWasmAudioInput::setAudioDevice(const QAudioDevice &audioDevice)
+{
+ if (device == audioDevice)
+ return;
+
+ device = audioDevice;
+ setDeviceSourceStream(device.id().toStdString());
+}
+
+void QWasmAudioInput::setVolume(float volume)
+{
+ Q_UNUSED(volume)
+ // TODO seems no easy way to set input volume
+}
+
+void QWasmAudioInput::setDeviceSourceStream(const std::string &id)
+{
+ qCDebug(qWasmAudioInput) << Q_FUNC_INFO << id;
+ emscripten::val navigator = emscripten::val::global("navigator");
+ emscripten::val mediaDevices = navigator["mediaDevices"];
+
+ if (mediaDevices.isNull() || mediaDevices.isUndefined()) {
+ qWarning() << "No media devices found";
+ return;
+ }
+
+ qstdweb::PromiseCallbacks getUserMediaCallback{
+ // default
+ .thenFunc =
+ [this](emscripten::val stream) {
+ qCDebug(qWasmAudioInput) << "getUserMediaSuccess";
+ m_mediaStream = stream;
+ },
+ .catchFunc =
+ [](emscripten::val error) {
+ qCDebug(qWasmAudioInput)
+ << "addCameraSourceElement getUserMedia fail"
+ << QString::fromStdString(error["name"].as<std::string>())
+ << QString::fromStdString(error["message"].as<std::string>());
+ }
+ };
+
+ emscripten::val constraints = emscripten::val::object();
+ constraints.set("audio", true);
+ if (!id.empty())
+ constraints.set("deviceId", id);
+
+ // we do it this way as this prompts user for mic permissions
+ qstdweb::Promise::make(mediaDevices, QStringLiteral("getUserMedia"),
+ std::move(getUserMediaCallback), constraints);
+}
+
+emscripten::val QWasmAudioInput::mediaStream()
+{
+ return m_mediaStream;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwasmaudioinput_p.cpp"
diff --git a/src/plugins/multimedia/wasm/common/qwasmaudioinput_p.h b/src/plugins/multimedia/wasm/common/qwasmaudioinput_p.h
new file mode 100644
index 000000000..c772ee956
--- /dev/null
+++ b/src/plugins/multimedia/wasm/common/qwasmaudioinput_p.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMAUDIOINPUT_H
+#define QWASMAUDIOINPUT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtCore/qloggingcategory.h>
+
+#include <private/qtmultimediaglobal_p.h>
+#include <private/qplatformaudioinput_p.h>
+
+#include <emscripten.h>
+#include <emscripten/val.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qWasmAudioInput)
+
+class QWasmAudioInput : public QObject, public QPlatformAudioInput
+{
+ Q_OBJECT
+public:
+ explicit QWasmAudioInput(QAudioInput *parent);
+ ~QWasmAudioInput();
+
+ void setMuted(bool muted) override;
+ void setAudioDevice(const QAudioDevice & device) final;
+
+ bool isMuted() const;
+ void setVolume(float volume) final;
+ emscripten::val mediaStream();
+
+
+Q_SIGNALS:
+ void mutedChanged(bool muted);
+
+private:
+ bool m_wasMuted = false;
+ void setDeviceSourceStream(const std::string &id);
+ emscripten::val m_mediaStream;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMAUDIOINPUT_H
diff --git a/src/plugins/multimedia/wasm/common/qwasmaudiooutput.cpp b/src/plugins/multimedia/wasm/common/qwasmaudiooutput.cpp
new file mode 100644
index 000000000..a9a644140
--- /dev/null
+++ b/src/plugins/multimedia/wasm/common/qwasmaudiooutput.cpp
@@ -0,0 +1,378 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <qaudiodevice.h>
+#include <qaudiooutput.h>
+#include <qwasmaudiooutput_p.h>
+
+#include <QMimeDatabase>
+#include <QtCore/qloggingcategory.h>
+#include <QMediaDevices>
+#include <QUrl>
+#include <QFile>
+#include <QMimeDatabase>
+#include <QFileInfo>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(qWasmMediaAudioOutput, "qt.multimedia.wasm.audiooutput")
+
+QWasmAudioOutput::QWasmAudioOutput(QAudioOutput *parent)
+ : QPlatformAudioOutput(parent)
+{
+}
+
+QWasmAudioOutput::~QWasmAudioOutput() = default;
+
+void QWasmAudioOutput::setAudioDevice(const QAudioDevice &audioDevice)
+{
+ qCDebug(qWasmMediaAudioOutput) << Q_FUNC_INFO << device.id();
+ device = audioDevice;
+}
+
+void QWasmAudioOutput::setVideoElement(emscripten::val videoElement)
+{
+ m_videoElement = videoElement;
+}
+
+emscripten::val QWasmAudioOutput::videoElement()
+{
+ return m_videoElement;
+}
+
+void QWasmAudioOutput::setMuted(bool muted)
+{
+ emscripten::val realElement = videoElement();
+ if (!realElement.isUndefined()) {
+ realElement.set("muted", muted);
+ return;
+ }
+ if (m_audio.isUndefined() || m_audio.isNull()) {
+ qCDebug(qWasmMediaAudioOutput) << "Error"
+ << "Audio element could not be created";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("Media file could not be opened"));
+ return;
+ }
+ m_audio.set("mute", muted);
+}
+
+void QWasmAudioOutput::setVolume(float volume)
+{
+ volume = qBound(qreal(0.0), volume, qreal(1.0));
+ emscripten::val realElement = videoElement();
+ if (!realElement.isUndefined()) {
+ realElement.set("volume", volume);
+ return;
+ }
+ if (m_audio.isUndefined() || m_audio.isNull()) {
+ qCDebug(qWasmMediaAudioOutput) << "Error"
+ << "Audio element not available";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("Media file could not be opened"));
+ return;
+ }
+
+ m_audio.set("volume", volume);
+}
+
+void QWasmAudioOutput::setSource(const QUrl &url)
+{
+ qCDebug(qWasmMediaAudioOutput) << Q_FUNC_INFO << url;
+ if (url.isEmpty()) {
+ stop();
+ return;
+ }
+
+ createAudioElement(device.id().toStdString());
+
+ if (m_audio.isUndefined() || m_audio.isNull()) {
+ qCDebug(qWasmMediaAudioOutput) << "Error"
+ << "Audio element could not be created";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("Audio element could not be created"));
+ return;
+ }
+
+ emscripten::val document = emscripten::val::global("document");
+ emscripten::val body = document["body"];
+
+ m_audio.set("id", device.id().toStdString());
+
+ body.call<void>("appendChild", m_audio);
+
+
+ if (url.isLocalFile()) { // is localfile
+ qCDebug(qWasmMediaAudioOutput) << "is localfile";
+ m_source = url.toLocalFile();
+
+ QFile mediaFile(m_source);
+ if (!mediaFile.open(QIODevice::ReadOnly)) {
+ qCDebug(qWasmMediaAudioOutput) << "Error"
+ << "Media file could not be opened";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("Media file could not be opened"));
+ return;
+ }
+
+ // local files are relatively small due to browser filesystem being restricted
+ QByteArray content = mediaFile.readAll();
+
+ QMimeDatabase db;
+ qCDebug(qWasmMediaAudioOutput) << db.mimeTypeForData(content).name();
+
+ qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(content.constData(), content.size());
+ emscripten::val contentUrl =
+ qstdweb::window()["URL"].call<emscripten::val>("createObjectURL", contentBlob.val());
+
+ emscripten::val audioSourceElement =
+ document.call<emscripten::val>("createElement", std::string("source"));
+
+ audioSourceElement.set("src", contentUrl);
+
+ // work around Safari not being able to read audio from blob URLs.
+ QFileInfo info(m_source);
+ QMimeType mimeType = db.mimeTypeForFile(info);
+
+ audioSourceElement.set("type", mimeType.name().toStdString());
+ m_audio.call<void>("appendChild", audioSourceElement);
+
+ m_audio.call<void>("setAttribute", emscripten::val("srcObject"), contentUrl);
+
+ } else {
+ m_source = url.toString();
+ m_audio.set("src", m_source.toStdString());
+ }
+ m_audio.set("id", device.id().toStdString());
+
+ body.call<void>("appendChild", m_audio);
+ qCDebug(qWasmMediaAudioOutput) << Q_FUNC_INFO << device.id();
+
+ doElementCallbacks();
+}
+
+void QWasmAudioOutput::setSource(QIODevice *stream)
+{
+ m_audioIODevice = stream;
+}
+
+void QWasmAudioOutput::start()
+{
+ if (m_audio.isNull() || m_audio.isUndefined()) {
+ qCDebug(qWasmMediaAudioOutput) << "audio failed to start";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("Audio element resource error"));
+ return;
+ }
+
+ m_audio.call<void>("play");
+}
+
+void QWasmAudioOutput::stop()
+{
+ if (m_audio.isNull() || m_audio.isUndefined()) {
+ qCDebug(qWasmMediaAudioOutput) << "audio failed to start";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("Audio element resource error"));
+ return;
+ }
+ if (!m_source.isEmpty()) {
+ pause();
+ m_audio.set("currentTime", emscripten::val(0));
+ }
+ if (m_audioIODevice) {
+ m_audioIODevice->close();
+ delete m_audioIODevice;
+ m_audioIODevice = 0;
+ }
+}
+
+void QWasmAudioOutput::pause()
+{
+ if (m_audio.isNull() || m_audio.isUndefined()) {
+ qCDebug(qWasmMediaAudioOutput) << "audio failed to start";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("Audio element resource error"));
+ return;
+ }
+ m_audio.call<emscripten::val>("pause");
+}
+
+void QWasmAudioOutput::createAudioElement(const std::string &id)
+{
+ emscripten::val document = emscripten::val::global("document");
+ m_audio = document.call<emscripten::val>("createElement", std::string("audio"));
+
+ // only works in chrome and firefox.
+ // Firefox this feature is behind media.setsinkid.enabled preferences
+ // allows user to choose audio output device
+
+ if (!m_audio.hasOwnProperty("sinkId") || m_audio["sinkId"].isUndefined()) {
+ return;
+ }
+
+ std::string usableId = id;
+ if (usableId.empty())
+ usableId = QMediaDevices::defaultAudioOutput().id();
+
+ qstdweb::PromiseCallbacks sinkIdCallbacks{
+ .thenFunc = [](emscripten::val) { qCWarning(qWasmMediaAudioOutput) << "setSinkId ok"; },
+ .catchFunc =
+ [](emscripten::val) {
+ qCWarning(qWasmMediaAudioOutput) << "Error while trying to setSinkId";
+ }
+ };
+ qstdweb::Promise::make(m_audio, "setSinkId", std::move(sinkIdCallbacks), std::move(usableId));
+
+ m_audio.set("id", usableId.c_str());
+}
+
+void QWasmAudioOutput::doElementCallbacks()
+{
+ // error
+ auto errorCallback = [&](emscripten::val event) {
+ qCDebug(qWasmMediaAudioOutput) << "error";
+ if (event.isUndefined() || event.isNull())
+ return;
+ emit errorOccured(m_audio["error"]["code"].as<int>(),
+ QString::fromStdString(m_audio["error"]["message"].as<std::string>()));
+
+ QString errorMessage =
+ QString::fromStdString(m_audio["error"]["message"].as<std::string>());
+ if (errorMessage.isEmpty()) {
+ switch (m_audio["error"]["code"].as<int>()) {
+ case AudioElementError::MEDIA_ERR_ABORTED:
+ errorMessage = QStringLiteral("aborted by the user agent at the user's request.");
+ break;
+ case AudioElementError::MEDIA_ERR_NETWORK:
+ errorMessage = QStringLiteral("network error.");
+ break;
+ case AudioElementError::MEDIA_ERR_DECODE:
+ errorMessage = QStringLiteral("decoding error.");
+ break;
+ case AudioElementError::MEDIA_ERR_SRC_NOT_SUPPORTED:
+ errorMessage = QStringLiteral("src attribute not suitable.");
+ break;
+ };
+ }
+ qCDebug(qWasmMediaAudioOutput) << m_audio["error"]["code"].as<int>() << errorMessage;
+
+ emit errorOccured(m_audio["error"]["code"].as<int>(), errorMessage);
+ };
+ m_errorChangeEvent.reset(new qstdweb::EventCallback(m_audio, "error", errorCallback));
+
+ // loadeddata
+ auto loadedDataCallback = [&](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaAudioOutput) << "loaded data";
+ qstdweb::window()["URL"].call<emscripten::val>("revokeObjectURL", m_audio["src"]);
+ };
+ m_loadedDataEvent.reset(new qstdweb::EventCallback(m_audio, "loadeddata", loadedDataCallback));
+
+ // canplay
+ auto canPlayCallback = [&](emscripten::val event) {
+ if (event.isUndefined() || event.isNull())
+ return;
+ qCDebug(qWasmMediaAudioOutput) << "can play";
+ emit readyChanged(true);
+ emit stateChanged(QWasmMediaPlayer::Preparing);
+ };
+ m_canPlayChangeEvent.reset(new qstdweb::EventCallback(m_audio, "canplay", canPlayCallback));
+
+ // canplaythrough
+ auto canPlayThroughCallback = [&](emscripten::val event) {
+ Q_UNUSED(event)
+ emit stateChanged(QWasmMediaPlayer::Prepared);
+ };
+ m_canPlayThroughChangeEvent.reset(
+ new qstdweb::EventCallback(m_audio, "canplaythrough", canPlayThroughCallback));
+
+ // play
+ auto playCallback = [&](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaAudioOutput) << "play";
+ emit stateChanged(QWasmMediaPlayer::Started);
+ };
+ m_playEvent.reset(new qstdweb::EventCallback(m_audio, "play", playCallback));
+
+ // durationchange
+ auto durationChangeCallback = [&](emscripten::val event) {
+ qCDebug(qWasmMediaAudioOutput) << "durationChange";
+
+ // duration in ms
+ emit durationChanged(event["target"]["duration"].as<double>() * 1000);
+ };
+ m_durationChangeEvent.reset(
+ new qstdweb::EventCallback(m_audio, "durationchange", durationChangeCallback));
+
+ // ended
+ auto endedCallback = [&](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaAudioOutput) << "ended";
+ m_currentMediaStatus = QMediaPlayer::EndOfMedia;
+ emit statusChanged(m_currentMediaStatus);
+ };
+ m_endedEvent.reset(new qstdweb::EventCallback(m_audio, "ended", endedCallback));
+
+ // progress (buffering progress)
+ auto progesssCallback = [&](emscripten::val event) {
+ if (event.isUndefined() || event.isNull())
+ return;
+ qCDebug(qWasmMediaAudioOutput) << "progress";
+ float duration = event["target"]["duration"].as<int>();
+ if (duration < 0) // track not exactly ready yet
+ return;
+
+ emscripten::val timeRanges = event["target"]["buffered"];
+
+ if ((!timeRanges.isNull() || !timeRanges.isUndefined())
+ && timeRanges["length"].as<int>() == 1) {
+ emscripten::val dVal = timeRanges.call<emscripten::val>("end", 0);
+
+ if (!dVal.isNull() || !dVal.isUndefined()) {
+ double bufferedEnd = dVal.as<double>();
+
+ if (duration > 0 && bufferedEnd > 0) {
+ float bufferedValue = (bufferedEnd / duration * 100);
+ qCDebug(qWasmMediaAudioOutput) << "progress buffered" << bufferedValue;
+
+ emit bufferingChanged(m_currentBufferedValue);
+ if (bufferedEnd == duration)
+ m_currentMediaStatus = QMediaPlayer::BufferedMedia;
+ else
+ m_currentMediaStatus = QMediaPlayer::BufferingMedia;
+
+ emit statusChanged(m_currentMediaStatus);
+ }
+ }
+ }
+ };
+ m_progressChangeEvent.reset(new qstdweb::EventCallback(m_audio, "progress", progesssCallback));
+
+ // timupdate
+ auto timeUpdateCallback = [&](emscripten::val event) {
+ qCDebug(qWasmMediaAudioOutput)
+ << "timeupdate" << (event["target"]["currentTime"].as<double>() * 1000);
+
+ // qt progress is ms
+ emit progressChanged(event["target"]["currentTime"].as<double>() * 1000);
+ };
+ m_timeUpdateEvent.reset(new qstdweb::EventCallback(m_audio, "timeupdate", timeUpdateCallback));
+
+ // pause
+ auto pauseCallback = [&](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaAudioOutput) << "pause";
+
+ int currentTime = m_audio["currentTime"].as<int>(); // in seconds
+ int duration = m_audio["duration"].as<int>(); // in seconds
+ if ((currentTime > 0 && currentTime < duration)) {
+ emit stateChanged(QWasmMediaPlayer::Paused);
+ } else {
+ emit stateChanged(QWasmMediaPlayer::Stopped);
+ }
+ };
+ m_pauseChangeEvent.reset(new qstdweb::EventCallback(m_audio, "pause", pauseCallback));
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/wasm/common/qwasmaudiooutput_p.h b/src/plugins/multimedia/wasm/common/qwasmaudiooutput_p.h
new file mode 100644
index 000000000..69fda120b
--- /dev/null
+++ b/src/plugins/multimedia/wasm/common/qwasmaudiooutput_p.h
@@ -0,0 +1,97 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMAUDIOOUTPUT_H
+#define QWASMAUDIOOUTPUT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformaudiooutput_p.h>
+#include "qwasmmediaplayer_p.h"
+
+#include <emscripten/val.h>
+#include <private/qstdweb_p.h>
+#include <private/qwasmaudiosink_p.h>
+#include <QIODevice>
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+
+class Q_MULTIMEDIA_EXPORT QWasmAudioOutput : public QObject, public QPlatformAudioOutput
+{
+ Q_OBJECT
+
+public:
+ QWasmAudioOutput(QAudioOutput *qq);
+ ~QWasmAudioOutput();
+
+ enum AudioElementError {
+ MEDIA_ERR_ABORTED = 1,
+ MEDIA_ERR_NETWORK,
+ MEDIA_ERR_DECODE,
+ MEDIA_ERR_SRC_NOT_SUPPORTED
+ };
+
+ void setAudioDevice(const QAudioDevice &device) final;
+ void setMuted(bool muted) override;
+ void setVolume(float volume) override;
+
+ void start();
+ void stop();
+ void pause();
+
+ void setSource(const QUrl &url);
+ void setSource(QIODevice *stream);
+ void setVideoElement(emscripten::val videoElement);
+
+Q_SIGNALS:
+ void readyChanged(bool);
+ void bufferingChanged(qint32 percent);
+ void errorOccured(qint32 code, const QString &message);
+ void stateChanged(QWasmMediaPlayer::QWasmMediaPlayerState newState);
+ void progressChanged(qint32 position);
+ void durationChanged(qint64 duration);
+ void statusChanged(QMediaPlayer::MediaStatus status);
+ void sizeChange(qint32 width, qint32 height);
+ void metaDataLoaded();
+
+private:
+ void doElementCallbacks();
+ void createAudioElement(const std::string &id);
+
+ emscripten::val videoElement();
+
+ QScopedPointer<QWasmAudioSink> m_sink;
+ QScopedPointer<qstdweb::EventCallback> m_playEvent;
+ QScopedPointer<qstdweb::EventCallback> m_endedEvent;
+ QScopedPointer<qstdweb::EventCallback> m_durationChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_errorChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_canPlayChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_canPlayThroughChangeEvent;
+
+ QScopedPointer<qstdweb::EventCallback> m_playingChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_progressChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_pauseChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_timeUpdateEvent;
+ QScopedPointer<qstdweb::EventCallback> m_loadedDataEvent;
+
+ QString m_source;
+ QIODevice *m_audioIODevice = nullptr;
+ emscripten::val m_audio = emscripten::val::undefined();
+ emscripten::val m_videoElement = emscripten::val::undefined();
+ QMediaPlayer::MediaStatus m_currentMediaStatus;
+ qreal m_currentBufferedValue;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMAUDIOOUTPUT_H
diff --git a/src/plugins/multimedia/wasm/common/qwasmvideooutput.cpp b/src/plugins/multimedia/wasm/common/qwasmvideooutput.cpp
new file mode 100644
index 000000000..74c14959c
--- /dev/null
+++ b/src/plugins/multimedia/wasm/common/qwasmvideooutput.cpp
@@ -0,0 +1,1069 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QDebug>
+#include <QUrl>
+#include <QPoint>
+#include <QRect>
+#include <QMediaPlayer>
+#include <QVideoFrame>
+#include <QFile>
+#include <QBuffer>
+#include <QMimeDatabase>
+#include "qwasmvideooutput_p.h"
+
+#include <qvideosink.h>
+#include <private/qabstractvideobuffer_p.h>
+#include <private/qplatformvideosink_p.h>
+#include <private/qmemoryvideobuffer_p.h>
+#include <private/qvideotexturehelper_p.h>
+#include <private/qstdweb_p.h>
+#include <QTimer>
+
+#include <emscripten/bind.h>
+#include <emscripten/html5.h>
+#include <emscripten/val.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+using namespace emscripten;
+
+Q_LOGGING_CATEGORY(qWasmMediaVideoOutput, "qt.multimedia.wasm.videooutput")
+
+// TODO unique videosurface ?
+static std::string m_videoSurfaceId;
+
+void qtVideoBeforeUnload(emscripten::val event)
+{
+ Q_UNUSED(event)
+ // large videos will leave the unloading window
+ // in a frozen state, so remove the video element first
+ emscripten::val document = emscripten::val::global("document");
+ emscripten::val videoElement =
+ document.call<emscripten::val>("getElementById", std::string(m_videoSurfaceId));
+ videoElement.call<void>("removeAttribute", emscripten::val("src"));
+ videoElement.call<void>("load");
+}
+
+EMSCRIPTEN_BINDINGS(video_module)
+{
+ emscripten::function("mbeforeUnload", qtVideoBeforeUnload);
+}
+
+static bool checkForVideoFrame()
+{
+ emscripten::val videoFrame = emscripten::val::global("VideoFrame");
+ return (!videoFrame.isNull() && !videoFrame.isUndefined());
+}
+
+Q_GLOBAL_STATIC_WITH_ARGS(bool, m_hasVideoFrame, (checkForVideoFrame()))
+
+QWasmVideoOutput::QWasmVideoOutput(QObject *parent) : QObject{ parent }
+{
+}
+
+void QWasmVideoOutput::setVideoSize(const QSize &newSize)
+{
+ if (m_pendingVideoSize == newSize)
+ return;
+
+ m_pendingVideoSize = newSize;
+ updateVideoElementGeometry(QRect(0, 0, m_pendingVideoSize.width(), m_pendingVideoSize.height()));
+}
+
+void QWasmVideoOutput::setVideoMode(QWasmVideoOutput::WasmVideoMode mode)
+{
+ m_currentVideoMode = mode;
+}
+
+void QWasmVideoOutput::start()
+{
+ if (m_video.isUndefined() || m_video.isNull()
+ || !m_wasmSink) {
+ // error
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("video surface error"));
+ return;
+ }
+
+ switch (m_currentVideoMode) {
+ case QWasmVideoOutput::VideoOutput: {
+ emscripten::val sourceObj = m_video["src"];
+ if ((sourceObj.isUndefined() || sourceObj.isNull()) && !m_source.isEmpty()) {
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO << "calling load" << m_source;
+ m_video.set("src", m_source);
+ m_video.call<void>("load");
+ }
+ } break;
+ case QWasmVideoOutput::Camera: {
+ if (!m_cameraIsReady) {
+ m_shouldBeStarted = true;
+ }
+
+ emscripten::val stream = m_video["srcObject"];
+ if (stream.isNull() || stream.isUndefined()) { // camera device
+ qCDebug(qWasmMediaVideoOutput) << "ERROR";
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("video surface error"));
+ return;
+ } else {
+ emscripten::val videoTracks = stream.call<emscripten::val>("getVideoTracks");
+ if (videoTracks.isNull() || videoTracks.isUndefined()) {
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO << "videoTracks is null";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("video surface error"));
+ return;
+ }
+ if (videoTracks["length"].as<int>() == 0) {
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO << "videoTracks count is 0";
+ emit errorOccured(QMediaPlayer::ResourceError,
+ QStringLiteral("video surface error"));
+ return;
+ }
+ emscripten::val videoSettings = videoTracks[0].call<emscripten::val>("getSettings");
+ if (!videoSettings.isNull() || !videoSettings.isUndefined()) {
+ // double fRate = videoSettings["frameRate"].as<double>(); TODO
+ const int width = videoSettings["width"].as<int>();
+ const int height = videoSettings["height"].as<int>();
+
+ qCDebug(qWasmMediaVideoOutput)
+ << "width" << width << "height" << height;
+
+ updateVideoElementGeometry(QRect(0, 0, width, height));
+ }
+ }
+ } break;
+ };
+
+ m_shouldStop = false;
+ m_toBePaused = false;
+ m_video.call<void>("play");
+
+ if (m_currentVideoMode == QWasmVideoOutput::Camera) {
+ if (m_hasVideoFrame) {
+ m_video.call<emscripten::val>("requestVideoFrameCallback",
+ emscripten::val::module_property("qtVideoFrameTimerCallback"));
+ } else {
+ videoFrameTimerCallback();
+ }
+ }
+}
+
+void QWasmVideoOutput::stop()
+{
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO;
+
+ if (m_video.isUndefined() || m_video.isNull()) {
+ // error
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("Resource error"));
+ return;
+ }
+ m_shouldStop = true;
+ if (m_toBePaused) {
+ // we are stopped , need to reset
+ m_toBePaused = false;
+ m_video.call<void>("load");
+ } else {
+ m_video.call<void>("pause");
+ }
+}
+
+void QWasmVideoOutput::pause()
+{
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO;
+
+ if (m_video.isUndefined() || m_video.isNull()) {
+ // error
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("video surface error"));
+ return;
+ }
+ m_shouldStop = false;
+ m_toBePaused = true;
+ m_video.call<void>("pause");
+}
+
+void QWasmVideoOutput::reset()
+{
+ // flush pending frame
+ if (m_wasmSink)
+ m_wasmSink->platformVideoSink()->setVideoFrame(QVideoFrame());
+
+ m_source = "";
+ m_video.set("currentTime", emscripten::val(0));
+ m_video.call<void>("load");
+}
+
+emscripten::val QWasmVideoOutput::surfaceElement()
+{
+ return m_video;
+}
+
+void QWasmVideoOutput::setSurface(QVideoSink *surface)
+{
+ if (!surface || surface == m_wasmSink) {
+ qWarning() << "Surface not ready";
+ return;
+ }
+
+ m_wasmSink = surface;
+}
+
+bool QWasmVideoOutput::isReady() const
+{
+ if (m_video.isUndefined() || m_video.isNull()) {
+ // error
+ return false;
+ }
+
+ constexpr int hasCurrentData = 2;
+ if (!m_video.isUndefined() || !m_video.isNull())
+ return m_video["readyState"].as<int>() >= hasCurrentData;
+ else
+ return true;
+}
+
+void QWasmVideoOutput::setSource(const QUrl &url)
+{
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO << url;
+
+ if (m_video.isUndefined() || m_video.isNull()) {
+ return;
+ }
+
+ m_source = url.toString();
+ if (url.isEmpty()) {
+ stop();
+ return;
+ }
+ if (url.isLocalFile()) {
+ QFile localFile(url.toLocalFile());
+ if (localFile.open(QIODevice::ReadOnly)) {
+ QDataStream buffer(&localFile); // we will serialize the data into the file
+ setSource(buffer.device());
+ } else {
+ qWarning() << "Failed to open file";
+ }
+ return;
+ }
+
+ updateVideoElementSource(m_source);
+}
+
+void QWasmVideoOutput::updateVideoElementSource(const QString &src)
+{
+ m_video.set("src", src.toStdString());
+ m_video.call<void>("load");
+}
+
+void QWasmVideoOutput::addCameraSourceElement(const std::string &id)
+{
+ m_cameraIsReady = false;
+ emscripten::val navigator = emscripten::val::global("navigator");
+ emscripten::val mediaDevices = navigator["mediaDevices"];
+
+ if (mediaDevices.isNull() || mediaDevices.isUndefined()) {
+ qWarning() << "No media devices found";
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("Resource error"));
+ return;
+ }
+
+ qstdweb::PromiseCallbacks getUserMediaCallback{
+ .thenFunc =
+ [this](emscripten::val stream) {
+ qCDebug(qWasmMediaVideoOutput) << "getUserMediaSuccess";
+
+ m_video.set("srcObject", stream);
+ m_cameraIsReady = true;
+ if (m_shouldBeStarted) {
+ start();
+ m_shouldBeStarted = false;
+ }
+ },
+ .catchFunc =
+ [](emscripten::val error) {
+ qCDebug(qWasmMediaVideoOutput)
+ << "getUserMedia fail"
+ << QString::fromStdString(error["name"].as<std::string>())
+ << QString::fromStdString(error["message"].as<std::string>());
+ }
+ };
+
+ emscripten::val constraints = emscripten::val::object();
+
+ constraints.set("audio", m_hasAudio);
+
+ emscripten::val videoContraints = emscripten::val::object();
+ videoContraints.set("exact", id);
+ videoContraints.set("deviceId", id);
+ constraints.set("video", videoContraints);
+
+ // we do it this way as this prompts user for mic/camera permissions
+ qstdweb::Promise::make(mediaDevices, QStringLiteral("getUserMedia"),
+ std::move(getUserMediaCallback), constraints);
+}
+
+void QWasmVideoOutput::setSource(QIODevice *stream)
+{
+ if (stream->bytesAvailable() == 0) {
+ qWarning() << "data not available";
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("data not available"));
+ return;
+ }
+ if (m_video.isUndefined() || m_video.isNull()) {
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("video surface error"));
+ return;
+ }
+
+ QMimeDatabase db;
+ QMimeType mime = db.mimeTypeForData(stream);
+
+ QByteArray buffer = stream->readAll();
+
+ qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(buffer.data(), buffer.size(), mime.name().toStdString());
+
+ emscripten::val window = qstdweb::window();
+
+ if (window["safari"].isUndefined()) {
+ emscripten::val contentUrl = window["URL"].call<emscripten::val>("createObjectURL", contentBlob.val());
+ m_video.set("src", contentUrl);
+ m_source = QString::fromStdString(contentUrl.as<std::string>());
+ } else {
+ // only Safari currently supports Blob with srcObject
+ m_video.set("srcObject", contentBlob.val());
+ }
+}
+
+void QWasmVideoOutput::setVolume(qreal volume)
+{ // between 0 - 1
+ volume = qBound(qreal(0.0), volume, qreal(1.0));
+ m_video.set("volume", volume);
+}
+
+void QWasmVideoOutput::setMuted(bool muted)
+{
+ if (m_video.isUndefined() || m_video.isNull()) {
+ // error
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("video surface error"));
+ return;
+ }
+ m_video.set("muted", muted);
+}
+
+qint64 QWasmVideoOutput::getCurrentPosition()
+{
+ return (!m_video.isUndefined() || !m_video.isNull())
+ ? (m_video["currentTime"].as<double>() * 1000)
+ : 0;
+}
+
+void QWasmVideoOutput::seekTo(qint64 positionMSecs)
+{
+ if (isVideoSeekable()) {
+ float positionToSetInSeconds = float(positionMSecs) / 1000;
+ emscripten::val seekableTimeRange = m_video["seekable"];
+ if (!seekableTimeRange.isNull() || !seekableTimeRange.isUndefined()) {
+ // range user can seek
+ if (seekableTimeRange["length"].as<int>() < 1)
+ return;
+ if (positionToSetInSeconds
+ >= seekableTimeRange.call<emscripten::val>("start", 0).as<double>()
+ && positionToSetInSeconds
+ <= seekableTimeRange.call<emscripten::val>("end", 0).as<double>()) {
+ m_requestedPosition = positionToSetInSeconds;
+
+ m_video.set("currentTime", m_requestedPosition);
+ }
+ }
+ }
+ qCDebug(qWasmMediaVideoOutput) << "m_requestedPosition" << m_requestedPosition;
+}
+
+bool QWasmVideoOutput::isVideoSeekable()
+{
+ if (m_video.isUndefined() || m_video.isNull()) {
+ // error
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("video surface error"));
+ return false;
+ }
+
+ emscripten::val seekableTimeRange = m_video["seekable"];
+ if (seekableTimeRange["length"].as<int>() < 1)
+ return false;
+ if (!seekableTimeRange.isNull() || !seekableTimeRange.isUndefined()) {
+ bool isit = !qFuzzyCompare(seekableTimeRange.call<emscripten::val>("start", 0).as<double>(),
+ seekableTimeRange.call<emscripten::val>("end", 0).as<double>());
+ return isit;
+ }
+ return false;
+}
+
+void QWasmVideoOutput::createVideoElement(const std::string &id)
+{
+ // TODO: there can be more than one element !!
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO << id;
+ // Create <video> element and add it to the page body
+ emscripten::val document = emscripten::val::global("document");
+ emscripten::val body = document["body"];
+
+ emscripten::val oldVideo = document.call<emscripten::val>("getElementsByClassName",
+ (m_currentVideoMode == QWasmVideoOutput::Camera
+ ? std::string("Camera")
+ : std::string("Video")));
+
+ // we don't provide alternate tracks
+ // but need to remove stale track
+ if (oldVideo["length"].as<int>() > 0)
+ oldVideo[0].call<void>("remove");
+
+ m_videoSurfaceId = id;
+ m_video = document.call<emscripten::val>("createElement", std::string("video"));
+
+ m_video.set("id", m_videoSurfaceId.c_str());
+ m_video.call<void>("setAttribute", std::string("class"),
+ (m_currentVideoMode == QWasmVideoOutput::Camera ? std::string("Camera")
+ : std::string("Video")));
+ m_video.set("data-qvideocontext",
+ emscripten::val(quintptr(reinterpret_cast<void *>(this))));
+
+ // if video
+ m_video.set("preload", "metadata");
+
+ // Uncaught DOMException: Failed to execute 'getImageData' on
+ // 'OffscreenCanvasRenderingContext2D': The canvas has been tainted by
+ // cross-origin data.
+ // TODO figure out somehow to let user choose between these
+ std::string originString = "anonymous"; // requires server Access-Control-Allow-Origin *
+ // std::string originString = "use-credentials"; // must not
+ // Access-Control-Allow-Origin *
+
+ m_video.call<void>("setAttribute", std::string("crossorigin"), originString);
+ body.call<void>("appendChild", m_video);
+
+ // Create/add video source
+ document.call<emscripten::val>("createElement",
+ std::string("source")).set("src", m_source.toStdString());
+
+ // Set position:absolute, which makes it possible to position the video
+ // element using x,y. coordinates, relative to its parent (the page's <body>
+ // element)
+ emscripten::val style = m_video["style"];
+ style.set("position", "absolute");
+ style.set("display", "none"); // hide
+
+ if (!m_source.isEmpty())
+ updateVideoElementSource(m_source);
+}
+
+void QWasmVideoOutput::createOffscreenElement(const QSize &offscreenSize)
+{
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO;
+
+ if (m_hasVideoFrame) // VideoFrame does not require offscreen canvas/context
+ return;
+
+ // create offscreen element for grabbing frames
+ // OffscreenCanvas - no safari :(
+ // https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
+
+ emscripten::val document = emscripten::val::global("document");
+
+ // TODO use correct frameBytesAllocationSize?
+ // offscreen render buffer
+ m_offscreen = emscripten::val::global("OffscreenCanvas");
+
+ if (m_offscreen.isUndefined()) {
+ // Safari OffscreenCanvas not supported, try old skool way
+ m_offscreen = document.call<emscripten::val>("createElement", std::string("canvas"));
+
+ m_offscreen.set("style",
+ "position:absolute;left:-1000px;top:-1000px"); // offscreen
+ m_offscreen.set("width", offscreenSize.width());
+ m_offscreen.set("height", offscreenSize.height());
+ m_offscreenContext = m_offscreen.call<emscripten::val>("getContext", std::string("2d"));
+ } else {
+ m_offscreen = emscripten::val::global("OffscreenCanvas")
+ .new_(offscreenSize.width(), offscreenSize.height());
+ emscripten::val offscreenAttributes = emscripten::val::array();
+ offscreenAttributes.set("willReadFrequently", true);
+ m_offscreenContext = m_offscreen.call<emscripten::val>("getContext", std::string("2d"),
+ offscreenAttributes);
+ }
+ std::string offscreenId = m_videoSurfaceId + "_offscreenOutputSurface";
+ m_offscreen.set("id", offscreenId.c_str());
+}
+
+void QWasmVideoOutput::doElementCallbacks()
+{
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO;
+
+ // event callbacks
+ // timupdate
+ auto timeUpdateCallback = [=](emscripten::val event) {
+ qCDebug(qWasmMediaVideoOutput) << "timeupdate";
+
+ // qt progress is ms
+ emit progressChanged(event["target"]["currentTime"].as<double>() * 1000);
+ };
+ m_timeUpdateEvent.reset(new qstdweb::EventCallback(m_video, "timeupdate", timeUpdateCallback));
+
+ // play
+ auto playCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "play" << m_video["src"].as<std::string>();
+ if (!m_isSeeking)
+ emit stateChanged(QWasmMediaPlayer::Preparing);
+ };
+ m_playEvent.reset(new qstdweb::EventCallback(m_video, "play", playCallback));
+
+ // ended
+ auto endedCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "ended";
+ m_currentMediaStatus = QMediaPlayer::EndOfMedia;
+ emit statusChanged(m_currentMediaStatus);
+ m_shouldStop = true;
+ stop();
+ };
+ m_endedEvent.reset(new qstdweb::EventCallback(m_video, "ended", endedCallback));
+
+ // durationchange
+ auto durationChangeCallback = [=](emscripten::val event) {
+ qCDebug(qWasmMediaVideoOutput) << "durationChange";
+
+ // qt duration is in milliseconds.
+ qint64 dur = event["target"]["duration"].as<double>() * 1000;
+ emit durationChanged(dur);
+ };
+ m_durationChangeEvent.reset(
+ new qstdweb::EventCallback(m_video, "durationchange", durationChangeCallback));
+
+ // loadeddata
+ auto loadedDataCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "loaded data";
+
+ emit stateChanged(QWasmMediaPlayer::Prepared);
+ };
+ m_loadedDataEvent.reset(new qstdweb::EventCallback(m_video, "loadeddata", loadedDataCallback));
+
+ // error
+ auto errorCallback = [=](emscripten::val event) {
+ qCDebug(qWasmMediaVideoOutput) << "error";
+ if (event.isUndefined() || event.isNull())
+ return;
+ emit errorOccured(m_video["error"]["code"].as<int>(),
+ QString::fromStdString(m_video["error"]["message"].as<std::string>()));
+ };
+ m_errorChangeEvent.reset(new qstdweb::EventCallback(m_video, "error", errorCallback));
+
+ // resize
+ auto resizeCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "resize";
+
+ updateVideoElementGeometry(
+ QRect(0, 0, m_video["videoWidth"].as<int>(), m_video["videoHeight"].as<int>()));
+ emit sizeChange(m_video["videoWidth"].as<int>(), m_video["videoHeight"].as<int>());
+
+ };
+ m_resizeChangeEvent.reset(new qstdweb::EventCallback(m_video, "resize", resizeCallback));
+
+ // loadedmetadata
+ auto loadedMetadataCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "loaded meta data";
+
+ emit metaDataLoaded();
+ };
+ m_loadedMetadataChangeEvent.reset(
+ new qstdweb::EventCallback(m_video, "loadedmetadata", loadedMetadataCallback));
+
+ // loadstart
+ auto loadStartCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "load started";
+ m_currentMediaStatus = QMediaPlayer::LoadingMedia;
+ emit statusChanged(m_currentMediaStatus);
+ m_shouldStop = false;
+ };
+ m_loadStartChangeEvent.reset(new qstdweb::EventCallback(m_video, "loadstart", loadStartCallback));
+
+ // canplay
+
+ auto canPlayCallback = [=](emscripten::val event) {
+ if (event.isUndefined() || event.isNull())
+ return;
+ qCDebug(qWasmMediaVideoOutput) << "can play"
+ << "m_requestedPosition" << m_requestedPosition;
+
+ if (!m_shouldStop)
+ emit readyChanged(true); // sets video available
+ };
+ m_canPlayChangeEvent.reset(new qstdweb::EventCallback(m_video, "canplay", canPlayCallback));
+
+ // canplaythrough
+ auto canPlayThroughCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "can play through"
+ << "m_shouldStop" << m_shouldStop;
+
+ if (m_currentMediaStatus == QMediaPlayer::EndOfMedia)
+ return;
+ if (!m_isSeeking && !m_shouldStop) {
+ emscripten::val timeRanges = m_video["buffered"];
+ if ((!timeRanges.isNull() || !timeRanges.isUndefined())
+ && timeRanges["length"].as<int>() == 1) {
+ double buffered = m_video["buffered"].call<emscripten::val>("end", 0).as<double>();
+ const double duration = m_video["duration"].as<double>();
+
+ if (duration == buffered) {
+ m_currentBufferedValue = 100;
+ emit bufferingChanged(m_currentBufferedValue);
+ }
+ }
+ m_currentMediaStatus = QMediaPlayer::LoadedMedia;
+ emit statusChanged(m_currentMediaStatus);
+ if (m_hasVideoFrame) {
+ m_video.call<emscripten::val>("requestVideoFrameCallback",
+ emscripten::val::module_property("qtVideoFrameTimerCallback"));
+ } else {
+ videoFrameTimerCallback();
+ }
+ } else {
+ m_shouldStop = false;
+ }
+ };
+ m_canPlayThroughChangeEvent.reset(
+ new qstdweb::EventCallback(m_video, "canplaythrough", canPlayThroughCallback));
+
+ // seeking
+ auto seekingCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput)
+ << "seeking started" << (m_video["currentTime"].as<double>() * 1000);
+ m_isSeeking = true;
+ };
+ m_seekingChangeEvent.reset(new qstdweb::EventCallback(m_video, "seeking", seekingCallback));
+
+ // seeked
+ auto seekedCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "seeked" << (m_video["currentTime"].as<double>() * 1000);
+ emit progressChanged(m_video["currentTime"].as<double>() * 1000);
+ m_isSeeking = false;
+ };
+ m_seekedChangeEvent.reset(new qstdweb::EventCallback(m_video, "seeked", seekedCallback));
+
+ // emptied
+ auto emptiedCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "emptied";
+ emit readyChanged(false);
+ m_currentMediaStatus = QMediaPlayer::EndOfMedia;
+ emit statusChanged(m_currentMediaStatus);
+ };
+ m_emptiedChangeEvent.reset(new qstdweb::EventCallback(m_video, "emptied", emptiedCallback));
+
+ // stalled
+ auto stalledCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "stalled";
+ m_currentMediaStatus = QMediaPlayer::StalledMedia;
+ emit statusChanged(m_currentMediaStatus);
+ };
+ m_stalledChangeEvent.reset(new qstdweb::EventCallback(m_video, "stalled", stalledCallback));
+
+ // waiting
+ auto waitingCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+
+ qCDebug(qWasmMediaVideoOutput) << "waiting";
+ // check buffer
+ };
+ m_waitingChangeEvent.reset(new qstdweb::EventCallback(m_video, "waiting", waitingCallback));
+
+ // suspend
+
+ // playing
+ auto playingCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "playing";
+ if (m_isSeeking)
+ return;
+ emit stateChanged(QWasmMediaPlayer::Started);
+ if (m_toBePaused || !m_shouldStop) { // paused
+ m_toBePaused = false;
+
+ if (m_hasVideoFrame) {
+ m_video.call<emscripten::val>("requestVideoFrameCallback",
+ emscripten::val::module_property("qtVideoFrameTimerCallback"));
+ } else {
+ videoFrameTimerCallback(); // get the ball rolling
+ }
+ }
+ };
+ m_playingChangeEvent.reset(new qstdweb::EventCallback(m_video, "playing", playingCallback));
+
+ // progress (buffering progress)
+ auto progesssCallback = [=](emscripten::val event) {
+ if (event.isUndefined() || event.isNull())
+ return;
+
+ const double duration = event["target"]["duration"].as<double>();
+ if (duration < 0) // track not exactly ready yet
+ return;
+
+ emscripten::val timeRanges = event["target"]["buffered"];
+
+ if ((!timeRanges.isNull() || !timeRanges.isUndefined())
+ && timeRanges["length"].as<int>() == 1) {
+ emscripten::val dVal = timeRanges.call<emscripten::val>("end", 0);
+ if (!dVal.isNull() || !dVal.isUndefined()) {
+ double bufferedEnd = dVal.as<double>();
+
+ if (duration > 0 && bufferedEnd > 0) {
+ const double bufferedValue = (bufferedEnd / duration * 100);
+ qCDebug(qWasmMediaVideoOutput) << "progress buffered";
+ m_currentBufferedValue = bufferedValue;
+ emit bufferingChanged(m_currentBufferedValue);
+ if (bufferedEnd == duration)
+ m_currentMediaStatus = QMediaPlayer::BufferedMedia;
+ else
+ m_currentMediaStatus = QMediaPlayer::BufferingMedia;
+ emit statusChanged(m_currentMediaStatus);
+ }
+ }
+ }
+ };
+ m_progressChangeEvent.reset(new qstdweb::EventCallback(m_video, "progress", progesssCallback));
+
+ // pause
+ auto pauseCallback = [=](emscripten::val event) {
+ Q_UNUSED(event)
+ qCDebug(qWasmMediaVideoOutput) << "pause";
+
+ const double currentTime = m_video["currentTime"].as<double>(); // in seconds
+ const double duration = m_video["duration"].as<double>(); // in seconds
+ if ((currentTime > 0 && currentTime < duration) && (!m_shouldStop && m_toBePaused)) {
+ emit stateChanged(QWasmMediaPlayer::Paused);
+ } else {
+ // stop this crazy thing!
+ m_video.set("currentTime", emscripten::val(0));
+ emit stateChanged(QWasmMediaPlayer::Stopped);
+ }
+ };
+ m_pauseChangeEvent.reset(new qstdweb::EventCallback(m_video, "pause", pauseCallback));
+
+ // onunload
+ // we use lower level events here as to avert a crash on activate using the
+ // qtdweb see _qt_beforeUnload
+ emscripten::val window = emscripten::val::global("window");
+ window.call<void>("addEventListener", std::string("beforeunload"),
+ emscripten::val::module_property("mbeforeUnload"));
+}
+
+void QWasmVideoOutput::updateVideoElementGeometry(const QRect &windowGeometry)
+{
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO << windowGeometry;
+ QRect m_videoElementSource(windowGeometry.topLeft(), windowGeometry.size());
+
+ emscripten::val style = m_video["style"];
+ style.set("left", QString("%1px").arg(m_videoElementSource.left()).toStdString());
+ style.set("top", QString("%1px").arg(m_videoElementSource.top()).toStdString());
+ style.set("width", QString("%1px").arg(m_videoElementSource.width()).toStdString());
+ style.set("height", QString("%1px").arg(m_videoElementSource.height()).toStdString());
+ style.set("z-index", "999");
+
+ if (!m_hasVideoFrame) {
+ // offscreen
+ m_offscreen.set("width", m_videoElementSource.width());
+ m_offscreen.set("height", m_videoElementSource.height());
+ }
+}
+
+qint64 QWasmVideoOutput::getDuration()
+{
+ // qt duration is in ms
+ // js is sec
+
+ if (m_video.isUndefined() || m_video.isNull())
+ return 0;
+ return m_video["duration"].as<double>() * 1000;
+}
+
+void QWasmVideoOutput::newFrame(const QVideoFrame &frame)
+{
+ m_wasmSink->setVideoFrame(frame);
+}
+
+void QWasmVideoOutput::setPlaybackRate(qreal rate)
+{
+ m_video.set("playbackRate", emscripten::val(rate));
+}
+
+qreal QWasmVideoOutput::playbackRate()
+{
+ return (m_video.isUndefined() || m_video.isNull()) ? 0 : m_video["playbackRate"].as<float>();
+}
+
+void QWasmVideoOutput::checkNetworkState()
+{
+ int netState = m_video["networkState"].as<int>();
+
+ qCDebug(qWasmMediaVideoOutput) << netState;
+
+ switch (netState) {
+ case QWasmMediaPlayer::QWasmMediaNetworkState::NetworkEmpty: // no data
+ break;
+ case QWasmMediaPlayer::QWasmMediaNetworkState::NetworkIdle:
+ break;
+ case QWasmMediaPlayer::QWasmMediaNetworkState::NetworkLoading:
+ break;
+ case QWasmMediaPlayer::QWasmMediaNetworkState::NetworkNoSource: // no source
+ emit errorOccured(netState, QStringLiteral("No media source found"));
+ break;
+ };
+}
+
+void QWasmVideoOutput::videoComputeFrame(void *context)
+{
+ if (m_offscreenContext.isUndefined() || m_offscreenContext.isNull()) {
+ qCDebug(qWasmMediaVideoOutput) << "offscreen canvas context could not be found";
+ return;
+ }
+ emscripten::val document = emscripten::val::global("document");
+
+ emscripten::val videoElement =
+ document.call<emscripten::val>("getElementById", std::string(m_videoSurfaceId));
+
+ if (videoElement.isUndefined() || videoElement.isNull()) {
+ qCDebug(qWasmMediaVideoOutput) << "video element could not be found";
+ return;
+ }
+
+ const int videoWidth = videoElement["videoWidth"].as<int>();
+ const int videoHeight = videoElement["videoHeight"].as<int>();
+
+ if (videoWidth == 0 || videoHeight == 0)
+ return;
+
+ m_offscreenContext.call<void>("drawImage", videoElement, 0, 0, videoWidth, videoHeight);
+
+ emscripten::val frame = // one frame, Uint8ClampedArray
+ m_offscreenContext.call<emscripten::val>("getImageData", 0, 0, videoWidth, videoHeight);
+
+ const QSize frameBytesAllocationSize(videoWidth, videoHeight);
+
+ // this seems to work ok, even though getImageData returns a Uint8ClampedArray
+ QByteArray frameBytes = qstdweb::Uint8Array(frame["data"]).copyToQByteArray();
+
+ QVideoFrameFormat frameFormat =
+ QVideoFrameFormat(frameBytesAllocationSize, QVideoFrameFormat::Format_RGBA8888);
+
+ auto *textureDescription = QVideoTextureHelper::textureDescription(frameFormat.pixelFormat());
+
+ QVideoFrame vFrame(
+ new QMemoryVideoBuffer(frameBytes,
+ textureDescription->strideForWidth(frameFormat.frameWidth())),
+ frameFormat);
+ QWasmVideoOutput *wasmVideoOutput = reinterpret_cast<QWasmVideoOutput *>(context);
+
+ if (!wasmVideoOutput->m_wasmSink) {
+ qWarning() << "ERROR ALERT!! video sink not set";
+ }
+ wasmVideoOutput->m_wasmSink->setVideoFrame(vFrame);
+}
+
+
+void QWasmVideoOutput::videoFrameCallback(emscripten::val now, emscripten::val metadata)
+{
+ Q_UNUSED(now)
+ Q_UNUSED(metadata)
+
+ emscripten::val videoElement =
+ emscripten::val::global("document").
+ call<emscripten::val>("getElementById",
+ std::string(m_videoSurfaceId));
+
+ emscripten::val oneVideoFrame = val::global("VideoFrame").new_(videoElement);
+
+ if (oneVideoFrame.isNull() || oneVideoFrame.isUndefined()) {
+ qCDebug(qWasmMediaVideoOutput) << Q_FUNC_INFO
+ << "ERROR" << "failed to construct VideoFrame";
+ return;
+ }
+ emscripten::val frameBytesAllocationSize = oneVideoFrame.call<emscripten::val>("allocationSize");
+
+ emscripten::val frameBuffer =
+ emscripten::val::global("Uint8Array").new_(frameBytesAllocationSize);
+ QWasmVideoOutput *wasmVideoOutput =
+ reinterpret_cast<QWasmVideoOutput*>(videoElement["data-qvideocontext"].as<quintptr>());
+
+ qstdweb::PromiseCallbacks copyToCallback;
+ copyToCallback.thenFunc = [wasmVideoOutput, oneVideoFrame, frameBuffer, videoElement]
+ (emscripten::val frameLayout)
+ {
+ if (frameLayout.isNull() || frameLayout.isUndefined()) {
+ qCDebug(qWasmMediaVideoOutput) << "theres no frameLayout";
+ return;
+ }
+
+ // frameBuffer now has a new frame, send to Qt
+ const QSize frameSize(oneVideoFrame["displayWidth"].as<int>(),
+ oneVideoFrame["displayHeight"].as<int>());
+
+
+ QByteArray frameBytes = QByteArray::fromEcmaUint8Array(frameBuffer);
+
+ QVideoFrameFormat::PixelFormat pixelFormat = fromJsPixelFormat(oneVideoFrame["format"].as<std::string>());
+ if (pixelFormat == QVideoFrameFormat::Format_Invalid) {
+ qWarning() << "Invalid pixel format";
+ return;
+ }
+ QVideoFrameFormat frameFormat = QVideoFrameFormat(frameSize, pixelFormat);
+
+ auto *textureDescription = QVideoTextureHelper::textureDescription(frameFormat.pixelFormat());
+
+ QVideoFrame vFrame(
+ new QMemoryVideoBuffer(frameBytes,
+ textureDescription->strideForWidth(frameFormat.frameWidth())),
+ frameFormat);
+
+ if (!wasmVideoOutput) {
+ qCDebug(qWasmMediaVideoOutput) << "ERROR:"
+ << "data-qvideocontext not found";
+ return;
+ }
+ if (!wasmVideoOutput->m_wasmSink) {
+ qWarning() << "ERROR ALERT!! video sink not set";
+ return;
+ }
+ wasmVideoOutput->m_wasmSink->setVideoFrame(vFrame);
+ oneVideoFrame.call<emscripten::val>("close");
+ };
+ copyToCallback.catchFunc = [&, wasmVideoOutput, oneVideoFrame, videoElement](emscripten::val error)
+ {
+ qCDebug(qWasmMediaVideoOutput) << "Error"
+ << QString::fromStdString(error["name"].as<std::string>())
+ << QString::fromStdString(error["message"].as<std::string>()) ;
+
+ oneVideoFrame.call<emscripten::val>("close");
+ wasmVideoOutput->stop();
+ return;
+ };
+
+ qstdweb::Promise::make(oneVideoFrame, "copyTo", std::move(copyToCallback), frameBuffer);
+
+ videoElement.call<emscripten::val>("requestVideoFrameCallback",
+ emscripten::val::module_property("qtVideoFrameTimerCallback"));
+
+}
+
+void QWasmVideoOutput::videoFrameTimerCallback()
+{
+ static auto frame = [](double frameTime, void *context) -> int {
+ Q_UNUSED(frameTime);
+ QWasmVideoOutput *videoOutput = reinterpret_cast<QWasmVideoOutput *>(context);
+
+ emscripten::val document = emscripten::val::global("document");
+ emscripten::val videoElement =
+ document.call<emscripten::val>("getElementById", std::string(m_videoSurfaceId));
+
+ if (videoElement["paused"].as<bool>() || videoElement["ended"].as<bool>())
+ return false;
+
+ videoOutput->videoComputeFrame(context);
+
+ return true;
+ };
+
+ emscripten_request_animation_frame_loop(frame, this);
+ // about 60 fps
+}
+
+
+QVideoFrameFormat::PixelFormat QWasmVideoOutput::fromJsPixelFormat(std::string videoFormat)
+{
+ if (videoFormat == "I420")
+ return QVideoFrameFormat::Format_YUV420P;
+ // no equivalent pixel format
+ // else if (videoFormat == "I420A")
+ else if (videoFormat == "I422")
+ return QVideoFrameFormat::Format_YUV422P;
+ // no equivalent pixel format
+ // else if (videoFormat == "I444")
+ else if (videoFormat == "NV12")
+ return QVideoFrameFormat::Format_NV12;
+ else if (videoFormat == "RGBA")
+ return QVideoFrameFormat::Format_RGBA8888;
+ else if (videoFormat == "I420")
+ return QVideoFrameFormat::Format_YUV420P;
+ else if (videoFormat == "RGBX")
+ return QVideoFrameFormat::Format_RGBX8888;
+ else if (videoFormat == "BGRA")
+ return QVideoFrameFormat::Format_BGRA8888;
+ else if (videoFormat == "BGRX")
+ return QVideoFrameFormat::Format_BGRX8888;
+
+ return QVideoFrameFormat::Format_Invalid;
+}
+
+
+emscripten::val QWasmVideoOutput::getDeviceCapabilities()
+{
+ emscripten::val stream = m_video["srcObject"];
+ if (!stream.isUndefined() || !stream["getVideoTracks"].isUndefined()) {
+ emscripten::val tracks = stream.call<emscripten::val>("getVideoTracks");
+ if (!tracks.isUndefined()) {
+ if (tracks["length"].as<int>() == 0)
+ return emscripten::val::undefined();
+
+ emscripten::val track = tracks[0];
+ if (!track.isUndefined()) {
+ emscripten::val trackCaps = emscripten::val::undefined();
+ if (!track["getCapabilities"].isUndefined())
+ trackCaps = track.call<emscripten::val>("getCapabilities");
+ else // firefox does not support getCapabilities
+ trackCaps = track.call<emscripten::val>("getSettings");
+
+ if (!trackCaps.isUndefined())
+ return trackCaps;
+ }
+ }
+ } else {
+ // camera not started track capabilities not available
+ emit errorOccured(QMediaPlayer::ResourceError, QStringLiteral("capabilities not available"));
+ }
+
+ return emscripten::val::undefined();
+}
+
+bool QWasmVideoOutput::setDeviceSetting(const std::string &key, emscripten::val value)
+{
+ emscripten::val stream = m_video["srcObject"];
+ if (stream.isNull() || stream.isUndefined()
+ || stream["getVideoTracks"].isUndefined())
+ return false;
+
+ emscripten::val tracks = stream.call<emscripten::val>("getVideoTracks");
+ if (!tracks.isNull() || !tracks.isUndefined()) {
+ if (tracks["length"].as<int>() == 0)
+ return false;
+
+ emscripten::val track = tracks[0];
+ emscripten::val contraint = emscripten::val::object();
+ contraint.set(std::move(key), value);
+ track.call<emscripten::val>("applyConstraints", contraint);
+ return true;
+ }
+
+ return false;
+}
+
+EMSCRIPTEN_BINDINGS(qtwasmvideooutput) {
+ emscripten::function("qtVideoFrameTimerCallback", &QWasmVideoOutput::videoFrameCallback);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwasmvideooutput_p.cpp"
diff --git a/src/plugins/multimedia/wasm/common/qwasmvideooutput_p.h b/src/plugins/multimedia/wasm/common/qwasmvideooutput_p.h
new file mode 100644
index 000000000..dc4a762bf
--- /dev/null
+++ b/src/plugins/multimedia/wasm/common/qwasmvideooutput_p.h
@@ -0,0 +1,152 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QWASMVIDEOOUTPUT_H
+#define QWASMVIDEOOUTPUT_H
+
+#include <QObject>
+
+#include <emscripten/val.h>
+#include <QMediaPlayer>
+#include <QVideoFrame>
+
+#include "qwasmmediaplayer_p.h"
+#include <QtCore/qloggingcategory.h>
+
+#include <private/qstdweb_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qWasmMediaVideoOutput)
+
+class QVideoSink;
+
+class QWasmVideoOutput : public QObject
+{
+ Q_OBJECT
+public:
+ enum WasmVideoMode { VideoOutput, Camera };
+ Q_ENUM(WasmVideoMode)
+
+ explicit QWasmVideoOutput(QObject *parent = nullptr);
+
+ void setVideoSize(const QSize &);
+ void start();
+ void stop();
+ void reset();
+ void pause();
+
+ void setSurface(QVideoSink *surface);
+ emscripten::val surfaceElement();
+
+ bool isReady() const;
+
+ void setSource(const QUrl &url);
+ void setSource(QIODevice *stream);
+ void setVolume(qreal volume);
+ void setMuted(bool muted);
+
+ qint64 getCurrentPosition();
+ void seekTo(qint64 position);
+ bool isVideoSeekable();
+ void setPlaybackRate(qreal rate);
+ qreal playbackRate();
+
+ qint64 getDuration();
+ void newFrame(const QVideoFrame &newFrame);
+
+ void createVideoElement(const std::string &id);
+ void createOffscreenElement(const QSize &offscreenSize);
+ void doElementCallbacks();
+ void updateVideoElementGeometry(const QRect &windowGeometry);
+ void updateVideoElementSource(const QString &src);
+ void addCameraSourceElement(const std::string &id);
+ void removeSourceElement();
+ void setVideoMode(QWasmVideoOutput::WasmVideoMode mode);
+
+ void setHasAudio(bool needsAudio) { m_hasAudio = needsAudio; }
+
+ bool hasCapability(const QString &cap);
+ emscripten::val getDeviceCapabilities();
+ bool setDeviceSetting(const std::string &key, emscripten::val value);
+ bool isCameraReady() { return m_cameraIsReady; }
+
+ static void videoFrameCallback(emscripten::val now, emscripten::val metadata);
+ void videoFrameTimerCallback();
+ // mediacapturesession has the videosink
+ QVideoSink *m_wasmSink = nullptr;
+
+ emscripten::val currentVideoElement() { return m_video; }
+
+Q_SIGNALS:
+ void readyChanged(bool);
+ void bufferingChanged(qint32 percent);
+ void errorOccured(qint32 code, const QString &message);
+ void stateChanged(QWasmMediaPlayer::QWasmMediaPlayerState newState);
+ void progressChanged(qint32 position);
+ void durationChanged(qint64 duration);
+ void statusChanged(QMediaPlayer::MediaStatus status);
+ void sizeChange(qint32 width, qint32 height);
+ void metaDataLoaded();
+
+private:
+ void checkNetworkState();
+ void videoComputeFrame(void *context);
+ void getDeviceSettings();
+
+ static QVideoFrameFormat::PixelFormat fromJsPixelFormat(std::string videoFormat);
+
+ emscripten::val m_video = emscripten::val::undefined();
+ emscripten::val m_videoElementSource = emscripten::val::undefined();
+
+ QString m_source;
+ float m_requestedPosition = 0.0;
+ emscripten::val m_offscreen = emscripten::val::undefined();
+
+ bool m_shouldStop = false;
+ bool m_toBePaused = false;
+ bool m_isSeeking = false;
+ bool m_hasAudio = false;
+ bool m_cameraIsReady = false;
+ bool m_shouldBeStarted = false;
+
+ emscripten::val m_offscreenContext = emscripten::val::undefined();
+ QSize m_pendingVideoSize;
+ QWasmVideoOutput::WasmVideoMode m_currentVideoMode = QWasmVideoOutput::VideoOutput;
+ QMediaPlayer::MediaStatus m_currentMediaStatus;
+ qreal m_currentBufferedValue;
+
+ QScopedPointer<qstdweb::EventCallback> m_timeUpdateEvent;
+ QScopedPointer<qstdweb::EventCallback> m_playEvent;
+ QScopedPointer<qstdweb::EventCallback> m_endedEvent;
+ QScopedPointer<qstdweb::EventCallback> m_durationChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_loadedDataEvent;
+ QScopedPointer<qstdweb::EventCallback> m_errorChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_resizeChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_loadedMetadataChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_loadStartChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_canPlayChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_canPlayThroughChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_seekingChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_seekedChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_emptiedChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_stalledChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_waitingChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_playingChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_progressChangeEvent;
+ QScopedPointer<qstdweb::EventCallback> m_pauseChangeEvent;
+};
+
+QT_END_NAMESPACE
+#endif // QWASMVIDEOOUTPUT_H
diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmcamera.cpp b/src/plugins/multimedia/wasm/mediacapture/qwasmcamera.cpp
new file mode 100644
index 000000000..9bd63b081
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmcamera.cpp
@@ -0,0 +1,479 @@
+// Copyright (C) 2022 The Qt Company Ltd and/or its subsidiary(-ies).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmcamera_p.h"
+#include "qmediadevices.h"
+#include <qcameradevice.h>
+#include "private/qabstractvideobuffer_p.h"
+#include "private/qplatformvideosink_p.h"
+#include <private/qmemoryvideobuffer_p.h>
+#include <private/qvideotexturehelper_p.h>
+#include <private/qwasmmediadevices_p.h>
+
+#include "qwasmmediacapturesession_p.h"
+#include <common/qwasmvideooutput_p.h>
+
+#include <emscripten/val.h>
+#include <emscripten/bind.h>
+#include <emscripten/html5.h>
+#include <QUuid>
+#include <QTimer>
+
+#include <private/qstdweb_p.h>
+
+Q_LOGGING_CATEGORY(qWasmCamera, "qt.multimedia.wasm.camera")
+
+QWasmCamera::QWasmCamera(QCamera *camera)
+ : QPlatformCamera(camera),
+ m_cameraOutput(new QWasmVideoOutput),
+ m_cameraIsReady(false)
+{
+ QWasmMediaDevices *wasmMediaDevices =
+ static_cast<QWasmMediaDevices *>(QPlatformMediaIntegration::instance()->mediaDevices());
+
+ connect(wasmMediaDevices, &QWasmMediaDevices::videoInputsChanged,this, [this]() {
+ const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
+
+ if (!cameras.isEmpty()) {
+ if (m_cameraDev.id().isEmpty())
+ setCamera(cameras.at(0)); // default camera
+ else
+ setCamera(m_cameraDev);
+ return;
+ }
+ });
+
+ connect(this, &QWasmCamera::cameraIsReady, this, [this]() {
+ m_cameraIsReady = true;
+ if (m_cameraShouldStartActive) {
+ QTimer::singleShot(50, this, [this]() {
+ setActive(true);
+ });
+ }
+ });
+}
+
+QWasmCamera::~QWasmCamera() = default;
+
+bool QWasmCamera::isActive() const
+{
+ return m_cameraActive;
+}
+
+void QWasmCamera::setActive(bool active)
+{
+
+ if (!m_CaptureSession) {
+ emit error(QCamera::CameraError, QStringLiteral("video surface error"));
+ m_shouldBeActive = true;
+ return;
+ }
+
+ if (!m_cameraIsReady) {
+ m_cameraShouldStartActive = true;
+ return;
+ }
+
+ QVideoSink *sink = m_CaptureSession->videoSink();
+ if (!sink) {
+ qWarning() << Q_FUNC_INFO << "sink not ready";
+ return;
+ }
+
+ m_cameraOutput->setSurface(m_CaptureSession->videoSink());
+ m_cameraActive = active;
+ m_shouldBeActive = false;
+
+ if (m_cameraActive)
+ m_cameraOutput->start();
+ else
+ m_cameraOutput->pause();
+
+ updateCameraFeatures();
+ emit activeChanged(active);
+}
+
+void QWasmCamera::setCamera(const QCameraDevice &camera)
+{
+ if (!m_cameraDev.id().isEmpty())
+ return;
+
+ m_cameraOutput->setVideoMode(QWasmVideoOutput::Camera);
+
+ constexpr QSize initialSize(0, 0);
+ constexpr QRect initialRect(QPoint(0, 0), initialSize);
+ m_cameraOutput->createVideoElement(camera.id().toStdString()); // videoElementId
+ m_cameraOutput->createOffscreenElement(initialSize);
+ m_cameraOutput->updateVideoElementGeometry(initialRect);
+
+ const auto cameras = QMediaDevices::videoInputs();
+
+ if (std::find(cameras.begin(), cameras.end(), camera) != cameras.end()) {
+ m_cameraDev = camera;
+ createCamera(m_cameraDev);
+ emit cameraIsReady();
+ return;
+ }
+
+ if (cameras.count() > 0) {
+ m_cameraDev = camera;
+ createCamera(m_cameraDev);
+ emit cameraIsReady();
+ } else {
+ emit error(QCamera::CameraError, QStringLiteral("Failed to find a camera"));
+ }
+}
+
+bool QWasmCamera::setCameraFormat(const QCameraFormat &format)
+{
+ m_cameraFormat = format;
+
+ return true;
+}
+
+void QWasmCamera::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QWasmMediaCaptureSession *captureSession = static_cast<QWasmMediaCaptureSession *>(session);
+ if (m_CaptureSession == captureSession)
+ return;
+
+ m_CaptureSession = captureSession;
+
+ if (m_shouldBeActive)
+ setActive(true);
+}
+
+void QWasmCamera::setFocusMode(QCamera::FocusMode mode)
+{
+ if (!isFocusModeSupported(mode))
+ return;
+
+ static constexpr std::string_view focusModeString = "focusMode";
+ if (mode == QCamera::FocusModeManual)
+ m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val("manual"));
+ if (mode == QCamera::FocusModeAuto)
+ m_cameraOutput->setDeviceSetting(focusModeString.data(), emscripten::val("continuous"));
+ focusModeChanged(mode);
+}
+
+bool QWasmCamera::isFocusModeSupported(QCamera::FocusMode mode) const
+{
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return false;
+
+ emscripten::val focusMode = caps["focusMode"];
+ if (focusMode.isUndefined())
+ return false;
+
+ std::vector<std::string> focalModes;
+
+ for (int i = 0; i < focusMode["length"].as<int>(); i++)
+ focalModes.push_back(focusMode[i].as<std::string>());
+
+ // Do we need to take into account focusDistance
+ // it is not always available, and what distance
+ // would be far/near
+
+ bool found = false;
+ switch (mode) {
+ case QCamera::FocusModeAuto:
+ return std::find(focalModes.begin(), focalModes.end(), "continuous") != focalModes.end()
+ || std::find(focalModes.begin(), focalModes.end(), "single-shot")
+ != focalModes.end();
+ case QCamera::FocusModeAutoNear:
+ case QCamera::FocusModeAutoFar:
+ case QCamera::FocusModeHyperfocal:
+ case QCamera::FocusModeInfinity:
+ break;
+ case QCamera::FocusModeManual:
+ found = std::find(focalModes.begin(), focalModes.end(), "manual") != focalModes.end();
+ };
+ return found;
+}
+
+void QWasmCamera::setTorchMode(QCamera::TorchMode mode)
+{
+ if (!isTorchModeSupported(mode))
+ return;
+
+ if (m_wasmTorchMode == mode)
+ return;
+
+ static constexpr std::string_view torchModeString = "torchMode";
+ bool hasChanged = false;
+ switch (mode) {
+ case QCamera::TorchOff:
+ m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(false));
+ hasChanged = true;
+ break;
+ case QCamera::TorchOn:
+ m_cameraOutput->setDeviceSetting(torchModeString.data(), emscripten::val(true));
+ hasChanged = true;
+ break;
+ case QCamera::TorchAuto:
+ break;
+ };
+ m_wasmTorchMode = mode;
+ if (hasChanged)
+ torchModeChanged(m_wasmTorchMode);
+}
+
+bool QWasmCamera::isTorchModeSupported(QCamera::TorchMode mode) const
+{
+ if (!m_cameraIsReady)
+ return false;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return false;
+
+ emscripten::val exposureMode = caps["torch"];
+ if (exposureMode.isUndefined())
+ return false;
+
+ return (mode != QCamera::TorchAuto);
+}
+
+void QWasmCamera::setExposureMode(QCamera::ExposureMode mode)
+{
+ // TODO manually come up with exposureTime values ?
+ if (!isExposureModeSupported(mode))
+ return;
+
+ if (m_wasmExposureMode == mode)
+ return;
+
+ bool hasChanged = false;
+ static constexpr std::string_view exposureModeString = "exposureMode";
+ switch (mode) {
+ case QCamera::ExposureManual:
+ m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val("manual"));
+ hasChanged = true;
+ break;
+ case QCamera::ExposureAuto:
+ m_cameraOutput->setDeviceSetting(exposureModeString.data(), emscripten::val("continuous"));
+ hasChanged = true;
+ break;
+ default:
+ break;
+ };
+
+ if (hasChanged) {
+ m_wasmExposureMode = mode;
+ exposureModeChanged(m_wasmExposureMode);
+ }
+}
+
+bool QWasmCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
+{
+ if (!m_cameraIsReady)
+ return false;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return false;
+
+ emscripten::val exposureMode = caps["exposureMode"];
+ if (exposureMode.isUndefined())
+ return false;
+
+ std::vector<std::string> exposureModes;
+
+ for (int i = 0; i < exposureMode["length"].as<int>(); i++)
+ exposureModes.push_back(exposureMode[i].as<std::string>());
+
+ bool found = false;
+ switch (mode) {
+ case QCamera::ExposureAuto:
+ found = std::find(exposureModes.begin(), exposureModes.end(), "continuous")
+ != exposureModes.end();
+ break;
+ case QCamera::ExposureManual:
+ found = std::find(exposureModes.begin(), exposureModes.end(), "manual")
+ != exposureModes.end();
+ break;
+ default:
+ break;
+ };
+
+ return found;
+}
+
+void QWasmCamera::setExposureCompensation(float bias)
+{
+ if (!m_cameraIsReady)
+ return;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return;
+
+ emscripten::val exposureComp = caps["exposureCompensation"];
+ if (exposureComp.isUndefined())
+ return;
+ if (m_wasmExposureCompensation == bias)
+ return;
+
+ static constexpr std::string_view exposureCompensationModeString = "exposureCompensation";
+ m_cameraOutput->setDeviceSetting(exposureCompensationModeString.data(), emscripten::val(bias));
+ m_wasmExposureCompensation = bias;
+ emit exposureCompensationChanged(m_wasmExposureCompensation);
+}
+
+void QWasmCamera::setManualExposureTime(float secs)
+{
+ if (m_wasmExposureTime == secs)
+ return;
+
+ if (!m_cameraIsReady)
+ return;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ emscripten::val exposureTime = caps["exposureTime"];
+ if (exposureTime.isUndefined())
+ return;
+ static constexpr std::string_view exposureTimeString = "exposureTime";
+ m_cameraOutput->setDeviceSetting(exposureTimeString.data(), emscripten::val(secs));
+ m_wasmExposureTime = secs;
+ emit exposureTimeChanged(m_wasmExposureTime);
+}
+
+int QWasmCamera::isoSensitivity() const
+{
+ if (!m_cameraIsReady)
+ return 0;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return false;
+
+ emscripten::val isoSpeed = caps["iso"];
+ if (isoSpeed.isUndefined())
+ return 0;
+
+ return isoSpeed.as<double>();
+}
+
+void QWasmCamera::setManualIsoSensitivity(int sens)
+{
+ if (!m_cameraIsReady)
+ return;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return;
+
+ emscripten::val isoSpeed = caps["iso"];
+ if (isoSpeed.isUndefined())
+ return;
+ if (m_wasmIsoSensitivity == sens)
+ return;
+ static constexpr std::string_view isoString = "iso";
+ m_cameraOutput->setDeviceSetting(isoString.data(), emscripten::val(sens));
+ m_wasmIsoSensitivity = sens;
+ emit isoSensitivityChanged(m_wasmIsoSensitivity);
+}
+
+bool QWasmCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
+{
+ if (!m_cameraIsReady)
+ return false;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return false;
+
+ emscripten::val whiteBalanceMode = caps["whiteBalanceMode"];
+ if (whiteBalanceMode.isUndefined())
+ return false;
+
+ if (mode == QCamera::WhiteBalanceAuto || mode == QCamera::WhiteBalanceManual)
+ return true;
+
+ return false;
+}
+
+void QWasmCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
+{
+ if (!isWhiteBalanceModeSupported(mode))
+ return;
+
+ if (m_wasmWhiteBalanceMode == mode)
+ return;
+
+ bool hasChanged = false;
+ static constexpr std::string_view whiteBalanceModeString = "whiteBalanceMode";
+ switch (mode) {
+ case QCamera::WhiteBalanceAuto:
+ m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val("auto"));
+ hasChanged = true;
+ break;
+ case QCamera::WhiteBalanceManual:
+ m_cameraOutput->setDeviceSetting(whiteBalanceModeString.data(), emscripten::val("manual"));
+ hasChanged = true;
+ break;
+ default:
+ break;
+ };
+
+ if (hasChanged) {
+ m_wasmWhiteBalanceMode = mode;
+ emit whiteBalanceModeChanged(m_wasmWhiteBalanceMode);
+ }
+}
+
+void QWasmCamera::setColorTemperature(int temperature)
+{
+ if (!m_cameraIsReady)
+ return;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return;
+
+ emscripten::val whiteBalanceMode = caps["colorTemperature"];
+ if (whiteBalanceMode.isUndefined())
+ return;
+ if(m_wasmColorTemperature == temperature)
+ return;
+
+ static constexpr std::string_view colorBalanceString = "colorTemperature";
+ m_cameraOutput->setDeviceSetting(colorBalanceString.data(), emscripten::val(temperature));
+ m_wasmColorTemperature = temperature;
+ colorTemperatureChanged(m_wasmColorTemperature);
+}
+
+void QWasmCamera::createCamera(const QCameraDevice &camera)
+{
+ m_cameraOutput->addCameraSourceElement(camera.id().toStdString());
+}
+
+void QWasmCamera::updateCameraFeatures()
+{
+ if (!m_cameraIsReady)
+ return;
+
+ emscripten::val caps = m_cameraOutput->getDeviceCapabilities();
+ if (caps.isUndefined())
+ return;
+
+ QCamera::Features cameraFeatures;
+
+ if (!caps["colorTemperature"].isUndefined())
+ cameraFeatures |= QCamera::Feature::ColorTemperature;
+
+ if (!caps["exposureCompensation"].isUndefined())
+ cameraFeatures |= QCamera::Feature::ExposureCompensation;
+
+ if (!caps["iso"].isUndefined())
+ cameraFeatures |= QCamera::Feature::IsoSensitivity;
+
+ if (!caps["exposureTime"].isUndefined())
+ cameraFeatures |= QCamera::Feature::ManualExposureTime;
+
+ if (!caps["focusDistance"].isUndefined())
+ cameraFeatures |= QCamera::Feature::FocusDistance;
+
+ supportedFeaturesChanged(cameraFeatures);
+}
diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmcamera_p.h b/src/plugins/multimedia/wasm/mediacapture/qwasmcamera_p.h
new file mode 100644
index 000000000..7bb6d02d7
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmcamera_p.h
@@ -0,0 +1,99 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMCAMERA_H
+#define QWASMCAMERA_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformcamera_p.h>
+#include <private/qplatformvideodevices_p.h>
+#include <common/qwasmvideooutput_p.h>
+
+#include <QCameraDevice>
+#include <QtCore/qloggingcategory.h>
+
+#include <emscripten/val.h>
+#include <emscripten/bind.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qWasmCamera)
+
+class QWasmMediaCaptureSession;
+
+class QWasmCamera : public QPlatformCamera
+{
+ Q_OBJECT
+
+public:
+ explicit QWasmCamera(QCamera *camera);
+ ~QWasmCamera();
+
+ bool isActive() const override;
+ void setActive(bool active) override;
+
+ void setCamera(const QCameraDevice &camera) override;
+ bool setCameraFormat(const QCameraFormat &format) override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session) override;
+
+ void setFocusMode(QCamera::FocusMode mode) override;
+ bool isFocusModeSupported(QCamera::FocusMode mode) const override;
+
+ void setTorchMode(QCamera::TorchMode mode) override;
+ bool isTorchModeSupported(QCamera::TorchMode mode) const override;
+
+ void setExposureMode(QCamera::ExposureMode mode) override;
+ bool isExposureModeSupported(QCamera::ExposureMode mode) const override;
+ void setExposureCompensation(float bias) override;
+
+ void setManualExposureTime(float) override;
+ int isoSensitivity() const override;
+ void setManualIsoSensitivity(int) override;
+
+ bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override;
+ void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override;
+
+ void setColorTemperature(int temperature) override;
+
+ QWasmVideoOutput *cameraOutput() { return m_cameraOutput.data(); }
+Q_SIGNALS:
+ void cameraIsReady();
+private:
+ void createCamera(const QCameraDevice &camera);
+ void updateCameraFeatures();
+
+ QCameraDevice m_cameraDev;
+ QWasmMediaCaptureSession *m_CaptureSession = nullptr;
+ bool m_cameraActive = false;
+ QScopedPointer <QWasmVideoOutput> m_cameraOutput;
+
+ emscripten::val supportedCapabilities = emscripten::val::object(); // browser
+ emscripten::val currentCapabilities = emscripten::val::object(); // camera track
+ emscripten::val currentSettings = emscripten::val::object(); // camera track
+
+ QCamera::TorchMode m_wasmTorchMode;
+ QCamera::ExposureMode m_wasmExposureMode;
+ float m_wasmExposureTime;
+ float m_wasmExposureCompensation;
+ int m_wasmIsoSensitivity;
+ QCamera::WhiteBalanceMode m_wasmWhiteBalanceMode;
+ int m_wasmColorTemperature;
+ bool m_cameraIsReady = false;
+ bool m_cameraShouldStartActive = false;
+ bool m_shouldBeActive = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMCAMERA_H
diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmimagecapture.cpp b/src/plugins/multimedia/wasm/mediacapture/qwasmimagecapture.cpp
new file mode 100644
index 000000000..f62d6f1a6
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmimagecapture.cpp
@@ -0,0 +1,130 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmimagecapture_p.h"
+#include <qimagewriter.h>
+#include "qwasmmediacapturesession_p.h"
+#include "qwasmcamera_p.h"
+#include "qwasmvideosink_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qWasmImageCapture, "qt.multimedia.wasm.imagecapture")
+/* TODO
+signals:
+imageExposed
+*/
+QWasmImageCapture::QWasmImageCapture(QImageCapture *parent) : QPlatformImageCapture(parent) { }
+
+QWasmImageCapture::~QWasmImageCapture() = default;
+
+int QWasmImageCapture::capture(const QString &fileName)
+{
+ if (!isReadyForCapture()) {
+ emit error(m_lastId, QImageCapture::NotReadyError, msgCameraNotReady());
+ return -1;
+ }
+
+ // TODO if fileName.isEmpty() we choose filename and location
+
+ QImage image = takePicture();
+ if (image.isNull())
+ return -1;
+
+ QImageWriter writer(fileName);
+ // TODO
+ // writer.setQuality(quality);
+ // writer.setFormat("png");
+
+ if (writer.write(image)) {
+ qCDebug(qWasmImageCapture) << Q_FUNC_INFO << "image saved";
+ emit imageSaved(m_lastId, fileName);
+ } else {
+ QImageCapture::Error err = (writer.error() == QImageWriter::UnsupportedFormatError)
+ ? QImageCapture::FormatError
+ : QImageCapture::ResourceError;
+
+ emit error(m_lastId, err, writer.errorString());
+ }
+
+ return m_lastId;
+}
+
+int QWasmImageCapture::captureToBuffer()
+{
+ if (!isReadyForCapture()) {
+ emit error(m_lastId, QImageCapture::NotReadyError, msgCameraNotReady());
+ return -1;
+ }
+
+ QImage image = takePicture();
+ if (image.isNull())
+ return -1;
+
+ emit imageCaptured(m_lastId, image);
+ return m_lastId;
+}
+
+QImage QWasmImageCapture::takePicture()
+{
+ QVideoFrame thisFrame = m_captureSession->videoSink()->videoFrame();
+ if (!thisFrame.isValid())
+ return QImage();
+
+ m_lastId++;
+ emit imageAvailable(m_lastId, thisFrame);
+
+ QImage image = thisFrame.toImage();
+ if (image.isNull()) {
+ qCDebug(qWasmImageCapture) << Q_FUNC_INFO << "image is null";
+ emit error(m_lastId, QImageCapture::ResourceError, QStringLiteral("Resource error"));
+ return QImage();
+ }
+
+ emit imageCaptured(m_lastId, image);
+ if (m_settings.resolution().isValid() && m_settings.resolution() != image.size())
+ image = image.scaled(m_settings.resolution());
+
+ return image;
+}
+
+bool QWasmImageCapture::isReadyForCapture() const
+{
+ return m_isReadyForCapture;
+}
+
+QImageEncoderSettings QWasmImageCapture::imageSettings() const
+{
+ return m_settings;
+}
+
+void QWasmImageCapture::setImageSettings(const QImageEncoderSettings &settings)
+{
+ m_settings = settings;
+}
+
+void QWasmImageCapture::setReadyForCapture(bool isReady)
+{
+ if (m_isReadyForCapture != isReady) {
+ m_isReadyForCapture = isReady;
+ emit readyForCaptureChanged(m_isReadyForCapture);
+ }
+}
+
+void QWasmImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ QWasmMediaCaptureSession *captureSession = static_cast<QWasmMediaCaptureSession *>(session);
+ // nullptr clears
+ if (m_captureSession == captureSession)
+ return;
+
+ m_isReadyForCapture = captureSession;
+ if (captureSession) {
+ m_lastId = 0;
+ m_captureSession = captureSession;
+ }
+ emit readyForCaptureChanged(m_isReadyForCapture);
+ m_captureSession = captureSession;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmimagecapture_p.h b/src/plugins/multimedia/wasm/mediacapture/qwasmimagecapture_p.h
new file mode 100644
index 000000000..2e9e9b227
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmimagecapture_p.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMIMAGECAPTURE_H
+#define QWASMIMAGECAPTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QObject>
+#include <private/qplatformimagecapture_p.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qWasmImageCapture)
+
+class QWasmMediaCaptureSession;
+
+class QWasmImageCapture : public QPlatformImageCapture
+{
+ Q_OBJECT
+public:
+ explicit QWasmImageCapture(QImageCapture *parent = nullptr);
+ ~QWasmImageCapture();
+
+ bool isReadyForCapture() const override;
+
+ int capture(const QString &fileName) override;
+ int captureToBuffer() override;
+
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+
+ void setReadyForCapture(bool isReady);
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+private:
+ QImage takePicture();
+
+ // weak
+ QWasmMediaCaptureSession *m_captureSession = nullptr;
+ QImageEncoderSettings m_settings;
+ bool m_isReadyForCapture = false;
+ int m_lastId = 0;
+};
+
+QT_END_NAMESPACE
+#endif // QWASMIMAGECAPTURE_H
diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmmediacapturesession.cpp b/src/plugins/multimedia/wasm/mediacapture/qwasmmediacapturesession.cpp
new file mode 100644
index 000000000..826650570
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmmediacapturesession.cpp
@@ -0,0 +1,111 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmmediacapturesession_p.h"
+#include "mediacapture/qwasmimagecapture_p.h"
+
+#include "qwasmcamera_p.h"
+#include <private/qplatformmediadevices_p.h>
+#include <private/qplatformmediaintegration_p.h>
+#include <private/qwasmmediadevices_p.h>
+
+Q_LOGGING_CATEGORY(qWasmMediaCaptureSession, "qt.multimedia.wasm.capturesession")
+
+QWasmMediaCaptureSession::QWasmMediaCaptureSession()
+{
+ QWasmMediaDevices *wasmMediaDevices = static_cast<QWasmMediaDevices *>(QPlatformMediaIntegration::instance()->mediaDevices());
+ wasmMediaDevices->initDevices();
+}
+
+QWasmMediaCaptureSession::~QWasmMediaCaptureSession() = default;
+
+QPlatformCamera *QWasmMediaCaptureSession::camera()
+{
+ return m_camera.data();
+}
+
+void QWasmMediaCaptureSession::setCamera(QPlatformCamera *camera)
+{
+ if (!camera) {
+ if (m_camera == nullptr)
+ return;
+ m_camera.reset(nullptr);
+ } else {
+ QWasmCamera *wasmCamera = static_cast<QWasmCamera *>(camera);
+ if (m_camera.data() == wasmCamera)
+ return;
+ m_camera.reset(wasmCamera);
+ m_camera->setCaptureSession(this);
+ }
+ emit cameraChanged();
+}
+
+QPlatformImageCapture *QWasmMediaCaptureSession::imageCapture()
+{
+ return m_imageCapture;
+}
+
+void QWasmMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCapture)
+{
+ if (m_imageCapture == imageCapture)
+ return;
+
+ if (m_imageCapture)
+ m_imageCapture->setCaptureSession(nullptr);
+
+ m_imageCapture = static_cast<QWasmImageCapture *>(imageCapture);
+
+ if (m_imageCapture) {
+ m_imageCapture->setCaptureSession(this);
+
+ m_imageCapture->setReadyForCapture(true);
+ emit imageCaptureChanged();
+ }
+}
+
+QPlatformMediaRecorder *QWasmMediaCaptureSession::mediaRecorder()
+{
+ return m_mediaRecorder;
+}
+
+void QWasmMediaCaptureSession::setMediaRecorder(QPlatformMediaRecorder *mediaRecorder)
+{
+ if (m_mediaRecorder == mediaRecorder)
+ return;
+
+ if (m_mediaRecorder)
+ m_mediaRecorder->setCaptureSession(nullptr);
+
+ m_mediaRecorder = static_cast<QWasmMediaRecorder *>(mediaRecorder);
+
+ if (m_mediaRecorder)
+ m_mediaRecorder->setCaptureSession(this);
+}
+
+void QWasmMediaCaptureSession::setAudioInput(QPlatformAudioInput *input)
+{
+ if (m_audioInput == input)
+ return;
+
+ m_needsAudio = (bool)input;
+ m_audioInput = input;
+}
+
+bool QWasmMediaCaptureSession::hasAudio()
+{
+ return m_needsAudio;
+}
+
+void QWasmMediaCaptureSession::setVideoPreview(QVideoSink *sink)
+{
+ if (m_wasmSink == sink)
+ return;
+ m_wasmSink = sink;
+}
+
+void QWasmMediaCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (m_audioOutput == output)
+ return;
+ m_audioOutput = output;
+}
diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmmediacapturesession_p.h b/src/plugins/multimedia/wasm/mediacapture/qwasmmediacapturesession_p.h
new file mode 100644
index 000000000..817580c90
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmmediacapturesession_p.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMMEDIACAPTURESESSION_H
+#define QWASMMEDIACAPTURESESSION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwasmimagecapture_p.h"
+
+#include <private/qplatformmediacapture_p.h>
+#include <private/qplatformmediaintegration_p.h>
+#include "qwasmmediarecorder_p.h"
+#include <QScopedPointer>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qWasmMediaCaptureSession)
+
+class QAudioInput;
+class QWasmCamera;
+
+class QWasmMediaCaptureSession : public QPlatformMediaCaptureSession
+{
+public:
+ explicit QWasmMediaCaptureSession();
+ ~QWasmMediaCaptureSession() override;
+
+ QPlatformCamera *camera() override;
+ void setCamera(QPlatformCamera *camera) override;
+
+ QPlatformImageCapture *imageCapture() override;
+ void setImageCapture(QPlatformImageCapture *imageCapture) override;
+
+ QPlatformMediaRecorder *mediaRecorder() override;
+ void setMediaRecorder(QPlatformMediaRecorder *recorder) override;
+
+ void setAudioInput(QPlatformAudioInput *input) override;
+ QPlatformAudioInput * audioInput() const { return m_audioInput; }
+ void setVideoPreview(QVideoSink *sink) override;
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+
+ bool hasAudio();
+ QVideoSink *videoSink() { return m_wasmSink; }
+
+private:
+ QWasmMediaRecorder *m_mediaRecorder = nullptr;
+
+ QScopedPointer <QWasmCamera> m_camera;
+
+ QWasmImageCapture *m_imageCapture = nullptr;
+
+ QPlatformAudioInput *m_audioInput = nullptr;
+ QPlatformAudioOutput *m_audioOutput = nullptr;
+ bool m_needsAudio = false;
+ QVideoSink *m_wasmSink = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMMEDIACAPTURESESSION_H
diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder.cpp b/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder.cpp
new file mode 100644
index 000000000..98f04616a
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder.cpp
@@ -0,0 +1,520 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmmediarecorder_p.h"
+#include "qwasmmediacapturesession_p.h"
+#include <private/qplatformmediadevices_p.h>
+#include <private/qplatformmediaintegration_p.h>
+#include "qwasmcamera_p.h"
+#include "qwasmaudioinput_p.h"
+
+#include <private/qstdweb_p.h>
+#include <QtCore/QIODevice>
+#include <QFile>
+#include <QTimer>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qWasmMediaRecorder, "qt.multimedia.wasm.mediarecorder")
+
+QWasmMediaRecorder::QWasmMediaRecorder(QMediaRecorder *parent)
+ : QPlatformMediaRecorder(parent)
+{
+ m_durationTimer.reset(new QElapsedTimer());
+ QPlatformMediaIntegration::instance()->mediaDevices(); // initialize getUserMedia
+}
+
+QWasmMediaRecorder::~QWasmMediaRecorder()
+{
+ if (m_outputTarget->isOpen())
+ m_outputTarget->close();
+
+ if (!m_mediaRecorder.isNull()) {
+ m_mediaStreamDataAvailable.reset(nullptr);
+ m_mediaStreamStopped.reset(nullptr);
+ m_mediaStreamError.reset(nullptr);
+ m_mediaStreamStart.reset(nullptr);
+ }
+}
+
+bool QWasmMediaRecorder::isLocationWritable(const QUrl &location) const
+{
+ return location.isValid() && (location.isLocalFile() || location.isRelative());
+}
+
+QMediaRecorder::RecorderState QWasmMediaRecorder::state() const
+{
+ QMediaRecorder::RecorderState recordingState = QMediaRecorder::StoppedState;
+
+ if (!m_mediaRecorder.isUndefined()) {
+ std::string state = m_mediaRecorder["state"].as<std::string>();
+ if (state == "recording")
+ recordingState = QMediaRecorder::RecordingState;
+ else if (state == "paused")
+ recordingState = QMediaRecorder::PausedState;
+ }
+ return recordingState;
+}
+
+qint64 QWasmMediaRecorder::duration() const
+{ // milliseconds
+ return m_durationMs;
+}
+
+void QWasmMediaRecorder::record(QMediaEncoderSettings &settings)
+{
+ if (!m_session)
+ return;
+
+ m_mediaSettings = settings;
+ initUserMedia();
+}
+
+void QWasmMediaRecorder::pause()
+{
+ if (!m_session || (m_mediaRecorder.isUndefined() || m_mediaRecorder.isNull())) {
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "could not find MediaRecorder";
+ return;
+ }
+ m_mediaRecorder.call<void>("pause");
+ emit stateChanged(state());
+}
+
+void QWasmMediaRecorder::resume()
+{
+ if (!m_session || (m_mediaRecorder.isUndefined() || m_mediaRecorder.isNull())) {
+ qCDebug(qWasmMediaRecorder)<< Q_FUNC_INFO << "could not find MediaRecorder";
+ return;
+ }
+
+ m_mediaRecorder.call<void>("resume");
+ emit stateChanged(state());
+}
+
+void QWasmMediaRecorder::stop()
+{
+ if (!m_session || (m_mediaRecorder.isUndefined() || m_mediaRecorder.isNull())) {
+ qCDebug(qWasmMediaRecorder)<< Q_FUNC_INFO << "could not find MediaRecorder";
+ return;
+ }
+ if (m_mediaRecorder["state"].as<std::string>() == "recording")
+ m_mediaRecorder.call<void>("stop");
+}
+
+void QWasmMediaRecorder::setCaptureSession(QPlatformMediaCaptureSession *session)
+{
+ m_session = static_cast<QWasmMediaCaptureSession *>(session);
+}
+
+bool QWasmMediaRecorder::hasCamera() const
+{
+ return m_session && m_session->camera();
+}
+
+void QWasmMediaRecorder::initUserMedia()
+{
+ setUpFileSink();
+ emscripten::val navigator = emscripten::val::global("navigator");
+ emscripten::val mediaDevices = navigator["mediaDevices"];
+
+ if (mediaDevices.isNull() || mediaDevices.isUndefined()) {
+ qCDebug(qWasmMediaRecorder) << "MediaDevices are undefined or null";
+ return;
+ }
+
+ if (!m_session)
+ return;
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << m_session;
+
+ emscripten::val stream = emscripten::val::undefined();
+ if (hasCamera()) {
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "has camera";
+ QWasmCamera *wasmCamera = reinterpret_cast<QWasmCamera *>(m_session->camera());
+
+ if (wasmCamera) {
+ emscripten::val m_video = wasmCamera->cameraOutput()->surfaceElement();
+ if (m_video.isNull() || m_video.isUndefined()) {
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "video element not found";
+ return;
+ }
+
+ stream = m_video["srcObject"];
+ if (stream.isNull() || stream.isUndefined()) {
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "Video input stream not found";
+ return;
+ }
+ }
+ } else {
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "has audio";
+ stream = static_cast<QWasmAudioInput *>(m_session->audioInput())->mediaStream();
+
+ if (stream.isNull() || stream.isUndefined()) {
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "Audio input stream not found";
+ return;
+ }
+ }
+ if (stream.isNull() || stream.isUndefined()) {
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "No input stream found";
+ return;
+ }
+
+ setStream(stream);
+}
+
+void QWasmMediaRecorder::startAudioRecording()
+{
+ startStream();
+}
+
+void QWasmMediaRecorder::setStream(emscripten::val stream)
+{
+ emscripten::val emMediaSettings = emscripten::val::object();
+ QMediaFormat::VideoCodec videoCodec = m_mediaSettings.videoCodec();
+ QMediaFormat::AudioCodec audioCodec = m_mediaSettings.audioCodec();
+ QMediaFormat::FileFormat fileFormat = m_mediaSettings.fileFormat();
+
+ // mime and codecs
+ QString mimeCodec;
+ if (!m_mediaSettings.mimeType().name().isEmpty()) {
+ mimeCodec = m_mediaSettings.mimeType().name();
+
+ if (videoCodec != QMediaFormat::VideoCodec::Unspecified)
+ mimeCodec += QStringLiteral(": codecs=");
+
+ if (audioCodec != QMediaFormat::AudioCodec::Unspecified) {
+ // TODO
+ }
+
+ if (fileFormat != QMediaFormat::UnspecifiedFormat)
+ mimeCodec += QMediaFormat::fileFormatName(m_mediaSettings.fileFormat());
+
+ emMediaSettings.set("mimeType", mimeCodec.toStdString());
+ }
+
+ if (m_mediaSettings.audioBitRate() > 0)
+ emMediaSettings.set("audioBitsPerSecond", emscripten::val(m_mediaSettings.audioBitRate()));
+
+ if (m_mediaSettings.videoBitRate() > 0)
+ emMediaSettings.set("videoBitsPerSecond", emscripten::val(m_mediaSettings.videoBitRate()));
+
+ // create the MediaRecorder, and set up data callback
+ m_mediaRecorder = emscripten::val::global("MediaRecorder").new_(stream, emMediaSettings);
+
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "m_mediaRecorder state:"
+ << QString::fromStdString(m_mediaRecorder["state"].as<std::string>());
+
+ if (m_mediaRecorder.isNull() || m_mediaRecorder.isUndefined()) {
+ qWarning() << "MediaRecorder could not be found";
+ return;
+ }
+ m_mediaRecorder.set("data-mediarecordercontext",
+ emscripten::val(quintptr(reinterpret_cast<void *>(this))));
+
+ if (!m_mediaStreamDataAvailable.isNull()) {
+ m_mediaStreamDataAvailable.reset();
+ m_mediaStreamStopped.reset();
+ m_mediaStreamError.reset();
+ m_mediaStreamStart.reset();
+ m_mediaStreamPause.reset();
+ m_mediaStreamResume.reset();
+ }
+
+ // dataavailable
+ auto callback = [](emscripten::val blob) {
+ if (blob.isUndefined() || blob.isNull()) {
+ qCDebug(qWasmMediaRecorder) << "blob is null";
+ return;
+ }
+ if (blob["target"].isUndefined() || blob["target"].isNull())
+ return;
+ if (blob["data"].isUndefined() || blob["data"].isNull())
+ return;
+ if (blob["target"]["data-mediarecordercontext"].isUndefined()
+ || blob["target"]["data-mediarecordercontext"].isNull())
+ return;
+
+ QWasmMediaRecorder *recorder = reinterpret_cast<QWasmMediaRecorder *>(
+ blob["target"]["data-mediarecordercontext"].as<quintptr>());
+
+ if (recorder) {
+ const double timeCode =
+ blob.hasOwnProperty("timecode") ? blob["timecode"].as<double>() : 0;
+ recorder->audioDataAvailable(blob["data"], timeCode);
+ }
+ };
+
+ m_mediaStreamDataAvailable.reset(
+ new qstdweb::EventCallback(m_mediaRecorder, "dataavailable", callback));
+
+ // stopped
+ auto stoppedCallback = [](emscripten::val event) {
+ if (event.isUndefined() || event.isNull()) {
+ qCDebug(qWasmMediaRecorder) << "event is null";
+ return;
+ }
+ qCDebug(qWasmMediaRecorder)
+ << "STOPPED: state changed"
+ << QString::fromStdString(event["target"]["state"].as<std::string>());
+
+ QWasmMediaRecorder *recorder = reinterpret_cast<QWasmMediaRecorder *>(
+ event["target"]["data-mediarecordercontext"].as<quintptr>());
+
+ if (recorder) {
+ recorder->m_isRecording = false;
+ recorder->m_durationTimer->invalidate();
+
+ emit recorder->stateChanged(recorder->state());
+ }
+ };
+
+ m_mediaStreamStopped.reset(
+ new qstdweb::EventCallback(m_mediaRecorder, "stop", stoppedCallback));
+
+ // error
+ auto errorCallback = [](emscripten::val theError) {
+ if (theError.isUndefined() || theError.isNull()) {
+ qCDebug(qWasmMediaRecorder) << "error is null";
+ return;
+ }
+ qCDebug(qWasmMediaRecorder)
+ << theError["code"].as<int>()
+ << QString::fromStdString(theError["message"].as<std::string>());
+
+ QWasmMediaRecorder *recorder = reinterpret_cast<QWasmMediaRecorder *>(
+ theError["target"]["data-mediarecordercontext"].as<quintptr>());
+
+ if (recorder) {
+ recorder->updateError(QMediaRecorder::ResourceError,
+ QString::fromStdString(theError["message"].as<std::string>()));
+ emit recorder->stateChanged(recorder->state());
+ }
+ };
+
+ m_mediaStreamError.reset(new qstdweb::EventCallback(m_mediaRecorder, "error", errorCallback));
+
+ // start
+ auto startCallback = [](emscripten::val event) {
+ if (event.isUndefined() || event.isNull()) {
+ qCDebug(qWasmMediaRecorder) << "event is null";
+ return;
+ }
+
+ qCDebug(qWasmMediaRecorder)
+ << "START: state changed"
+ << QString::fromStdString(event["target"]["state"].as<std::string>());
+
+ QWasmMediaRecorder *recorder = reinterpret_cast<QWasmMediaRecorder *>(
+ event["target"]["data-mediarecordercontext"].as<quintptr>());
+
+ if (recorder) {
+ recorder->m_isRecording = true;
+ recorder->m_durationTimer->start();
+ emit recorder->stateChanged(recorder->state());
+ }
+ };
+
+ m_mediaStreamStart.reset(new qstdweb::EventCallback(m_mediaRecorder, "start", startCallback));
+
+ // pause
+ auto pauseCallback = [](emscripten::val event) {
+ if (event.isUndefined() || event.isNull()) {
+ qCDebug(qWasmMediaRecorder) << "event is null";
+ return;
+ }
+
+ qCDebug(qWasmMediaRecorder)
+ << "pause: state changed"
+ << QString::fromStdString(event["target"]["state"].as<std::string>());
+
+ QWasmMediaRecorder *recorder = reinterpret_cast<QWasmMediaRecorder *>(
+ event["target"]["data-mediarecordercontext"].as<quintptr>());
+
+ if (recorder) {
+ recorder->m_isRecording = true;
+ recorder->m_durationTimer->start();
+ emit recorder->stateChanged(recorder->state());
+ }
+ };
+
+ m_mediaStreamPause.reset(new qstdweb::EventCallback(m_mediaRecorder, "pause", pauseCallback));
+
+ // resume
+ auto resumeCallback = [](emscripten::val event) {
+ if (event.isUndefined() || event.isNull()) {
+ qCDebug(qWasmMediaRecorder) << "event is null";
+ return;
+ }
+
+ qCDebug(qWasmMediaRecorder)
+ << "resume: state changed"
+ << QString::fromStdString(event["target"]["state"].as<std::string>());
+
+ QWasmMediaRecorder *recorder = reinterpret_cast<QWasmMediaRecorder *>(
+ event["target"]["data-mediarecordercontext"].as<quintptr>());
+
+ if (recorder) {
+ recorder->m_isRecording = true;
+ recorder->m_durationTimer->start();
+ emit recorder->stateChanged(recorder->state());
+ }
+ };
+
+ m_mediaStreamResume.reset(
+ new qstdweb::EventCallback(m_mediaRecorder, "resume", resumeCallback));
+
+ // set up what options we can
+ if (hasCamera())
+ setTrackContraints(m_mediaSettings, stream);
+ else
+ startStream();
+}
+
+void QWasmMediaRecorder::audioDataAvailable(emscripten::val blob, double timeCodeDifference)
+{
+ Q_UNUSED(timeCodeDifference)
+ if (blob.isUndefined() || blob.isNull()) {
+ qCDebug(qWasmMediaRecorder) << "blob is null";
+ return;
+ }
+
+ auto fileReader = std::make_shared<qstdweb::FileReader>();
+
+ fileReader->onError([=](emscripten::val theError) {
+ updateError(QMediaRecorder::ResourceError,
+ QString::fromStdString(theError["message"].as<std::string>()));
+ });
+
+ fileReader->onAbort([=](emscripten::val) {
+ updateError(QMediaRecorder::ResourceError, QStringLiteral("File read aborted"));
+ });
+
+ fileReader->onLoad([=](emscripten::val) {
+ if (fileReader->val().isNull() || fileReader->val().isUndefined())
+ return;
+ qstdweb::ArrayBuffer result = fileReader->result();
+ if (result.val().isNull() || result.val().isUndefined())
+ return;
+ QByteArray fileContent = qstdweb::Uint8Array(result).copyToQByteArray();
+
+ if (m_isRecording && !fileContent.isEmpty()) {
+ m_durationMs = m_durationTimer->elapsed();
+ if (m_outputTarget->isOpen())
+ m_outputTarget->write(fileContent, fileContent.length());
+ // we've read everything
+ emit durationChanged(m_durationMs);
+ qCDebug(qWasmMediaRecorder) << "duration changed" << m_durationMs;
+ }
+ });
+
+ fileReader->readAsArrayBuffer(qstdweb::Blob(blob));
+}
+
+// constraints are suggestions, as not all hardware supports all settings
+void QWasmMediaRecorder::setTrackContraints(QMediaEncoderSettings &settings, emscripten::val stream)
+{
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << settings.audioSampleRate();
+
+ if (stream.isUndefined() || stream.isNull()) {
+ qCDebug(qWasmMediaRecorder)<< Q_FUNC_INFO << "could not find MediaStream";
+ return;
+ }
+
+ emscripten::val navigator = emscripten::val::global("navigator");
+ emscripten::val mediaDevices = navigator["mediaDevices"];
+
+ // check which ones are supported
+ // emscripten::val allConstraints = mediaDevices.call<emscripten::val>("getSupportedConstraints");
+ // browsers only support some settings
+
+ emscripten::val videoParams = emscripten::val::object();
+ emscripten::val constraints = emscripten::val::object();
+
+ if (hasCamera()) {
+ if (settings.videoFrameRate() > 0)
+ videoParams.set("frameRate", emscripten::val(settings.videoFrameRate()));
+ if (settings.videoResolution().height() > 0)
+ videoParams.set("height",
+ emscripten::val(settings.videoResolution().height())); // viewportHeight?
+ if (settings.videoResolution().width() > 0)
+ videoParams.set("width", emscripten::val(settings.videoResolution().width()));
+
+ constraints.set("video", videoParams); // only video here
+ }
+
+ emscripten::val audioParams = emscripten::val::object();
+ if (settings.audioSampleRate() > 0)
+ audioParams.set("sampleRate", emscripten::val(settings.audioSampleRate())); // may not work
+ if (settings.audioBitRate() > 0)
+ audioParams.set("sampleSize", emscripten::val(settings.audioBitRate())); // may not work
+ if (settings.audioChannelCount() > 0)
+ audioParams.set("channelCount", emscripten::val(settings.audioChannelCount()));
+
+ constraints.set("audio", audioParams); // only audio here
+
+ if (hasCamera() && stream["active"].as<bool>()) {
+ emscripten::val videoTracks = emscripten::val::undefined();
+ videoTracks = stream.call<emscripten::val>("getVideoTracks");
+ if (videoTracks.isNull() || videoTracks.isUndefined()) {
+ qCDebug(qWasmMediaRecorder) << "no video tracks";
+ return;
+ }
+ if (videoTracks["length"].as<int>() > 0) {
+ // try to apply the video options
+ qstdweb::Promise::make(videoTracks[0], QStringLiteral("applyConstraints"),
+ { .thenFunc =
+ [this](emscripten::val result) {
+ Q_UNUSED(result)
+ startStream();
+ },
+ .catchFunc =
+ [this](emscripten::val theError) {
+ qWarning() << "setting video params failed error";
+ qCDebug(qWasmMediaRecorder)
+ << theError["code"].as<int>()
+ << QString::fromStdString(theError["message"].as<std::string>());
+ updateError(QMediaRecorder::ResourceError,
+ QString::fromStdString(theError["message"].as<std::string>()));
+ } },
+ constraints);
+ }
+ }
+}
+
+// this starts the recording stream
+void QWasmMediaRecorder::startStream()
+{
+ if (m_mediaRecorder.isUndefined() || m_mediaRecorder.isNull()) {
+ qCDebug(qWasmMediaRecorder) << Q_FUNC_INFO << "could not find MediaStream";
+ return;
+ }
+ qCDebug(qWasmMediaRecorder) << "m_mediaRecorder state:" <<
+ QString::fromStdString(m_mediaRecorder["state"].as<std::string>());
+
+ constexpr int sliceSizeInMs = 250; // TODO find what size is best
+ m_mediaRecorder.call<void>("start", emscripten::val(sliceSizeInMs));
+
+ /* this method can optionally be passed a timeslice argument with a value in milliseconds.
+ * If this is specified, the media will be captured in separate chunks of that duration,
+ * rather than the default behavior of recording the media in a single large chunk.*/
+
+ emit stateChanged(state());
+}
+
+void QWasmMediaRecorder::setUpFileSink()
+{
+ QString m_targetFileName = outputLocation().toLocalFile();
+ QString suffix = m_mediaSettings.mimeType().preferredSuffix();
+ if (m_targetFileName.isEmpty()) {
+ m_targetFileName = "/home/web_user/tmp." + suffix;
+ QPlatformMediaRecorder::setOutputLocation(m_targetFileName);
+ }
+
+ m_outputTarget = new QFile(m_targetFileName, this);
+ if (!m_outputTarget->open(QIODevice::WriteOnly)) {
+ qWarning() << "target file is not writable";
+ return;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder_p.h b/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder_p.h
new file mode 100644
index 000000000..c325e411b
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder_p.h
@@ -0,0 +1,89 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMMEDIARECORDER_H
+#define QWASMMEDIARECORDER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformmediarecorder_p.h>
+#include <private/qplatformmediacapture_p.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qloggingcategory.h>
+#include <QElapsedTimer>
+
+#include <emscripten.h>
+#include <emscripten/val.h>
+#include <emscripten/bind.h>
+#include <private/qstdweb_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qWasmMediaRecorder)
+
+class QWasmMediaCaptureSession;
+class QIODevice;
+
+class QWasmMediaRecorder final : public QObject, public QPlatformMediaRecorder
+{
+ Q_OBJECT
+public:
+ explicit QWasmMediaRecorder(QMediaRecorder *parent);
+ ~QWasmMediaRecorder() final;
+
+ bool isLocationWritable(const QUrl &location) const override;
+ QMediaRecorder::RecorderState state() const override;
+ qint64 duration() const override;
+ void record(QMediaEncoderSettings &settings) override;
+ void pause() override;
+ void resume() override;
+ void stop() override;
+
+ void setCaptureSession(QPlatformMediaCaptureSession *session);
+
+private:
+
+ bool hasCamera() const;
+ void startAudioRecording();
+ void setStream(emscripten::val stream);
+ void streamCallback(emscripten::val event);
+ void exceptionCallback(emscripten::val event);
+ void dataAvailableCallback(emscripten::val dataEvent);
+ void startStream();
+ void setTrackContraints(QMediaEncoderSettings &settings, emscripten::val stream);
+ void initUserMedia();
+ void audioDataAvailable(emscripten::val Blob, double timeCodeDifference);
+ void setUpFileSink();
+
+ emscripten::val m_mediaRecorder = emscripten::val::undefined();
+ emscripten::val m_mediaStream = emscripten::val::undefined();
+
+ QWasmMediaCaptureSession *m_session = nullptr;
+ QMediaEncoderSettings m_mediaSettings;
+ QIODevice *m_outputTarget;
+ QScopedPointer<qstdweb::EventCallback> m_mediaStreamDataAvailable;
+ QScopedPointer<qstdweb::EventCallback> m_mediaStreamStopped;
+ QScopedPointer<qstdweb::EventCallback> m_mediaStreamError;
+ QScopedPointer<qstdweb::EventCallback> m_mediaStreamStart;
+ QScopedPointer<qstdweb::EventCallback> m_mediaStreamPause;
+ QScopedPointer<qstdweb::EventCallback> m_mediaStreamResume;
+
+ qint64 m_durationMs = 0;
+ bool m_isRecording = false;
+ QScopedPointer <QElapsedTimer> m_durationTimer;
+
+private Q_SLOTS:
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMMEDIARECORDER_H
diff --git a/src/plugins/multimedia/wasm/mediaplayer/qwasmmediaplayer.cpp b/src/plugins/multimedia/wasm/mediaplayer/qwasmmediaplayer.cpp
new file mode 100644
index 000000000..75886b7c2
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediaplayer/qwasmmediaplayer.cpp
@@ -0,0 +1,475 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmmediaplayer_p.h"
+#include <common/qwasmvideooutput_p.h>
+#include <common/qwasmaudiooutput_p.h>
+#include "qaudiooutput.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QUuid>
+#include <QtGlobal>
+#include <QMimeDatabase>
+#include <QFileInfo>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lcMediaPlayer, "qt.multimedia.wasm.mediaplayer");
+
+QWasmMediaPlayer::QWasmMediaPlayer(QMediaPlayer *parent)
+ : QPlatformMediaPlayer(parent),
+ m_videoOutput(new QWasmVideoOutput),
+ m_State(QWasmMediaPlayer::Idle)
+{
+ qCDebug(lcMediaPlayer) << Q_FUNC_INFO << this;
+
+}
+
+QWasmMediaPlayer::~QWasmMediaPlayer()
+{
+ delete m_videoOutput;
+}
+
+void QWasmMediaPlayer::initVideo()
+{
+ m_videoOutput->setVideoMode(QWasmVideoOutput::VideoOutput);
+ QUuid videoElementId = QUuid::createUuid();
+ qCDebug(lcMediaPlayer) << Q_FUNC_INFO << "videoElementId"<< videoElementId << this;
+
+ m_videoOutput->createVideoElement(videoElementId.toString(QUuid::WithoutBraces).toStdString());
+ m_videoOutput->doElementCallbacks();
+ m_videoOutput->createOffscreenElement(QSize(1280, 720));
+ m_videoOutput->updateVideoElementGeometry(QRect(0, 0, 1280, 720)); // initial size 720p standard
+
+ connect(m_videoOutput, &QWasmVideoOutput::bufferingChanged, this,
+ &QWasmMediaPlayer::bufferingChanged);
+ connect(m_videoOutput, &QWasmVideoOutput::errorOccured, this,
+ &QWasmMediaPlayer::errorOccured);
+ connect(m_videoOutput, &QWasmVideoOutput::stateChanged, this,
+ &QWasmMediaPlayer::mediaStateChanged);
+ connect(m_videoOutput, &QWasmVideoOutput::progressChanged, this,
+ &QWasmMediaPlayer::setPositionChanged);
+ connect(m_videoOutput, &QWasmVideoOutput::durationChanged, this,
+ &QWasmMediaPlayer::setDurationChanged);
+ connect(m_videoOutput, &QWasmVideoOutput::sizeChange, this,
+ &QWasmMediaPlayer::videoSizeChanged);
+ connect(m_videoOutput, &QWasmVideoOutput::readyChanged, this,
+ &QWasmMediaPlayer::videoOutputReady);
+ connect(m_videoOutput, &QWasmVideoOutput::statusChanged, this,
+ &QWasmMediaPlayer::onMediaStatusChanged);
+ connect(m_videoOutput, &QWasmVideoOutput::metaDataLoaded, this,
+ &QWasmMediaPlayer::videoMetaDataChanged);
+
+ setVideoAvailable(true);
+}
+
+void QWasmMediaPlayer::initAudio()
+{
+ connect(m_audioOutput->q, &QAudioOutput::deviceChanged,
+ this, &QWasmMediaPlayer::updateAudioDevice);
+ connect(m_audioOutput->q, &QAudioOutput::volumeChanged,
+ this, &QWasmMediaPlayer::volumeChanged);
+ connect(m_audioOutput->q, &QAudioOutput::mutedChanged,
+ this, &QWasmMediaPlayer::mutedChanged);
+
+ connect(m_audioOutput, &QWasmAudioOutput::bufferingChanged, this,
+ &QWasmMediaPlayer::bufferingChanged);
+ connect(m_audioOutput, &QWasmAudioOutput::errorOccured, this,
+ &QWasmMediaPlayer::errorOccured);
+ connect(m_audioOutput, &QWasmAudioOutput::progressChanged, this,
+ &QWasmMediaPlayer::setPositionChanged);
+ connect(m_audioOutput, &QWasmAudioOutput::durationChanged, this,
+ &QWasmMediaPlayer::setDurationChanged);
+ connect(m_audioOutput, &QWasmAudioOutput::statusChanged, this,
+ &QWasmMediaPlayer::onMediaStatusChanged);
+ connect(m_audioOutput, &QWasmAudioOutput::stateChanged, this,
+ &QWasmMediaPlayer::mediaStateChanged);
+ setAudioAvailable(true);
+}
+
+qint64 QWasmMediaPlayer::duration() const
+{
+ return m_videoOutput->getDuration();
+}
+
+qint64 QWasmMediaPlayer::position() const
+{
+ if (mediaStatus() == QMediaPlayer::EndOfMedia)
+ return duration();
+
+ if (m_videoAvailable)
+ return m_videoOutput->getCurrentPosition();
+
+ return 0;
+}
+
+void QWasmMediaPlayer::setPosition(qint64 position)
+{
+ if (!isSeekable())
+ return;
+
+ const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
+
+ if (seekPosition == this->position())
+ return;
+
+ if (mediaStatus() == QMediaPlayer::EndOfMedia)
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+
+ if (m_videoAvailable)
+ return m_videoOutput->seekTo(position);
+
+ emit positionChanged(seekPosition);
+}
+
+void QWasmMediaPlayer::volumeChanged(float gain)
+{
+ if (m_State != QWasmMediaPlayer::Started)
+ return;
+
+ if (m_videoAvailable)
+ m_videoOutput->setVolume(gain);
+}
+
+void QWasmMediaPlayer::mutedChanged(bool muted)
+{
+ if (m_State != QWasmMediaPlayer::Started)
+ return;
+
+ if (m_videoAvailable)
+ m_videoOutput->setMuted(muted);
+}
+
+float QWasmMediaPlayer::bufferProgress() const
+{
+ return qBound(0.0, (m_bufferPercent * .01), 1.0);
+}
+
+bool QWasmMediaPlayer::isAudioAvailable() const
+{
+ return m_audioAvailable;
+}
+
+bool QWasmMediaPlayer::isVideoAvailable() const
+{
+ return m_videoAvailable;
+}
+
+QMediaTimeRange QWasmMediaPlayer::availablePlaybackRanges() const
+{
+ return m_availablePlaybackRange;
+}
+
+void QWasmMediaPlayer::updateAvailablePlaybackRanges()
+{
+ if (m_buffering) {
+ const qint64 pos = position();
+ const qint64 end = (duration() / 100) * m_bufferPercent;
+ m_availablePlaybackRange.addInterval(pos, end);
+ } else if (isSeekable()) {
+ m_availablePlaybackRange = QMediaTimeRange(0, duration());
+ } else {
+ m_availablePlaybackRange = QMediaTimeRange();
+ }
+}
+
+qreal QWasmMediaPlayer::playbackRate() const
+{
+ if (m_State != QWasmMediaPlayer::Started)
+ return 0;
+
+ if (isVideoAvailable())
+ return m_videoOutput->playbackRate();
+ return 0;
+}
+
+void QWasmMediaPlayer::setPlaybackRate(qreal rate)
+{
+ if (m_State != QWasmMediaPlayer::Started || !isVideoAvailable())
+ return;
+
+ m_videoOutput->setPlaybackRate(rate);
+ emit playbackRateChanged(rate);
+}
+
+QUrl QWasmMediaPlayer::media() const
+{
+ return m_mediaContent;
+}
+
+const QIODevice *QWasmMediaPlayer::mediaStream() const
+{
+ return m_mediaStream;
+}
+
+void QWasmMediaPlayer::setMedia(const QUrl &mediaContent, QIODevice *stream)
+{
+ qCDebug(lcMediaPlayer) << Q_FUNC_INFO << mediaContent << stream;
+ QMimeDatabase db;
+
+ if (mediaContent.isEmpty()) {
+ if (stream) {
+ m_mediaStream = stream;
+ qCDebug(lcMediaPlayer) << db.mimeTypeForData(stream).name();
+ if (db.mimeTypeForData(stream).name().contains("audio")) {
+ setAudioAvailable(true);
+ m_audioOutput->setSource(m_mediaStream);
+ } else { // treat octet-stream as video
+ setVideoAvailable(true);
+ m_videoOutput->setSource(m_mediaStream);
+ }
+ } else {
+
+ setMediaStatus(QMediaPlayer::NoMedia);
+ }
+ } else {
+ QString sourceFile = mediaContent.toLocalFile();
+ qCDebug(lcMediaPlayer) << db.mimeTypeForFile(QFileInfo(sourceFile)).name();
+ if (db.mimeTypeForFile(QFileInfo(sourceFile)).name().contains("audio")) {
+ setAudioAvailable(true);
+ m_audioOutput->setSource(mediaContent);
+ } else { // treat octet-stream as video
+ setVideoAvailable(true);
+ m_videoOutput->setSource(mediaContent);
+ }
+ }
+
+ resetBufferingProgress();
+}
+
+void QWasmMediaPlayer::setVideoSink(QVideoSink *sink)
+{
+ if (m_videoSink == sink)
+ return;
+
+ m_videoSink = sink;
+
+ if (!m_videoSink)
+ return;
+
+ initVideo();
+ m_videoOutput->setSurface(sink);
+ setVideoAvailable(true);
+ if (isAudioAvailable() && m_audioOutput)
+ m_audioOutput->setVideoElement(m_videoOutput->currentVideoElement());
+}
+
+void QWasmMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
+{
+ if (m_audioOutput == output)
+ return;
+
+ if (m_audioOutput)
+ m_audioOutput->q->disconnect(this);
+ m_audioOutput = static_cast<QWasmAudioOutput *>(output);
+ setAudioAvailable(true);
+}
+
+void QWasmMediaPlayer::updateAudioDevice()
+{
+ if (m_audioOutput) {
+ m_audioOutput->setAudioDevice(m_audioOutput->q->device());
+ }
+}
+
+void QWasmMediaPlayer::play()
+{
+ resetCurrentLoop();
+
+ if (isVideoAvailable()) {
+ m_videoOutput->start();
+ m_playWhenReady = true;
+ } else {
+ initAudio();
+ if (isAudioAvailable()) {
+ m_audioOutput->start();
+ }
+ }
+
+#ifdef DEBUG_AUDIOENGINE
+ QAudioEnginePrivate::checkNoError("play");
+#endif
+}
+
+void QWasmMediaPlayer::pause()
+{
+ if ((m_State
+ & (QWasmMediaPlayer::Started | QWasmMediaPlayer::Paused
+ | QWasmMediaPlayer::PlaybackCompleted)) == 0) {
+ return;
+ }
+ if (isVideoAvailable()) {
+ m_videoOutput->pause();
+ } else {
+ m_audioOutput->pause();
+ stateChanged(QMediaPlayer::PausedState);
+ }
+}
+
+void QWasmMediaPlayer::stop()
+{
+ m_playWhenReady = false;
+
+ if (m_State == QWasmMediaPlayer::Idle || m_State == QWasmMediaPlayer::PlaybackCompleted
+ || m_State == QWasmMediaPlayer::Stopped) {
+ qWarning() << Q_FUNC_INFO << __LINE__;
+ return;
+ }
+
+ if (isVideoAvailable()) {
+ m_videoOutput->stop();
+ } else {
+ m_audioOutput->stop();
+ }
+
+}
+
+bool QWasmMediaPlayer::isSeekable() const
+{
+ return isVideoAvailable() && m_videoOutput->isVideoSeekable();
+}
+
+void QWasmMediaPlayer::errorOccured(qint32 code, const QString &message)
+{
+ QString errorString;
+ QMediaPlayer::Error error = QMediaPlayer::ResourceError;
+
+ switch (code) {
+ case QWasmMediaNetworkState::NetworkEmpty: // no data
+ break;
+ case QWasmMediaNetworkState::NetworkIdle:
+ break;
+ case QWasmMediaNetworkState::NetworkLoading:
+ break;
+ case QWasmMediaNetworkState::NetworkNoSource: // no source
+ error = QMediaPlayer::ResourceError;
+ errorString = message;
+ break;
+ };
+
+ emit QPlatformMediaPlayer::error(error, errorString);
+}
+
+void QWasmMediaPlayer::bufferingChanged(qint32 percent)
+{
+ m_buffering = percent != 100;
+ m_bufferPercent = percent;
+
+ updateAvailablePlaybackRanges();
+ emit bufferProgressChanged(bufferProgress());
+}
+
+void QWasmMediaPlayer::videoSizeChanged(qint32 width, qint32 height)
+{
+ QSize newSize(width, height);
+
+ if (width == 0 || height == 0 || newSize == m_videoSize)
+ return;
+
+ m_videoSize = newSize;
+}
+
+void QWasmMediaPlayer::mediaStateChanged(QWasmMediaPlayer::QWasmMediaPlayerState state)
+{
+ m_State = state;
+ QMediaPlayer::PlaybackState m_mediaPlayerState;
+ switch (m_State) {
+ case QWasmMediaPlayer::Started:
+ m_mediaPlayerState = QMediaPlayer::PlayingState;
+ break;
+ case QWasmMediaPlayer::Paused:
+ m_mediaPlayerState = QMediaPlayer::PausedState;
+ break;
+ case QWasmMediaPlayer::Stopped:
+ m_mediaPlayerState = QMediaPlayer::StoppedState;
+ break;
+ default:
+ m_mediaPlayerState = QMediaPlayer::StoppedState;
+ break;
+ };
+
+ QPlatformMediaPlayer::stateChanged(m_mediaPlayerState);
+}
+
+int QWasmMediaPlayer::trackCount(TrackType trackType)
+{
+ Q_UNUSED(trackType)
+ // TODO QTBUG-108517
+ return 0; // tracks.count();
+}
+
+void QWasmMediaPlayer::setPositionChanged(qint64 position)
+{
+ QPlatformMediaPlayer::positionChanged(position);
+}
+
+void QWasmMediaPlayer::setDurationChanged(qint64 duration)
+{
+ QPlatformMediaPlayer::durationChanged(duration);
+}
+
+void QWasmMediaPlayer::videoOutputReady(bool ready)
+{
+ setVideoAvailable(ready);
+
+ if (m_playWhenReady && m_videoOutput->isReady())
+ play();
+}
+
+void QWasmMediaPlayer::setMediaStatus(QMediaPlayer::MediaStatus status)
+{
+ mediaStatusChanged(status);
+
+ switch (status) {
+ case QMediaPlayer::NoMedia:
+ case QMediaPlayer::InvalidMedia:
+ emit durationChanged(0);
+ break;
+ case QMediaPlayer::EndOfMedia:
+ setPositionChanged(position());
+ default:
+ break;
+ };
+}
+
+void QWasmMediaPlayer::setAudioAvailable(bool available)
+{
+ if (m_audioAvailable == available)
+ return;
+
+ m_audioAvailable = available;
+ emit audioAvailableChanged(m_audioAvailable);
+}
+
+void QWasmMediaPlayer::setVideoAvailable(bool available)
+{
+ if (m_videoAvailable == available)
+ return;
+
+ if (!available)
+ m_videoSize = QSize();
+
+ m_videoAvailable = available;
+ emit videoAvailableChanged(m_videoAvailable);
+}
+
+void QWasmMediaPlayer::resetBufferingProgress()
+{
+ m_buffering = false;
+ m_bufferPercent = 0;
+ m_availablePlaybackRange = QMediaTimeRange();
+}
+
+void QWasmMediaPlayer::onMediaStatusChanged(QMediaPlayer::MediaStatus status)
+{
+ setMediaStatus(status);
+}
+
+void QWasmMediaPlayer::videoMetaDataChanged()
+{
+ metaDataChanged();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwasmmediaplayer_p.cpp"
diff --git a/src/plugins/multimedia/wasm/mediaplayer/qwasmmediaplayer_p.h b/src/plugins/multimedia/wasm/mediaplayer/qwasmmediaplayer_p.h
new file mode 100644
index 000000000..9269ecdb6
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediaplayer/qwasmmediaplayer_p.h
@@ -0,0 +1,124 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMMEDIAPLAYER_H
+#define QWASMMEDIAPLAYER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <private/qplatformmediaplayer_p.h>
+#include <qsize.h>
+#include <qurl.h>
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmAudioOutput;
+class QWasmVideoOutput;
+
+class QWasmMediaPlayer : public QObject, public QPlatformMediaPlayer
+{
+ Q_OBJECT
+
+public:
+ explicit QWasmMediaPlayer(QMediaPlayer *parent = 0);
+ ~QWasmMediaPlayer() override;
+
+ enum QWasmMediaPlayerState {
+ Error,
+ Idle,
+ Uninitialized,
+ Preparing,
+ Prepared,
+ Started,
+ Paused,
+ Stopped,
+ PlaybackCompleted
+ };
+ Q_ENUM(QWasmMediaPlayerState)
+
+ enum QWasmMediaNetworkState { NetworkEmpty = 0, NetworkIdle, NetworkLoading, NetworkNoSource };
+ Q_ENUM(QWasmMediaNetworkState)
+
+ qint64 duration() const override;
+ qint64 position() const override;
+ float bufferProgress() const override;
+ bool isAudioAvailable() const override;
+ bool isVideoAvailable() const override;
+ QMediaTimeRange availablePlaybackRanges() const override;
+ qreal playbackRate() const override;
+ void setPlaybackRate(qreal rate) override;
+ QUrl media() const override;
+ const QIODevice *mediaStream() const override;
+ void setMedia(const QUrl &mediaContent, QIODevice *stream) override;
+ void setVideoSink(QVideoSink *surface) override;
+ void setAudioOutput(QPlatformAudioOutput *output) override;
+ void setPosition(qint64 position) override;
+ void play() override;
+ void pause() override;
+ void stop() override;
+ bool isSeekable() const override;
+ int trackCount(TrackType trackType) override;
+
+ void updateAudioDevice();
+
+private Q_SLOTS:
+ void volumeChanged(float volume);
+ void mutedChanged(bool muted);
+ void videoOutputReady(bool ready);
+ void errorOccured(qint32 code, const QString &message);
+ void bufferingChanged(qint32 percent);
+ void videoSizeChanged(qint32 width, qint32 height);
+ void mediaStateChanged(QWasmMediaPlayer::QWasmMediaPlayerState state);
+ void setPositionChanged(qint64 position);
+ void setDurationChanged(qint64 duration);
+ void videoMetaDataChanged();
+
+ void onMediaStatusChanged(QMediaPlayer::MediaStatus status);
+
+private:
+ void setMediaStatus(QMediaPlayer::MediaStatus status);
+ void setAudioAvailable(bool available);
+ void setVideoAvailable(bool available);
+ void updateAvailablePlaybackRanges();
+ void resetBufferingProgress();
+
+ void setSubtitle(QString subtitle);
+ void disableTrack(TrackType trackType);
+ void initVideo();
+ void initAudio();
+
+ friend class StateChangeNotifier;
+
+ QPointer<QWasmVideoOutput> m_videoOutput;
+ QWasmAudioOutput *m_audioOutput = nullptr;
+
+ QUrl m_mediaContent;
+ QIODevice *m_mediaStream = nullptr;
+
+ QVideoSink *m_videoSink = nullptr;
+ int m_bufferPercent = -1;
+ bool m_audioAvailable = false;
+ bool m_videoAvailable = false;
+ QSize m_videoSize;
+ bool m_buffering = false;
+ QMediaTimeRange m_availablePlaybackRange;
+ int m_State;
+
+ bool m_playWhenReady = false;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMMEDIAPLAYER_H
diff --git a/src/plugins/multimedia/wasm/mediaplayer/qwasmvideosink.cpp b/src/plugins/multimedia/wasm/mediaplayer/qwasmvideosink.cpp
new file mode 100644
index 000000000..b6fe0e8e0
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediaplayer/qwasmvideosink.cpp
@@ -0,0 +1,26 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmvideosink_p.h"
+
+#include <QtGui/rhi/qrhi.h>
+
+QT_BEGIN_NAMESPACE
+
+QWasmVideoSink::QWasmVideoSink(QVideoSink *parent)
+ : QPlatformVideoSink(parent)
+{
+}
+
+void QWasmVideoSink::setRhi(QRhi *rhi)
+{
+ if (rhi && rhi->backend() != QRhi::OpenGLES2)
+ rhi = nullptr;
+ if (m_rhi == rhi)
+ return;
+ m_rhi = rhi;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwasmvideosink_p.cpp"
diff --git a/src/plugins/multimedia/wasm/mediaplayer/qwasmvideosink_p.h b/src/plugins/multimedia/wasm/mediaplayer/qwasmvideosink_p.h
new file mode 100644
index 000000000..5f2885249
--- /dev/null
+++ b/src/plugins/multimedia/wasm/mediaplayer/qwasmvideosink_p.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QWASMVIDEOSINK_H
+#define QWASMVIDEOSINK_H
+
+#include <private/qplatformvideosink_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QVideoSink;
+class QRhi;
+
+class QWasmVideoSink : public QPlatformVideoSink
+{
+ Q_OBJECT
+
+public:
+ explicit QWasmVideoSink(QVideoSink *parent = 0);
+
+ void setRhi(QRhi *) override;
+
+private:
+ QRhi *m_rhi = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMVIDEOSINK_H
diff --git a/src/plugins/multimedia/wasm/qwasmmediaintegration.cpp b/src/plugins/multimedia/wasm/qwasmmediaintegration.cpp
new file mode 100644
index 000000000..effc194a4
--- /dev/null
+++ b/src/plugins/multimedia/wasm/qwasmmediaintegration.cpp
@@ -0,0 +1,109 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmmediaintegration_p.h"
+#include <QLoggingCategory>
+
+#include <QCamera>
+#include <QCameraDevice>
+
+#include <private/qplatformmediaformatinfo_p.h>
+#include <private/qplatformmediaplugin_p.h>
+#include <private/qplatformmediadevices_p.h>
+#include <private/qplatformvideodevices_p.h>
+
+#include "mediaplayer/qwasmmediaplayer_p.h"
+#include "mediaplayer/qwasmvideosink_p.h"
+#include "qwasmaudioinput_p.h"
+#include "common/qwasmaudiooutput_p.h"
+
+#include "mediacapture/qwasmmediacapturesession_p.h"
+#include "mediacapture/qwasmmediarecorder_p.h"
+#include "mediacapture/qwasmcamera_p.h"
+#include "mediacapture/qwasmmediacapturesession_p.h"
+#include "mediacapture/qwasmimagecapture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+class QWasmMediaPlugin : public QPlatformMediaPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "wasm.json")
+
+public:
+ QWasmMediaPlugin()
+ : QPlatformMediaPlugin()
+ {}
+
+ QPlatformMediaIntegration *create(const QString &name) override
+ {
+ if (name == u"wasm")
+ return new QWasmMediaIntegration;
+ return nullptr;
+ }
+};
+
+QWasmMediaIntegration::QWasmMediaIntegration()
+ : QPlatformMediaIntegration(QLatin1String("wasm")) { }
+
+QMaybe<QPlatformMediaPlayer *> QWasmMediaIntegration::createPlayer(QMediaPlayer *player)
+{
+ return new QWasmMediaPlayer(player);
+}
+
+QMaybe<QPlatformVideoSink *> QWasmMediaIntegration::createVideoSink(QVideoSink *sink)
+{
+ return new QWasmVideoSink(sink);
+}
+
+QMaybe<QPlatformAudioInput *> QWasmMediaIntegration::createAudioInput(QAudioInput *audioInput)
+{
+ return new QWasmAudioInput(audioInput);
+}
+
+QMaybe<QPlatformAudioOutput *> QWasmMediaIntegration::createAudioOutput(QAudioOutput *q)
+{
+ return new QWasmAudioOutput(q);
+}
+
+QPlatformMediaFormatInfo *QWasmMediaIntegration::createFormatInfo()
+{
+ // TODO: create custom implementation
+ return new QPlatformMediaFormatInfo;
+}
+
+QPlatformVideoDevices *QWasmMediaIntegration::createVideoDevices()
+{
+ return new QWasmCameraDevices(this);
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QWasmMediaIntegration::createCaptureSession()
+{
+ return new QWasmMediaCaptureSession();
+}
+
+QMaybe<QPlatformMediaRecorder *> QWasmMediaIntegration::createRecorder(QMediaRecorder *recorder)
+{
+ return new QWasmMediaRecorder(recorder);
+}
+
+QMaybe<QPlatformCamera *> QWasmMediaIntegration::createCamera(QCamera *camera)
+{
+ return new QWasmCamera(camera);
+}
+
+QMaybe<QPlatformImageCapture *>
+QWasmMediaIntegration::createImageCapture(QImageCapture *imageCapture)
+{
+ return new QWasmImageCapture(imageCapture);
+}
+
+QList<QCameraDevice> QWasmMediaIntegration::videoInputs()
+{
+ return videoDevices()->videoDevices();
+}
+
+QT_END_NAMESPACE
+
+#include "qwasmmediaintegration.moc"
diff --git a/src/plugins/multimedia/wasm/qwasmmediaintegration_p.h b/src/plugins/multimedia/wasm/qwasmmediaintegration_p.h
new file mode 100644
index 000000000..d946c1854
--- /dev/null
+++ b/src/plugins/multimedia/wasm/qwasmmediaintegration_p.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMMEDIAINTEGRATION_H
+#define QWASMMEDIAINTEGRATION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformmediaintegration_p.h>
+
+#include <private/qwasmmediadevices_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmMediaDevices;
+
+class QWasmMediaIntegration : public QPlatformMediaIntegration
+{
+public:
+ QWasmMediaIntegration();
+
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *player) override;
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
+
+ QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *audioInput) override;
+ QMaybe<QPlatformAudioOutput *> createAudioOutput(QAudioOutput *q) override;
+
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
+ QMaybe<QPlatformCamera *> createCamera(QCamera *camera) override;
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *recorder) override;
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *imageCapture) override;
+ QList<QCameraDevice> videoInputs() override;
+
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
+ QPlatformVideoDevices *createVideoDevices() override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMMEDIAINTEGRATION_H
diff --git a/src/plugins/multimedia/wasm/wasm.json b/src/plugins/multimedia/wasm/wasm.json
new file mode 100644
index 000000000..02335aebe
--- /dev/null
+++ b/src/plugins/multimedia/wasm/wasm.json
@@ -0,0 +1,5 @@
+{
+ "Keys": [
+ "wasm"
+ ]
+}
diff --git a/src/plugins/multimedia/windows/CMakeLists.txt b/src/plugins/multimedia/windows/CMakeLists.txt
index 4443e69e4..963081e0a 100644
--- a/src/plugins/multimedia/windows/CMakeLists.txt
+++ b/src/plugins/multimedia/windows/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_internal_add_plugin(QWindowsMediaPlugin
OUTPUT_NAME windowsmediaplugin
PLUGIN_TYPE multimedia
@@ -14,9 +17,7 @@ qt_internal_add_plugin(QWindowsMediaPlugin
player/mfevrvideowindowcontrol.cpp player/mfevrvideowindowcontrol_p.h
player/mfplayercontrol.cpp player/mfplayercontrol_p.h
player/mfplayersession.cpp player/mfplayersession_p.h
- player/mftvideo.cpp player/mftvideo_p.h
player/mfvideorenderercontrol.cpp player/mfvideorenderercontrol_p.h
- player/samplegrabber.cpp player/samplegrabber_p.h
mediacapture/qwindowscamera.cpp
mediacapture/qwindowscamera_p.h
mediacapture/qwindowsimagecapture.cpp
diff --git a/src/plugins/multimedia/windows/common/mfmetadata.cpp b/src/plugins/multimedia/windows/common/mfmetadata.cpp
index 9f66dc64c..cc8c425e3 100644
--- a/src/plugins/multimedia/windows/common/mfmetadata.cpp
+++ b/src/plugins/multimedia/windows/common/mfmetadata.cpp
@@ -1,44 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qmediametadata.h>
#include <qdatetime.h>
+#include <qtimezone.h>
#include <qimage.h>
#include <quuid.h>
@@ -77,7 +42,7 @@ static QVariant convertValue(const PROPVARIANT& var)
value = QDateTime(QDate(t.wYear, t.wMonth, t.wDay),
QTime(t.wHour, t.wMinute, t.wSecond, t.wMilliseconds),
- Qt::UTC);
+ QTimeZone(QTimeZone::UTC));
break;
case VT_STREAM:
{
diff --git a/src/plugins/multimedia/windows/common/mfmetadata_p.h b/src/plugins/multimedia/windows/common/mfmetadata_p.h
index d1846e9c5..9ff196240 100644
--- a/src/plugins/multimedia/windows/common/mfmetadata_p.h
+++ b/src/plugins/multimedia/windows/common/mfmetadata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFMETADATACONTROL_H
#define MFMETADATACONTROL_H
@@ -52,7 +16,7 @@
//
#include <qmediametadata.h>
-#include "Mfidl.h"
+#include "mfidl.h"
QT_USE_NAMESPACE
diff --git a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
index 7cc162204..912ab5e94 100644
--- a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
+++ b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol.cpp
@@ -1,55 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <system_error>
#include <mferror.h>
#include <qglobal.h>
-#include "Wmcodecdsp.h"
+#include "wmcodecdsp.h"
#include "mfaudiodecodercontrol_p.h"
#include <private/qwindowsaudioutils_p.h>
+QT_BEGIN_NAMESPACE
+
MFAudioDecoderControl::MFAudioDecoderControl(QAudioDecoder *parent)
: QPlatformAudioDecoder(parent)
, m_sourceResolver(new SourceResolver)
{
- connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady()));
- connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleMediaSourceError(long)));
+ connect(m_sourceResolver, &SourceResolver::mediaSourceReady, this, &MFAudioDecoderControl::handleMediaSourceReady);
+ connect(m_sourceResolver, &SourceResolver::error, this, &MFAudioDecoderControl::handleMediaSourceError);
}
MFAudioDecoderControl::~MFAudioDecoderControl()
@@ -121,22 +87,22 @@ void MFAudioDecoderControl::startReadingSource(IMFMediaSource *source)
{
Q_ASSERT(source);
- m_decoderSourceReader.reset(new MFDecoderSourceReader());
+ m_decoderSourceReader = makeComObject<MFDecoderSourceReader>();
if (!m_decoderSourceReader) {
error(QAudioDecoder::ResourceError, tr("Could not instantiate MFDecoderSourceReader"));
return;
}
auto mediaType = m_decoderSourceReader->setSource(source, m_outputFormat.sampleFormat());
- QAudioFormat mediaFormat = QWindowsAudioUtils::mediaTypeToFormat(mediaType.get());
+ QAudioFormat mediaFormat = QWindowsAudioUtils::mediaTypeToFormat(mediaType.Get());
if (!mediaFormat.isValid()) {
error(QAudioDecoder::FormatError, tr("Invalid media format"));
- m_decoderSourceReader.reset();
+ m_decoderSourceReader.Reset();
return;
}
- QWindowsIUPointer<IMFPresentationDescriptor> pd;
- if (SUCCEEDED(source->CreatePresentationDescriptor(pd.address()))) {
+ ComPtr<IMFPresentationDescriptor> pd;
+ if (SUCCEEDED(source->CreatePresentationDescriptor(pd.GetAddressOf()))) {
UINT64 duration = 0;
pd->GetUINT64(MF_PD_DURATION, &duration);
duration /= 10000;
@@ -145,12 +111,12 @@ void MFAudioDecoderControl::startReadingSource(IMFMediaSource *source)
}
if (!m_resampler.setup(mediaFormat, m_outputFormat.isValid() ? m_outputFormat : mediaFormat)) {
- qWarning() << "Failed to setup resampler";
+ qWarning() << "Failed to set up resampler";
return;
}
- connect(m_decoderSourceReader.get(), SIGNAL(finished()), this, SLOT(handleSourceFinished()));
- connect(m_decoderSourceReader.get(), SIGNAL(newSample(QWindowsIUPointer<IMFSample>)), this, SLOT(handleNewSample(QWindowsIUPointer<IMFSample>)));
+ connect(m_decoderSourceReader.Get(), &MFDecoderSourceReader::finished, this, &MFAudioDecoderControl::handleSourceFinished);
+ connect(m_decoderSourceReader.Get(), &MFDecoderSourceReader::newSample, this, &MFAudioDecoderControl::handleNewSample);
setIsDecoding(true);
@@ -186,14 +152,14 @@ void MFAudioDecoderControl::stop()
if (!isDecoding())
return;
- disconnect(m_decoderSourceReader.get());
+ disconnect(m_decoderSourceReader.Get());
m_decoderSourceReader->clearSource();
- m_decoderSourceReader.reset();
+ m_decoderSourceReader.Reset();
if (bufferAvailable()) {
QAudioBuffer buffer;
m_audioBuffer.swap(buffer);
- emit bufferAvailableChanged(false);
+ bufferAvailableChanged(false);
}
setIsDecoding(false);
@@ -207,12 +173,12 @@ void MFAudioDecoderControl::stop()
}
}
-void MFAudioDecoderControl::handleNewSample(QWindowsIUPointer<IMFSample> sample)
+void MFAudioDecoderControl::handleNewSample(ComPtr<IMFSample> sample)
{
Q_ASSERT(sample);
qint64 sampleStartTimeUs = m_resampler.outputFormat().durationForBytes(m_resampler.totalOutputBytes());
- QByteArray out = m_resampler.resample(sample.get());
+ QByteArray out = m_resampler.resample(sample.Get());
if (out.isEmpty()) {
error(QAudioDecoder::Error::ResourceError, tr("Failed processing a sample"));
@@ -220,15 +186,15 @@ void MFAudioDecoderControl::handleNewSample(QWindowsIUPointer<IMFSample> sample)
} else {
m_audioBuffer = QAudioBuffer(out, m_resampler.outputFormat(), sampleStartTimeUs);
- emit bufferAvailableChanged(true);
- emit bufferReady();
+ bufferAvailableChanged(true);
+ bufferReady();
}
}
void MFAudioDecoderControl::handleSourceFinished()
{
stop();
- emit finished();
+ finished();
}
void MFAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
@@ -236,7 +202,7 @@ void MFAudioDecoderControl::setAudioFormat(const QAudioFormat &format)
if (m_outputFormat == format)
return;
m_outputFormat = format;
- emit formatChanged(m_outputFormat);
+ formatChanged(m_outputFormat);
}
QAudioBuffer MFAudioDecoderControl::read()
@@ -246,10 +212,14 @@ QAudioBuffer MFAudioDecoderControl::read()
if (bufferAvailable()) {
buffer.swap(m_audioBuffer);
m_position = buffer.startTime() / 1000;
- emit positionChanged(m_position);
- emit bufferAvailableChanged(false);
+ positionChanged(m_position);
+ bufferAvailableChanged(false);
m_decoderSourceReader->readNextSample();
}
return buffer;
}
+
+QT_END_NAMESPACE
+
+#include "moc_mfaudiodecodercontrol_p.cpp"
diff --git a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h
index 0b1b42198..9bb2371ec 100644
--- a/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h
+++ b/src/plugins/multimedia/windows/decoder/mfaudiodecodercontrol_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFAUDIODECODERCONTROL_H
#define MFAUDIODECODERCONTROL_H
@@ -54,10 +18,10 @@
#include "mfdecodersourcereader_p.h"
#include <private/qplatformaudiodecoder_p.h>
#include <sourceresolver_p.h>
-#include <private/qwindowsiupointer_p.h>
+#include <private/qcomptr_p.h>
#include <private/qwindowsresampler_p.h>
-QT_USE_NAMESPACE
+QT_BEGIN_NAMESPACE
class MFAudioDecoderControl : public QPlatformAudioDecoder
{
@@ -87,13 +51,13 @@ public:
private Q_SLOTS:
void handleMediaSourceReady();
void handleMediaSourceError(long hr);
- void handleNewSample(QWindowsIUPointer<IMFSample>);
+ void handleNewSample(ComPtr<IMFSample>);
void handleSourceFinished();
private:
void startReadingSource(IMFMediaSource *source);
- QWindowsIUPointer<MFDecoderSourceReader> m_decoderSourceReader;
+ ComPtr<MFDecoderSourceReader> m_decoderSourceReader;
SourceResolver *m_sourceResolver;
QWindowsResampler m_resampler;
QUrl m_source;
@@ -106,4 +70,6 @@ private:
bool m_deferredStart = false;
};
+QT_END_NAMESPACE
+
#endif//MFAUDIODECODERCONTROL_H
diff --git a/src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp
index e0b54dcde..097f83437 100644
--- a/src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp
+++ b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <system_error>
#include <mferror.h>
@@ -43,24 +7,26 @@
#include <qdebug.h>
#include "mfdecodersourcereader_p.h"
-QWindowsIUPointer<IMFMediaType> MFDecoderSourceReader::setSource(IMFMediaSource *source, QAudioFormat::SampleFormat sampleFormat)
+QT_BEGIN_NAMESPACE
+
+ComPtr<IMFMediaType> MFDecoderSourceReader::setSource(IMFMediaSource *source, QAudioFormat::SampleFormat sampleFormat)
{
- QWindowsIUPointer<IMFMediaType> mediaType;
- m_sourceReader.reset();
+ ComPtr<IMFMediaType> mediaType;
+ m_sourceReader.Reset();
if (!source)
return mediaType;
- QWindowsIUPointer<IMFAttributes> attr;
- MFCreateAttributes(attr.address(), 1);
+ ComPtr<IMFAttributes> attr;
+ MFCreateAttributes(attr.GetAddressOf(), 1);
if (FAILED(attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this)))
return mediaType;
if (FAILED(attr->SetUINT32(MF_SOURCE_READER_DISCONNECT_MEDIASOURCE_ON_SHUTDOWN, TRUE)))
return mediaType;
- HRESULT hr = MFCreateSourceReaderFromMediaSource(source, attr.get(), m_sourceReader.address());
+ HRESULT hr = MFCreateSourceReaderFromMediaSource(source, attr.Get(), m_sourceReader.GetAddressOf());
if (FAILED(hr)) {
- qWarning() << "MFDecoderSourceReader: failed to setup source reader: "
+ qWarning() << "MFDecoderSourceReader: failed to set up source reader: "
<< std::system_category().message(hr).c_str();
return mediaType;
}
@@ -68,12 +34,12 @@ QWindowsIUPointer<IMFMediaType> MFDecoderSourceReader::setSource(IMFMediaSource
m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_ALL_STREAMS), FALSE);
m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
- QWindowsIUPointer<IMFMediaType> pPartialType;
- MFCreateMediaType(pPartialType.address());
+ ComPtr<IMFMediaType> pPartialType;
+ MFCreateMediaType(pPartialType.GetAddressOf());
pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
pPartialType->SetGUID(MF_MT_SUBTYPE, sampleFormat == QAudioFormat::Float ? MFAudioFormat_Float : MFAudioFormat_PCM);
- m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), nullptr, pPartialType.get());
- m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), mediaType.address());
+ m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), nullptr, pPartialType.Get());
+ m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), mediaType.GetAddressOf());
// Ensure the stream is selected.
m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
@@ -125,10 +91,13 @@ STDMETHODIMP MFDecoderSourceReader::OnReadSample(HRESULT hrStatus, DWORD dwStrea
Q_UNUSED(dwStreamIndex);
Q_UNUSED(llTimestamp);
if (pSample) {
- pSample->AddRef();
- emit newSample(QWindowsIUPointer{pSample});
+ emit newSample(ComPtr<IMFSample>{pSample});
} else if ((dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) == MF_SOURCE_READERF_ENDOFSTREAM) {
emit finished();
}
return S_OK;
}
+
+QT_END_NAMESPACE
+
+#include "moc_mfdecodersourcereader_p.cpp"
diff --git a/src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h
index da35f466e..dee6f8bf5 100644
--- a/src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h
+++ b/src/plugins/multimedia/windows/decoder/mfdecodersourcereader_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFDECODERSOURCEREADER_H
#define MFDECODERSOURCEREADER_H
@@ -57,9 +21,9 @@
#include <QtCore/qobject.h>
#include "qaudioformat.h"
-#include <private/qwindowsiupointer_p.h>
+#include <private/qcomptr_p.h>
-QT_USE_NAMESPACE
+QT_BEGIN_NAMESPACE
class MFDecoderSourceReader : public QObject, public IMFSourceReaderCallback
{
@@ -68,8 +32,8 @@ public:
MFDecoderSourceReader() {}
~MFDecoderSourceReader() override {}
- void clearSource() { m_sourceReader.reset(); }
- QWindowsIUPointer<IMFMediaType> setSource(IMFMediaSource *source, QAudioFormat::SampleFormat);
+ void clearSource() { m_sourceReader.Reset(); }
+ ComPtr<IMFMediaType> setSource(IMFMediaSource *source, QAudioFormat::SampleFormat);
void readNextSample();
@@ -85,12 +49,15 @@ public:
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override { return S_OK; }
Q_SIGNALS:
- void newSample(QWindowsIUPointer<IMFSample>);
+ void newSample(ComPtr<IMFSample>);
void finished();
private:
long m_cRef = 1;
- QWindowsIUPointer<IMFSourceReader> m_sourceReader;
+ ComPtr<IMFSourceReader> m_sourceReader;
};
+
+QT_END_NAMESPACE
+
#endif//MFDECODERSOURCEREADER_H
diff --git a/src/plugins/multimedia/windows/evr/evrcustompresenter.cpp b/src/plugins/multimedia/windows/evr/evrcustompresenter.cpp
index e8b99a09f..2a3433f4d 100644
--- a/src/plugins/multimedia/windows/evr/evrcustompresenter.cpp
+++ b/src/plugins/multimedia/windows/evr/evrcustompresenter.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "evrcustompresenter_p.h"
@@ -45,7 +9,7 @@
#include <private/qplatformvideosink_p.h>
#include <private/qwindowsmfdefs_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtCore/qmutex.h>
#include <QtCore/qvarlengtharray.h>
@@ -53,7 +17,7 @@
#include <qthread.h>
#include <qcoreapplication.h>
#include <qmath.h>
-#include <QtCore/qdebug.h>
+#include <qloggingcategory.h>
#include <mutex>
@@ -62,14 +26,14 @@
QT_BEGIN_NAMESPACE
+static Q_LOGGING_CATEGORY(qLcEvrCustomPresenter, "qt.multimedia.evrcustompresenter")
+
const static MFRatio g_DefaultFrameRate = { 30, 1 };
static const DWORD SCHEDULER_TIMEOUT = 5000;
static const MFTIME ONE_SECOND = 10000000;
static const LONG ONE_MSEC = 1000;
// Function declarations.
-static HRESULT setDesiredSampleTime(IMFSample *sample, const LONGLONG& hnsSampleTime, const LONGLONG& hnsDuration);
-static HRESULT clearDesiredSampleTime(IMFSample *sample);
static HRESULT setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect& nrcSource);
static QVideoFrameFormat::PixelFormat pixelFormatFromMediaType(IMFMediaType *type);
@@ -97,45 +61,27 @@ bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter)
class PresentSampleEvent : public QEvent
{
public:
- PresentSampleEvent(IMFSample *sample)
- : QEvent(QEvent::Type(EVRCustomPresenter::PresentSample))
- , m_sample(sample)
- {
- if (m_sample)
- m_sample->AddRef();
- }
-
- ~PresentSampleEvent() override
+ explicit PresentSampleEvent(const ComPtr<IMFSample> &sample)
+ : QEvent(static_cast<Type>(EVRCustomPresenter::PresentSample)), m_sample(sample)
{
- if (m_sample)
- m_sample->Release();
}
- IMFSample *sample() const { return m_sample; }
+ ComPtr<IMFSample> sample() const { return m_sample; }
private:
- IMFSample *m_sample;
+ const ComPtr<IMFSample> m_sample;
};
Scheduler::Scheduler(EVRCustomPresenter *presenter)
: m_presenter(presenter)
- , m_clock(NULL)
, m_threadID(0)
- , m_schedulerThread(0)
- , m_threadReadyEvent(0)
- , m_flushEvent(0)
, m_playbackRate(1.0f)
- , m_perFrameInterval(0)
, m_perFrame_1_4th(0)
- , m_lastSampleTime(0)
{
}
Scheduler::~Scheduler()
{
- qt_evr_safe_release(&m_clock);
- for (int i = 0; i < m_scheduledSamples.size(); ++i)
- m_scheduledSamples[i]->Release();
m_scheduledSamples.clear();
}
@@ -146,13 +92,11 @@ void Scheduler::setFrameRate(const MFRatio& fps)
// Convert to a duration.
MFFrameRateToAverageTimePerFrame(fps.Numerator, fps.Denominator, &AvgTimePerFrame);
- m_perFrameInterval = (MFTIME)AvgTimePerFrame;
-
// Calculate 1/4th of this value, because we use it frequently.
- m_perFrame_1_4th = m_perFrameInterval / 4;
+ m_perFrame_1_4th = AvgTimePerFrame / 4;
}
-HRESULT Scheduler::startScheduler(IMFClock *clock)
+HRESULT Scheduler::startScheduler(ComPtr<IMFClock> clock)
{
if (m_schedulerThread)
return E_UNEXPECTED;
@@ -162,44 +106,39 @@ HRESULT Scheduler::startScheduler(IMFClock *clock)
HANDLE hObjects[2];
DWORD dwWait = 0;
- if (m_clock)
- m_clock->Release();
m_clock = clock;
- if (m_clock)
- m_clock->AddRef();
// Set a high the timer resolution (ie, short timer period).
timeBeginPeriod(1);
// Create an event to wait for the thread to start.
- m_threadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ m_threadReadyEvent = EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
if (!m_threadReadyEvent) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
// Create an event to wait for flush commands to complete.
- m_flushEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ m_flushEvent = EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
if (!m_flushEvent) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
// Create the scheduler thread.
- m_schedulerThread = CreateThread(NULL, 0, schedulerThreadProc, (LPVOID)this, 0, &dwID);
+ m_schedulerThread = ThreadHandle{ CreateThread(NULL, 0, schedulerThreadProc, (LPVOID)this, 0, &dwID) };
if (!m_schedulerThread) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
// Wait for the thread to signal the "thread ready" event.
- hObjects[0] = m_threadReadyEvent;
- hObjects[1] = m_schedulerThread;
+ hObjects[0] = m_threadReadyEvent.get();
+ hObjects[1] = m_schedulerThread.get();
dwWait = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); // Wait for EITHER of these handles.
if (WAIT_OBJECT_0 != dwWait) {
// The thread terminated early for some reason. This is an error condition.
- CloseHandle(m_schedulerThread);
- m_schedulerThread = NULL;
+ m_schedulerThread = {};
hr = E_UNEXPECTED;
goto done;
@@ -209,10 +148,8 @@ HRESULT Scheduler::startScheduler(IMFClock *clock)
done:
// Regardless success/failure, we are done using the "thread ready" event.
- if (m_threadReadyEvent) {
- CloseHandle(m_threadReadyEvent);
- m_threadReadyEvent = NULL;
- }
+ m_threadReadyEvent = {};
+
return hr;
}
@@ -225,19 +162,14 @@ HRESULT Scheduler::stopScheduler()
PostThreadMessage(m_threadID, Terminate, 0, 0);
// Wait for the thread to exit.
- WaitForSingleObject(m_schedulerThread, INFINITE);
+ WaitForSingleObject(m_schedulerThread.get(), INFINITE);
// Close handles.
- CloseHandle(m_schedulerThread);
- m_schedulerThread = NULL;
-
- CloseHandle(m_flushEvent);
- m_flushEvent = NULL;
+ m_schedulerThread = {};
+ m_flushEvent = {};
// Discard samples.
m_mutex.lock();
- for (int i = 0; i < m_scheduledSamples.size(); ++i)
- m_scheduledSamples[i]->Release();
m_scheduledSamples.clear();
m_mutex.unlock();
@@ -255,7 +187,7 @@ HRESULT Scheduler::flush()
// Wait for the scheduler thread to signal the flush event,
// OR for the thread to terminate.
- HANDLE objects[] = { m_flushEvent, m_schedulerThread };
+ HANDLE objects[] = { m_flushEvent.get(), m_schedulerThread.get() };
WaitForMultipleObjects(ARRAYSIZE(objects), objects, FALSE, SCHEDULER_TIMEOUT);
}
@@ -269,7 +201,7 @@ bool Scheduler::areSamplesScheduled()
return m_scheduledSamples.count() > 0;
}
-HRESULT Scheduler::scheduleSample(IMFSample *sample, bool presentNow)
+HRESULT Scheduler::scheduleSample(const ComPtr<IMFSample> &sample, bool presentNow)
{
if (!m_schedulerThread)
return MF_E_NOT_INITIALIZED;
@@ -277,16 +209,20 @@ HRESULT Scheduler::scheduleSample(IMFSample *sample, bool presentNow)
HRESULT hr = S_OK;
DWORD dwExitCode = 0;
- GetExitCodeThread(m_schedulerThread, &dwExitCode);
+ GetExitCodeThread(m_schedulerThread.get(), &dwExitCode);
if (dwExitCode != STILL_ACTIVE)
return E_FAIL;
if (presentNow || !m_clock) {
m_presenter->presentSample(sample);
} else {
+ if (m_playbackRate > 0.0f && qt_evr_isSampleTimePassed(m_clock.Get(), sample.Get())) {
+ qCDebug(qLcEvrCustomPresenter) << "Discard the sample, it came too late";
+ return hr;
+ }
+
// Queue the sample and ask the scheduler thread to wake up.
m_mutex.lock();
- sample->AddRef();
m_scheduledSamples.enqueue(sample);
m_mutex.unlock();
@@ -301,25 +237,37 @@ HRESULT Scheduler::processSamplesInQueue(LONG *nextSleep)
{
HRESULT hr = S_OK;
LONG wait = 0;
- IMFSample *sample = NULL;
+
+ QQueue<ComPtr<IMFSample>> scheduledSamples;
+
+ m_mutex.lock();
+ m_scheduledSamples.swap(scheduledSamples);
+ m_mutex.unlock();
// Process samples until the queue is empty or until the wait time > 0.
- while (!m_scheduledSamples.isEmpty()) {
- m_mutex.lock();
- sample = m_scheduledSamples.dequeue();
- m_mutex.unlock();
+ while (!scheduledSamples.isEmpty()) {
+ ComPtr<IMFSample> sample = scheduledSamples.dequeue();
// Process the next sample in the queue. If the sample is not ready
// for presentation. the value returned in wait is > 0, which
// means the scheduler should sleep for that amount of time.
+ if (isSampleReadyToPresent(sample.Get(), &wait)) {
+ m_presenter->presentSample(sample.Get());
+ continue;
+ }
- hr = processSample(sample, &wait);
- qt_evr_safe_release(&sample);
-
- if (FAILED(hr) || wait > 0)
+ if (wait > 0) {
+ // return the sample to scheduler
+ scheduledSamples.prepend(sample);
break;
+ }
}
+ m_mutex.lock();
+ scheduledSamples.append(std::move(m_scheduledSamples));
+ m_scheduledSamples.swap(scheduledSamples);
+ m_mutex.unlock();
+
// If the wait time is zero, it means we stopped because the queue is
// empty (or an error occurred). Set the wait time to infinite; this will
// make the scheduler thread sleep until it gets another thread message.
@@ -330,66 +278,50 @@ HRESULT Scheduler::processSamplesInQueue(LONG *nextSleep)
return hr;
}
-HRESULT Scheduler::processSample(IMFSample *sample, LONG *pNextSleep)
+bool Scheduler::isSampleReadyToPresent(IMFSample *sample, LONG *pNextSleep) const
{
- HRESULT hr = S_OK;
-
- LONGLONG hnsPresentationTime = 0;
- LONGLONG hnsTimeNow = 0;
- MFTIME hnsSystemTime = 0;
-
- bool presentNow = true;
- LONG nextSleep = 0;
-
- if (m_clock) {
- // Get the sample's time stamp. It is valid for a sample to
- // have no time stamp.
- hr = sample->GetSampleTime(&hnsPresentationTime);
-
- // Get the clock time. (But if the sample does not have a time stamp,
- // we don't need the clock time.)
- if (SUCCEEDED(hr))
- hr = m_clock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime);
-
- // Calculate the time until the sample's presentation time.
- // A negative value means the sample is late.
- LONGLONG hnsDelta = hnsPresentationTime - hnsTimeNow;
- if (m_playbackRate < 0) {
- // For reverse playback, the clock runs backward. Therefore, the
- // delta is reversed.
- hnsDelta = - hnsDelta;
- }
+ *pNextSleep = 0;
+ if (!m_clock)
+ return true;
- if (hnsDelta < - m_perFrame_1_4th) {
- // This sample is late.
- presentNow = true;
- } else if (hnsDelta > (3 * m_perFrame_1_4th)) {
- // This sample is still too early. Go to sleep.
- nextSleep = MFTimeToMsec(hnsDelta - (3 * m_perFrame_1_4th));
+ MFTIME hnsPresentationTime = 0;
+ MFTIME hnsTimeNow = 0;
+ MFTIME hnsSystemTime = 0;
- // Adjust the sleep time for the clock rate. (The presentation clock runs
- // at m_fRate, but sleeping uses the system clock.)
- if (m_playbackRate != 0)
- nextSleep = (LONG)(nextSleep / qFabs(m_playbackRate));
+ // Get the sample's time stamp. It is valid for a sample to
+ // have no time stamp.
+ HRESULT hr = sample->GetSampleTime(&hnsPresentationTime);
- // Don't present yet.
- presentNow = false;
- }
+ // Get the clock time. (But if the sample does not have a time stamp,
+ // we don't need the clock time.)
+ if (SUCCEEDED(hr))
+ hr = m_clock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime);
+
+ // Calculate the time until the sample's presentation time.
+ // A negative value means the sample is late.
+ MFTIME hnsDelta = hnsPresentationTime - hnsTimeNow;
+ if (m_playbackRate < 0) {
+ // For reverse playback, the clock runs backward. Therefore, the
+ // delta is reversed.
+ hnsDelta = - hnsDelta;
}
- if (presentNow) {
- m_presenter->presentSample(sample);
+ if (hnsDelta < - m_perFrame_1_4th) {
+ // This sample is late - skip.
+ return false;
+ } else if (hnsDelta > (3 * m_perFrame_1_4th)) {
+ // This sample came too early - reschedule
+ *pNextSleep = MFTimeToMsec(hnsDelta - (3 * m_perFrame_1_4th));
+
+ // Adjust the sleep time for the clock rate. (The presentation clock runs
+ // at m_fRate, but sleeping uses the system clock.)
+ if (m_playbackRate != 0)
+ *pNextSleep = (LONG)(*pNextSleep / qFabs(m_playbackRate));
+ return *pNextSleep == 0;
} else {
- // The sample is not ready yet. Return it to the queue.
- m_mutex.lock();
- sample->AddRef();
- m_scheduledSamples.prepend(sample);
- m_mutex.unlock();
+ // This sample can be presented right now
+ return true;
}
-
- *pNextSleep = nextSleep;
-
- return hr;
}
DWORD WINAPI Scheduler::schedulerThreadProc(LPVOID parameter)
@@ -412,7 +344,7 @@ DWORD Scheduler::schedulerThreadProcPrivate()
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
// Signal to the scheduler that the thread is ready.
- SetEvent(m_threadReadyEvent);
+ SetEvent(m_threadReadyEvent.get());
while (!exitThread) {
// Wait for a thread message OR until the wait time expires.
@@ -435,12 +367,10 @@ DWORD Scheduler::schedulerThreadProcPrivate()
case Flush:
// Flushing: Clear the sample queue and set the event.
m_mutex.lock();
- for (int i = 0; i < m_scheduledSamples.size(); ++i)
- m_scheduledSamples[i]->Release();
m_scheduledSamples.clear();
m_mutex.unlock();
wait = INFINITE;
- SetEvent(m_flushEvent);
+ SetEvent(m_flushEvent.get());
break;
case Schedule:
// Process as many samples as we can.
@@ -470,47 +400,44 @@ SamplePool::~SamplePool()
clear();
}
-HRESULT SamplePool::getSample(IMFSample **sample)
+ComPtr<IMFSample> SamplePool::takeSample()
{
QMutexLocker locker(&m_mutex);
- if (!m_initialized)
- return MF_E_NOT_INITIALIZED;
+ Q_ASSERT(m_initialized);
+ if (!m_initialized) {
+ qCWarning(qLcEvrCustomPresenter) << "SamplePool is not initialized yet";
+ return nullptr;
+ }
- if (m_videoSampleQueue.isEmpty())
- return MF_E_SAMPLEALLOCATOR_EMPTY;
+ if (m_videoSampleQueue.isEmpty()) {
+ qCDebug(qLcEvrCustomPresenter) << "SamplePool is empty";
+ return nullptr;
+ }
// Get a sample from the allocated queue.
// It doesn't matter if we pull them from the head or tail of the list,
// but when we get it back, we want to re-insert it onto the opposite end.
- // (see ReturnSample)
-
- IMFSample *taken = m_videoSampleQueue.takeFirst();
+ // (see returnSample)
- // Give the sample to the caller.
- *sample = taken;
- (*sample)->AddRef();
-
- taken->Release();
-
- return S_OK;
+ return m_videoSampleQueue.takeFirst();
}
-HRESULT SamplePool::returnSample(IMFSample *sample)
+void SamplePool::returnSample(const ComPtr<IMFSample> &sample)
{
QMutexLocker locker(&m_mutex);
- if (!m_initialized)
- return MF_E_NOT_INITIALIZED;
+ Q_ASSERT(m_initialized);
+ if (!m_initialized) {
+ qCWarning(qLcEvrCustomPresenter) << "SamplePool is not initialized yet";
+ return;
+ }
m_videoSampleQueue.append(sample);
- sample->AddRef();
-
- return S_OK;
}
-HRESULT SamplePool::initialize(QList<IMFSample*> &samples)
+HRESULT SamplePool::initialize(QList<ComPtr<IMFSample>> &&samples)
{
QMutexLocker locker(&m_mutex);
@@ -518,16 +445,10 @@ HRESULT SamplePool::initialize(QList<IMFSample*> &samples)
return MF_E_INVALIDREQUEST;
// Move these samples into our allocated queue.
- for (auto sample : qAsConst(samples)) {
- sample->AddRef();
- m_videoSampleQueue.append(sample);
- }
+ m_videoSampleQueue.append(std::move(samples));
m_initialized = true;
- for (auto sample : qAsConst(samples))
- sample->Release();
- samples.clear();
return S_OK;
}
@@ -535,8 +456,6 @@ HRESULT SamplePool::clear()
{
QMutexLocker locker(&m_mutex);
- for (auto sample : qAsConst(m_videoSampleQueue))
- sample->Release();
m_videoSampleQueue.clear();
m_initialized = false;
@@ -552,14 +471,10 @@ EVRCustomPresenter::EVRCustomPresenter(QVideoSink *sink)
, m_scheduler(this)
, m_tokenCounter(0)
, m_sampleNotify(false)
- , m_repaint(false)
, m_prerolled(false)
, m_endStreaming(false)
, m_playbackRate(1.0f)
, m_presentEngine(new D3DPresentEngine(sink))
- , m_clock(0)
- , m_mixer(0)
- , m_mediaEventSink(0)
, m_mediaType(0)
, m_videoSink(0)
, m_canRenderToSurface(false)
@@ -580,11 +495,6 @@ EVRCustomPresenter::~EVRCustomPresenter()
m_scheduler.stopScheduler();
m_samplePool.clear();
- qt_evr_safe_release(&m_clock);
- qt_evr_safe_release(&m_mixer);
- qt_evr_safe_release(&m_mediaEventSink);
- qt_evr_safe_release(&m_mediaType);
-
delete m_presentEngine;
}
@@ -623,7 +533,7 @@ ULONG EVRCustomPresenter::Release()
{
ULONG uCount = InterlockedDecrement(&m_refCount);
if (uCount == 0)
- delete this;
+ deleteLater();
return uCount;
}
@@ -671,9 +581,9 @@ HRESULT EVRCustomPresenter::InitServicePointers(IMFTopologyServiceLookup *lookup
if (isActive())
return MF_E_INVALIDREQUEST;
- qt_evr_safe_release(&m_clock);
- qt_evr_safe_release(&m_mixer);
- qt_evr_safe_release(&m_mediaEventSink);
+ m_clock.Reset();
+ m_mixer.Reset();
+ m_mediaEventSink.Reset();
// Ask for the clock. Optional, because the EVR might not have a clock.
objectCount = 1;
@@ -695,7 +605,7 @@ HRESULT EVRCustomPresenter::InitServicePointers(IMFTopologyServiceLookup *lookup
return hr;
// Make sure that we can work with this mixer.
- hr = configureMixer(m_mixer);
+ hr = configureMixer(m_mixer.Get());
if (FAILED(hr))
return hr;
@@ -729,9 +639,9 @@ HRESULT EVRCustomPresenter::ReleaseServicePointers()
setMediaType(NULL);
// Release all services that were acquired from InitServicePointers.
- qt_evr_safe_release(&m_clock);
- qt_evr_safe_release(&m_mixer);
- qt_evr_safe_release(&m_mediaEventSink);
+ m_clock.Reset();
+ m_mixer.Reset();
+ m_mediaEventSink.Reset();
return S_OK;
}
@@ -932,8 +842,6 @@ HRESULT EVRCustomPresenter::OnClockSetRate(MFTIME, float rate)
// frame-step operation.
if ((m_playbackRate == 0.0f) && (rate != 0.0f)) {
cancelFrameStep();
- for (auto sample : qAsConst(m_frameStep.samples))
- sample->Release();
m_frameStep.samples.clear();
}
@@ -1052,6 +960,13 @@ void EVRCustomPresenter::setSink(QVideoSink *sink)
supportedFormatsChanged();
}
+void EVRCustomPresenter::setCropRect(QRect cropRect)
+{
+ m_mutex.lock();
+ m_cropRect = cropRect;
+ m_mutex.unlock();
+}
+
HRESULT EVRCustomPresenter::configureMixer(IMFTransform *mixer)
{
// Set the zoom rectangle (ie, the source clipping rectangle).
@@ -1129,13 +1044,11 @@ HRESULT EVRCustomPresenter::flush()
m_scheduler.flush();
// Flush the frame-step queue.
- for (auto sample : qAsConst(m_frameStep.samples))
- sample->Release();
m_frameStep.samples.clear();
if (m_renderState == RenderStopped && m_videoSink) {
// Repaint with black.
- presentSample(NULL);
+ presentSample(nullptr);
}
return S_OK;
@@ -1223,9 +1136,6 @@ HRESULT EVRCustomPresenter::prepareFrameStep(DWORD steps)
HRESULT EVRCustomPresenter::startFrameStep()
{
- HRESULT hr = S_OK;
- IMFSample *sample = NULL;
-
if (m_frameStep.state == FrameStepWaitingStart) {
// We have a frame-step request, and are waiting for the clock to start.
// Set the state to "pending," which means we are waiting for samples.
@@ -1233,13 +1143,11 @@ HRESULT EVRCustomPresenter::startFrameStep()
// If the frame-step queue already has samples, process them now.
while (!m_frameStep.samples.isEmpty() && (m_frameStep.state == FrameStepPending)) {
- sample = m_frameStep.samples.takeFirst();
+ const ComPtr<IMFSample> sample = m_frameStep.samples.takeFirst();
- hr = deliverFrameStepSample(sample);
+ const HRESULT hr = deliverFrameStepSample(sample.Get());
if (FAILED(hr))
- goto done;
-
- qt_evr_safe_release(&sample);
+ return hr;
// We break from this loop when:
// (a) the frame-step queue is empty, or
@@ -1249,22 +1157,18 @@ HRESULT EVRCustomPresenter::startFrameStep()
// We are not frame stepping. Therefore, if the frame-step queue has samples,
// we need to process them normally.
while (!m_frameStep.samples.isEmpty()) {
- sample = m_frameStep.samples.takeFirst();
+ const ComPtr<IMFSample> sample = m_frameStep.samples.takeFirst();
- hr = deliverSample(sample, false);
+ const HRESULT hr = deliverSample(sample.Get());
if (FAILED(hr))
- goto done;
-
- qt_evr_safe_release(&sample);
+ return hr;
}
}
-done:
- qt_evr_safe_release(&sample);
- return hr;
+ return S_OK;
}
-HRESULT EVRCustomPresenter::completeFrameStep(IMFSample *sample)
+HRESULT EVRCustomPresenter::completeFrameStep(const ComPtr<IMFSample> &sample)
{
HRESULT hr = S_OK;
MFTIME sampleTime = 0;
@@ -1342,13 +1246,30 @@ HRESULT EVRCustomPresenter::createOptimalVideoType(IMFMediaType *proposedType, I
hr = proposedType->GetUINT64(MF_MT_FRAME_SIZE, &size);
width = int(HI32(size));
height = int(LO32(size));
- rcOutput.left = 0;
- rcOutput.top = 0;
- rcOutput.right = width;
- rcOutput.bottom = height;
+
+ if (m_cropRect.isValid()) {
+ rcOutput.left = m_cropRect.x();
+ rcOutput.top = m_cropRect.y();
+ rcOutput.right = m_cropRect.x() + m_cropRect.width();
+ rcOutput.bottom = m_cropRect.y() + m_cropRect.height();
+
+ m_sourceRect.left = float(m_cropRect.x()) / width;
+ m_sourceRect.top = float(m_cropRect.y()) / height;
+ m_sourceRect.right = float(m_cropRect.x() + m_cropRect.width()) / width;
+ m_sourceRect.bottom = float(m_cropRect.y() + m_cropRect.height()) / height;
+
+ if (m_mixer)
+ configureMixer(m_mixer.Get());
+ } else {
+ rcOutput.left = 0;
+ rcOutput.top = 0;
+ rcOutput.right = width;
+ rcOutput.bottom = height;
+ }
// Set the geometric aperture, and disable pan/scan.
- displayArea = qt_evr_makeMFArea(0, 0, rcOutput.right, rcOutput.bottom);
+ displayArea = qt_evr_makeMFArea(0, 0, rcOutput.right - rcOutput.left,
+ rcOutput.bottom - rcOutput.top);
hr = mtOptimal->SetUINT32(MF_MT_PAN_SCAN_ENABLED, FALSE);
if (FAILED(hr))
@@ -1389,13 +1310,13 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
// Clearing the media type is allowed in any state (including shutdown).
if (!mediaType) {
stopSurface();
- qt_evr_safe_release(&m_mediaType);
+ m_mediaType.Reset();
releaseResources();
return S_OK;
}
MFRatio fps = { 0, 0 };
- QList<IMFSample*> sampleQueue;
+ QList<ComPtr<IMFSample>> sampleQueue;
// Cannot set the media type after shutdown.
HRESULT hr = checkShutdown();
@@ -1404,30 +1325,30 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
// Check if the new type is actually different.
// Note: This function safely handles NULL input parameters.
- if (qt_evr_areMediaTypesEqual(m_mediaType, mediaType))
+ if (qt_evr_areMediaTypesEqual(m_mediaType.Get(), mediaType))
goto done; // Nothing more to do.
// We're really changing the type. First get rid of the old type.
- qt_evr_safe_release(&m_mediaType);
+ m_mediaType.Reset();
releaseResources();
// Initialize the presenter engine with the new media type.
// The presenter engine allocates the samples.
- hr = m_presentEngine->createVideoSamples(mediaType, sampleQueue);
+ hr = m_presentEngine->createVideoSamples(mediaType, sampleQueue, m_cropRect.size());
if (FAILED(hr))
goto done;
// Mark each sample with our token counter. If this batch of samples becomes
// invalid, we increment the counter, so that we know they should be discarded.
- for (auto sample : qAsConst(sampleQueue)) {
+ for (auto sample : std::as_const(sampleQueue)) {
hr = sample->SetUINT32(MFSamplePresenter_SampleCounter, m_tokenCounter);
if (FAILED(hr))
goto done;
}
// Add the samples to the sample pool.
- hr = m_samplePool.initialize(sampleQueue);
+ hr = m_samplePool.initialize(std::move(sampleQueue));
if (FAILED(hr))
goto done;
@@ -1544,21 +1465,9 @@ void EVRCustomPresenter::processOutputLoop()
HRESULT EVRCustomPresenter::processOutput()
{
- HRESULT hr = S_OK;
- DWORD status = 0;
- LONGLONG mixerStartTime = 0, mixerEndTime = 0;
- MFTIME systemTime = 0;
- BOOL repaint = m_repaint; // Temporarily store this state flag.
-
- MFT_OUTPUT_DATA_BUFFER dataBuffer;
- ZeroMemory(&dataBuffer, sizeof(dataBuffer));
-
- IMFSample *sample = NULL;
-
// If the clock is not running, we present the first sample,
// and then don't present any more until the clock starts.
-
- if ((m_renderState != RenderStarted) && !m_repaint && m_prerolled)
+ if ((m_renderState != RenderStarted) && m_prerolled)
return S_FALSE;
// Make sure we have a pointer to the mixer.
@@ -1566,45 +1475,33 @@ HRESULT EVRCustomPresenter::processOutput()
return MF_E_INVALIDREQUEST;
// Try to get a free sample from the video sample pool.
- hr = m_samplePool.getSample(&sample);
- if (hr == MF_E_SAMPLEALLOCATOR_EMPTY) // No free samples. Try again when a sample is released.
- return S_FALSE;
- if (FAILED(hr))
- return hr;
+ ComPtr<IMFSample> sample = m_samplePool.takeSample();
+ if (!sample)
+ return S_FALSE; // No free samples. Try again when a sample is released.
// From now on, we have a valid video sample pointer, where the mixer will
// write the video data.
- if (m_repaint) {
- // Repaint request. Ask the mixer for the most recent sample.
- setDesiredSampleTime(sample, m_scheduler.lastSampleTime(), m_scheduler.frameDuration());
-
- m_repaint = false; // OK to clear this flag now.
- } else {
- // Not a repaint request. Clear the desired sample time; the mixer will
- // give us the next frame in the stream.
- clearDesiredSampleTime(sample);
+ LONGLONG mixerStartTime = 0, mixerEndTime = 0;
+ MFTIME systemTime = 0;
- if (m_clock) {
- // Latency: Record the starting time for ProcessOutput.
- m_clock->GetCorrelatedTime(0, &mixerStartTime, &systemTime);
- }
+ if (m_clock) {
+ // Latency: Record the starting time for ProcessOutput.
+ m_clock->GetCorrelatedTime(0, &mixerStartTime, &systemTime);
}
// Now we are ready to get an output sample from the mixer.
- dataBuffer.dwStreamID = 0;
- dataBuffer.pSample = sample;
- dataBuffer.dwStatus = 0;
-
- hr = m_mixer->ProcessOutput(0, 1, &dataBuffer, &status);
+ DWORD status = 0;
+ MFT_OUTPUT_DATA_BUFFER dataBuffer = {};
+ dataBuffer.pSample = sample.Get();
+ HRESULT hr = m_mixer->ProcessOutput(0, 1, &dataBuffer, &status);
+ // Important: Release any events returned from the ProcessOutput method.
+ qt_evr_safe_release(&dataBuffer.pEvents);
if (FAILED(hr)) {
// Return the sample to the pool.
- HRESULT hr2 = m_samplePool.returnSample(sample);
- if (FAILED(hr2)) {
- hr = hr2;
- goto done;
- }
+ m_samplePool.returnSample(sample);
+
// Handle some known error codes from ProcessOutput.
if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) {
// The mixer's format is not set. Negotiate a new format.
@@ -1617,54 +1514,46 @@ HRESULT EVRCustomPresenter::processOutput()
// We have to wait for the mixer to get more input.
m_sampleNotify = false;
}
- } else {
- // We got an output sample from the mixer.
- if (m_clock && !repaint) {
- // Latency: Record the ending time for the ProcessOutput operation,
- // and notify the EVR of the latency.
+ return hr;
+ }
- m_clock->GetCorrelatedTime(0, &mixerEndTime, &systemTime);
+ // We got an output sample from the mixer.
+ if (m_clock) {
+ // Latency: Record the ending time for the ProcessOutput operation,
+ // and notify the EVR of the latency.
- LONGLONG latencyTime = mixerEndTime - mixerStartTime;
- notifyEvent(EC_PROCESSING_LATENCY, reinterpret_cast<LONG_PTR>(&latencyTime), 0);
- }
+ m_clock->GetCorrelatedTime(0, &mixerEndTime, &systemTime);
- // Set up notification for when the sample is released.
- hr = trackSample(sample);
- if (FAILED(hr))
- goto done;
+ LONGLONG latencyTime = mixerEndTime - mixerStartTime;
+ notifyEvent(EC_PROCESSING_LATENCY, reinterpret_cast<LONG_PTR>(&latencyTime), 0);
+ }
- // Schedule the sample.
- if ((m_frameStep.state == FrameStepNone) || repaint) {
- hr = deliverSample(sample, repaint);
- if (FAILED(hr))
- goto done;
- } else {
- // We are frame-stepping (and this is not a repaint request).
- hr = deliverFrameStepSample(sample);
- if (FAILED(hr))
- goto done;
- }
+ // Set up notification for when the sample is released.
+ hr = trackSample(sample);
+ if (FAILED(hr))
+ return hr;
- m_prerolled = true; // We have presented at least one sample now.
- }
+ // Schedule the sample.
+ if (m_frameStep.state == FrameStepNone)
+ hr = deliverSample(sample);
+ else // We are frame-stepping
+ hr = deliverFrameStepSample(sample);
-done:
- qt_evr_safe_release(&sample);
+ if (FAILED(hr))
+ return hr;
- // Important: Release any events returned from the ProcessOutput method.
- qt_evr_safe_release(&dataBuffer.pEvents);
- return hr;
+ m_prerolled = true; // We have presented at least one sample now.
+ return S_OK;
}
-HRESULT EVRCustomPresenter::deliverSample(IMFSample *sample, bool repaint)
+HRESULT EVRCustomPresenter::deliverSample(const ComPtr<IMFSample> &sample)
{
- // If we are not actively playing, OR we are scrubbing (rate = 0) OR this is a
- // repaint request, then we need to present the sample immediately. Otherwise,
+ // If we are not actively playing, OR we are scrubbing (rate = 0),
+ // then we need to present the sample immediately. Otherwise,
// schedule it normally.
- bool presentNow = ((m_renderState != RenderStarted) || isScrubbing() || repaint);
+ bool presentNow = ((m_renderState != RenderStarted) || isScrubbing());
HRESULT hr = m_scheduler.scheduleSample(sample, presentNow);
@@ -1678,19 +1567,18 @@ HRESULT EVRCustomPresenter::deliverSample(IMFSample *sample, bool repaint)
return hr;
}
-HRESULT EVRCustomPresenter::deliverFrameStepSample(IMFSample *sample)
+HRESULT EVRCustomPresenter::deliverFrameStepSample(const ComPtr<IMFSample> &sample)
{
HRESULT hr = S_OK;
IUnknown *unk = NULL;
// For rate 0, discard any sample that ends earlier than the clock time.
- if (isScrubbing() && m_clock && qt_evr_isSampleTimePassed(m_clock, sample)) {
+ if (isScrubbing() && m_clock && qt_evr_isSampleTimePassed(m_clock.Get(), sample.Get())) {
// Discard this sample.
} else if (m_frameStep.state >= FrameStepScheduled) {
// A frame was already submitted. Put this sample on the frame-step queue,
// in case we are asked to step to the next frame. If frame-stepping is
// cancelled, this sample will be processed normally.
- sample->AddRef();
m_frameStep.samples.append(sample);
} else {
// We're ready to frame-step.
@@ -1705,11 +1593,10 @@ HRESULT EVRCustomPresenter::deliverFrameStepSample(IMFSample *sample)
// This is the right frame, but the clock hasn't started yet. Put the
// sample on the frame-step queue. When the clock starts, the sample
// will be processed.
- sample->AddRef();
m_frameStep.samples.append(sample);
} else {
// This is the right frame *and* the clock has started. Deliver this sample.
- hr = deliverSample(sample, false);
+ hr = deliverSample(sample);
if (FAILED(hr))
goto done;
@@ -1734,7 +1621,7 @@ done:
return hr;
}
-HRESULT EVRCustomPresenter::trackSample(IMFSample *sample)
+HRESULT EVRCustomPresenter::trackSample(const ComPtr<IMFSample> &sample)
{
IMFTrackedSample *tracked = NULL;
@@ -1811,11 +1698,9 @@ HRESULT EVRCustomPresenter::onSampleFree(IMFAsyncResult *result)
if (token == m_tokenCounter) {
// Return the sample to the sample pool.
- hr = m_samplePool.returnSample(sample);
- if (SUCCEEDED(hr)) {
- // A free sample is available. Process more data if possible.
- processOutputLoop();
- }
+ m_samplePool.returnSample(sample);
+ // A free sample is available. Process more data if possible.
+ processOutputLoop();
}
m_mutex.unlock();
@@ -1843,7 +1728,7 @@ float EVRCustomPresenter::getMaxRate(bool thin)
UINT monitorRateHz = 0;
if (!thin && m_mediaType) {
- qt_evr_getFrameRate(m_mediaType, &fps);
+ qt_evr_getFrameRate(m_mediaType.Get(), &fps);
monitorRateHz = m_presentEngine->refreshRate();
if (fps.Denominator && fps.Numerator && monitorRateHz) {
@@ -1889,7 +1774,7 @@ void EVRCustomPresenter::stopSurface()
}
}
-void EVRCustomPresenter::presentSample(IMFSample *sample)
+void EVRCustomPresenter::presentSample(const ComPtr<IMFSample> &sample)
{
if (thread() != QThread::currentThread()) {
QCoreApplication::postEvent(this, new PresentSampleEvent(sample));
@@ -1910,15 +1795,15 @@ void EVRCustomPresenter::presentSample(IMFSample *sample)
frame.setEndTime(frame.endTime() + m_positionOffset);
}
- QWindowsIUPointer<IMFMediaType> inputStreamType;
- if (SUCCEEDED(m_mixer->GetInputCurrentType(0, inputStreamType.address()))) {
- auto rotation = static_cast<MFVideoRotationFormat>(MFGetAttributeUINT32(inputStreamType.get(), MF_MT_VIDEO_ROTATION, 0));
+ ComPtr<IMFMediaType> inputStreamType;
+ if (SUCCEEDED(m_mixer->GetInputCurrentType(0, inputStreamType.GetAddressOf()))) {
+ auto rotation = static_cast<MFVideoRotationFormat>(MFGetAttributeUINT32(inputStreamType.Get(), MF_MT_VIDEO_ROTATION, 0));
switch (rotation) {
- case MFVideoRotationFormat_0: frame.setRotationAngle(QVideoFrame::Rotation0); break;
- case MFVideoRotationFormat_90: frame.setRotationAngle(QVideoFrame::Rotation90); break;
- case MFVideoRotationFormat_180: frame.setRotationAngle(QVideoFrame::Rotation180); break;
- case MFVideoRotationFormat_270: frame.setRotationAngle(QVideoFrame::Rotation270); break;
- default: frame.setRotationAngle(QVideoFrame::Rotation0);
+ case MFVideoRotationFormat_0: frame.setRotation(QtVideo::Rotation::None); break;
+ case MFVideoRotationFormat_90: frame.setRotation(QtVideo::Rotation::Clockwise90); break;
+ case MFVideoRotationFormat_180: frame.setRotation(QtVideo::Rotation::Clockwise180); break;
+ case MFVideoRotationFormat_270: frame.setRotation(QtVideo::Rotation::Clockwise270); break;
+ default: frame.setRotation(QtVideo::Rotation::None);
}
}
@@ -1930,55 +1815,6 @@ void EVRCustomPresenter::positionChanged(qint64 position)
m_positionOffset = position * 1000;
}
-HRESULT setDesiredSampleTime(IMFSample *sample, const LONGLONG &sampleTime, const LONGLONG &duration)
-{
- if (!sample)
- return E_POINTER;
-
- HRESULT hr = S_OK;
- IMFDesiredSample *desired = NULL;
-
- hr = sample->QueryInterface(IID_PPV_ARGS(&desired));
- if (SUCCEEDED(hr))
- desired->SetDesiredSampleTimeAndDuration(sampleTime, duration);
-
- qt_evr_safe_release(&desired);
- return hr;
-}
-
-HRESULT clearDesiredSampleTime(IMFSample *sample)
-{
- if (!sample)
- return E_POINTER;
-
- HRESULT hr = S_OK;
-
- IMFDesiredSample *desired = NULL;
- IUnknown *unkSwapChain = NULL;
-
- // We store some custom attributes on the sample, so we need to cache them
- // and reset them.
- //
- // This works around the fact that IMFDesiredSample::Clear() removes all of the
- // attributes from the sample.
-
- UINT32 counter = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1);
-
- hr = sample->QueryInterface(IID_PPV_ARGS(&desired));
- if (SUCCEEDED(hr)) {
- desired->Clear();
-
- hr = sample->SetUINT32(MFSamplePresenter_SampleCounter, counter);
- if (FAILED(hr))
- goto done;
- }
-
-done:
- qt_evr_safe_release(&unkSwapChain);
- qt_evr_safe_release(&desired);
- return hr;
-}
-
HRESULT setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect &sourceRect)
{
if (!mixer)
diff --git a/src/plugins/multimedia/windows/evr/evrcustompresenter_p.h b/src/plugins/multimedia/windows/evr/evrcustompresenter_p.h
index 1bf443efa..28f1cbc68 100644
--- a/src/plugins/multimedia/windows/evr/evrcustompresenter_p.h
+++ b/src/plugins/multimedia/windows/evr/evrcustompresenter_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef EVRCUSTOMPRESENTER_H
#define EVRCUSTOMPRESENTER_H
@@ -55,8 +19,12 @@
#include <qmutex.h>
#include <qqueue.h>
#include <qevent.h>
+#include <qrect.h>
#include <qvideoframeformat.h>
#include <qvideosink.h>
+#include <qpointer.h>
+#include <private/qcomptr_p.h>
+#include "evrhelpers_p.h"
#include <d3d9.h>
#include <dxva2api.h>
@@ -142,15 +110,11 @@ public:
void setFrameRate(const MFRatio &fps);
void setClockRate(float rate) { m_playbackRate = rate; }
- const LONGLONG &lastSampleTime() const { return m_lastSampleTime; }
- const LONGLONG &frameDuration() const { return m_perFrameInterval; }
-
- HRESULT startScheduler(IMFClock *clock);
+ HRESULT startScheduler(ComPtr<IMFClock> clock);
HRESULT stopScheduler();
- HRESULT scheduleSample(IMFSample *sample, bool presentNow);
+ HRESULT scheduleSample(const ComPtr<IMFSample> &sample, bool presentNow);
HRESULT processSamplesInQueue(LONG *nextSleep);
- HRESULT processSample(IMFSample *sample, LONG *nextSleep);
HRESULT flush();
bool areSamplesScheduled();
@@ -160,22 +124,21 @@ public:
private:
DWORD schedulerThreadProcPrivate();
+ bool isSampleReadyToPresent(IMFSample *sample, LONG *pNextSleep) const;
EVRCustomPresenter *m_presenter;
- QQueue<IMFSample*> m_scheduledSamples; // Samples waiting to be presented.
+ QQueue<ComPtr<IMFSample>> m_scheduledSamples; // Samples waiting to be presented.
- IMFClock *m_clock; // Presentation clock. Can be NULL.
+ ComPtr<IMFClock> m_clock; // Presentation clock. Can be NULL.
DWORD m_threadID;
- HANDLE m_schedulerThread;
- HANDLE m_threadReadyEvent;
- HANDLE m_flushEvent;
+ ThreadHandle m_schedulerThread;
+ EventHandle m_threadReadyEvent;
+ EventHandle m_flushEvent;
float m_playbackRate;
- MFTIME m_perFrameInterval; // Duration of each frame.
- LONGLONG m_perFrame_1_4th; // 1/4th of the frame duration.
- MFTIME m_lastSampleTime; // Most recent sample time.
+ MFTIME m_perFrame_1_4th; // 1/4th of the frame duration.
QMutex m_mutex;
};
@@ -187,15 +150,15 @@ public:
SamplePool();
~SamplePool();
- HRESULT initialize(QList<IMFSample*> &samples);
+ HRESULT initialize(QList<ComPtr<IMFSample>> &&samples);
HRESULT clear();
- HRESULT getSample(IMFSample **sample);
- HRESULT returnSample(IMFSample *sample);
+ ComPtr<IMFSample> takeSample();
+ void returnSample(const ComPtr<IMFSample> &sample);
private:
QMutex m_mutex;
- QList<IMFSample*> m_videoSampleQueue;
+ QList<ComPtr<IMFSample>> m_videoSampleQueue;
bool m_initialized;
};
@@ -273,10 +236,11 @@ public:
void supportedFormatsChanged();
void setSink(QVideoSink *sink);
+ void setCropRect(QRect cropRect);
void startSurface();
void stopSurface();
- void presentSample(IMFSample *sample);
+ void presentSample(const ComPtr<IMFSample> &sample);
bool event(QEvent *) override;
@@ -329,15 +293,15 @@ private:
// Managing samples
void processOutputLoop();
HRESULT processOutput();
- HRESULT deliverSample(IMFSample *sample, bool repaint);
- HRESULT trackSample(IMFSample *sample);
+ HRESULT deliverSample(const ComPtr<IMFSample> &sample);
+ HRESULT trackSample(const ComPtr<IMFSample> &sample);
void releaseResources();
// Frame-stepping
HRESULT prepareFrameStep(DWORD steps);
HRESULT startFrameStep();
- HRESULT deliverFrameStepSample(IMFSample *sample);
- HRESULT completeFrameStep(IMFSample *sample);
+ HRESULT deliverFrameStepSample(const ComPtr<IMFSample> &sample);
+ HRESULT completeFrameStep(const ComPtr<IMFSample> &sample);
HRESULT cancelFrameStep();
// Callback when a video sample is released.
@@ -348,7 +312,7 @@ private:
struct FrameStep
{
FrameStepState state = FrameStepNone;
- QList<IMFSample*> samples;
+ QList<ComPtr<IMFSample>> samples;
DWORD steps = 0;
DWORD_PTR sampleNoRef = 0;
};
@@ -367,7 +331,6 @@ private:
// Rendering state
bool m_sampleNotify; // Did the mixer signal it has an input sample?
- bool m_repaint; // Do we need to repaint the last sample?
bool m_prerolled; // Have we presented at least one sample?
bool m_endStreaming; // Did we reach the end of the stream (EOS)?
@@ -376,14 +339,15 @@ private:
D3DPresentEngine *m_presentEngine; // Rendering engine. (Never null if the constructor succeeds.)
- IMFClock *m_clock; // The EVR's clock.
- IMFTransform *m_mixer; // The EVR's mixer.
- IMediaEventSink *m_mediaEventSink; // The EVR's event-sink interface.
- IMFMediaType *m_mediaType; // Output media type
+ ComPtr<IMFClock> m_clock; // The EVR's clock.
+ ComPtr<IMFTransform> m_mixer; // The EVR's mixer.
+ ComPtr<IMediaEventSink> m_mediaEventSink; // The EVR's event-sink interface.
+ ComPtr<IMFMediaType> m_mediaType; // Output media type
- QVideoSink *m_videoSink;
+ QPointer<QVideoSink> m_videoSink;
bool m_canRenderToSurface;
qint64 m_positionOffset; // Seek position in microseconds.
+ QRect m_cropRect; // Video crop rectangle
};
bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter);
diff --git a/src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp b/src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp
index 96c7836cf..cc14cd419 100644
--- a/src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp
+++ b/src/plugins/multimedia/windows/evr/evrd3dpresentengine.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "evrd3dpresentengine_p.h"
@@ -50,8 +14,7 @@
#include <d3d11_1.h>
-#include <private/qrhi_p.h>
-#include <private/qrhid3d11_p.h>
+#include <rhi/qrhi.h>
#if QT_CONFIG(opengl)
# include <qopenglcontext.h>
@@ -61,19 +24,18 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcEvrD3DPresentEngine, "qt.multimedia.evrd3dpresentengine")
+static Q_LOGGING_CATEGORY(qLcEvrD3DPresentEngine, "qt.multimedia.evrd3dpresentengine")
class IMFSampleVideoBuffer: public QAbstractVideoBuffer
{
public:
- IMFSampleVideoBuffer(QWindowsIUPointer<IDirect3DDevice9Ex> device,
- IMFSample *sample, QRhi *rhi, QVideoFrame::HandleType type = QVideoFrame::NoHandle)
+ IMFSampleVideoBuffer(ComPtr<IDirect3DDevice9Ex> device,
+ const ComPtr<IMFSample> &sample, QRhi *rhi, QVideoFrame::HandleType type = QVideoFrame::NoHandle)
: QAbstractVideoBuffer(type, rhi)
, m_device(device)
+ , m_sample(sample)
, m_mapMode(QVideoFrame::NotMapped)
{
- sample->AddRef();
- m_sample.reset(sample);
}
~IMFSampleVideoBuffer() override
@@ -95,24 +57,24 @@ public:
return {};
} else {
- QWindowsIUPointer<IMFMediaBuffer> buffer;
- HRESULT hr = m_sample->GetBufferByIndex(0, buffer.address());
+ ComPtr<IMFMediaBuffer> buffer;
+ HRESULT hr = m_sample->GetBufferByIndex(0, buffer.GetAddressOf());
if (FAILED(hr))
return {};
- QWindowsIUPointer<IDirect3DSurface9> surface;
- hr = MFGetService(buffer.get(), MR_BUFFER_SERVICE, IID_IDirect3DSurface9, (void **)(surface.address()));
+ ComPtr<IDirect3DSurface9> surface;
+ hr = MFGetService(buffer.Get(), MR_BUFFER_SERVICE, IID_IDirect3DSurface9, (void **)(surface.GetAddressOf()));
if (FAILED(hr))
return {};
if (FAILED(surface->GetDesc(&desc)))
return {};
- if (FAILED(m_device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, m_memSurface.address(), nullptr)))
+ if (FAILED(m_device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, m_memSurface.GetAddressOf(), nullptr)))
return {};
- if (FAILED(m_device->GetRenderTargetData(surface.get(), m_memSurface.get()))) {
- m_memSurface.reset();
+ if (FAILED(m_device->GetRenderTargetData(surface.Get(), m_memSurface.Get()))) {
+ m_memSurface.Reset();
return {};
}
}
@@ -142,76 +104,114 @@ public:
}
protected:
- QWindowsIUPointer<IDirect3DDevice9Ex> m_device;
- QWindowsIUPointer<IMFSample> m_sample;
+ ComPtr<IDirect3DDevice9Ex> m_device;
+ ComPtr<IMFSample> m_sample;
private:
- QWindowsIUPointer<IDirect3DSurface9> m_memSurface;
+ ComPtr<IDirect3DSurface9> m_memSurface;
QVideoFrame::MapMode m_mapMode;
};
+class QVideoFrameD3D11Textures: public QVideoFrameTextures
+{
+public:
+ QVideoFrameD3D11Textures(std::unique_ptr<QRhiTexture> &&tex, ComPtr<ID3D11Texture2D> &&d3d11tex)
+ : m_tex(std::move(tex))
+ , m_d3d11tex(std::move(d3d11tex))
+ {}
+
+ QRhiTexture *texture(uint plane) const override
+ {
+ return plane == 0 ? m_tex.get() : nullptr;
+ };
+
+private:
+ std::unique_ptr<QRhiTexture> m_tex;
+ ComPtr<ID3D11Texture2D> m_d3d11tex;
+};
+
class D3D11TextureVideoBuffer: public IMFSampleVideoBuffer
{
public:
- D3D11TextureVideoBuffer(QWindowsIUPointer<IDirect3DDevice9Ex> device, IMFSample *sample,
- QWindowsIUPointer<ID3D11Texture2D> d2d11tex, QRhi *rhi)
- : IMFSampleVideoBuffer(device, sample, rhi, QVideoFrame::RhiTextureHandle)
- , m_d2d11tex(d2d11tex)
+ D3D11TextureVideoBuffer(ComPtr<IDirect3DDevice9Ex> device, const ComPtr<IMFSample> &sample,
+ HANDLE sharedHandle, QRhi *rhi)
+ : IMFSampleVideoBuffer(std::move(device), sample, rhi, QVideoFrame::RhiTextureHandle)
+ , m_sharedHandle(sharedHandle)
{}
- std::unique_ptr<QRhiTexture> texture(int plane) const override
+ std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *rhi) override
{
- if (!m_rhi || !m_d2d11tex || plane > 0)
+ if (!rhi || rhi->backend() != QRhi::D3D11)
return {};
- D3D11_TEXTURE2D_DESC desc = {};
- m_d2d11tex->GetDesc(&desc);
- QRhiTexture::Format format;
- if (desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM)
- format = QRhiTexture::BGRA8;
- else if (desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM)
- format = QRhiTexture::RGBA8;
- else
+
+ auto nh = static_cast<const QRhiD3D11NativeHandles*>(rhi->nativeHandles());
+ if (!nh)
+ return {};
+
+ auto dev = reinterpret_cast<ID3D11Device *>(nh->dev);
+ if (!dev)
return {};
- std::unique_ptr<QRhiTexture> tex(m_rhi->newTexture(format, QSize{int(desc.Width), int(desc.Height)}, 1, {}));
- tex->createFrom({quint64(m_d2d11tex.get()), 0});
- return tex;
+ ComPtr<ID3D11Texture2D> d3d11tex;
+ HRESULT hr = dev->OpenSharedResource(m_sharedHandle, __uuidof(ID3D11Texture2D), (void**)(d3d11tex.GetAddressOf()));
+ if (SUCCEEDED(hr)) {
+ D3D11_TEXTURE2D_DESC desc = {};
+ d3d11tex->GetDesc(&desc);
+ QRhiTexture::Format format;
+ if (desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM)
+ format = QRhiTexture::BGRA8;
+ else if (desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM)
+ format = QRhiTexture::RGBA8;
+ else
+ return {};
+
+ std::unique_ptr<QRhiTexture> tex(rhi->newTexture(format, QSize{int(desc.Width), int(desc.Height)}, 1, {}));
+ tex->createFrom({quint64(d3d11tex.Get()), 0});
+ return std::make_unique<QVideoFrameD3D11Textures>(std::move(tex), std::move(d3d11tex));
+
+ } else {
+ qCDebug(qLcEvrD3DPresentEngine) << "Failed to obtain D3D11Texture2D from D3D9Texture2D handle";
+ }
+ return {};
}
private:
- QWindowsIUPointer<ID3D11Texture2D> m_d2d11tex;
+ HANDLE m_sharedHandle = nullptr;
};
#if QT_CONFIG(opengl)
-class OpenGlVideoBuffer: public IMFSampleVideoBuffer
+class QVideoFrameOpenGlTextures : public QVideoFrameTextures
{
+ struct InterOpHandles {
+ GLuint textureName = 0;
+ HANDLE device = nullptr;
+ HANDLE texture = nullptr;
+ };
+
public:
- OpenGlVideoBuffer(QWindowsIUPointer<IDirect3DDevice9Ex> device, IMFSample *sample,
- const WglNvDxInterop &wglNvDxInterop, HANDLE sharedHandle, QRhi *rhi)
- : IMFSampleVideoBuffer(device, sample, rhi, QVideoFrame::RhiTextureHandle)
- , m_sharedHandle(sharedHandle)
- , m_wgl(wglNvDxInterop)
- {}
+ Q_DISABLE_COPY(QVideoFrameOpenGlTextures);
- ~OpenGlVideoBuffer() override
- {
- if (!m_d3dglHandle)
- return;
+ QVideoFrameOpenGlTextures(std::unique_ptr<QRhiTexture> &&tex, const WglNvDxInterop &wgl, InterOpHandles &handles)
+ : m_tex(std::move(tex))
+ , m_wgl(wgl)
+ , m_handles(handles)
+ {}
+ ~QVideoFrameOpenGlTextures() override {
if (QOpenGLContext::currentContext()) {
- if (m_glHandle) {
- if (!m_wgl.wglDXUnlockObjectsNV(m_d3dglHandle, 1, &m_glHandle))
- qCDebug(qLcEvrD3DPresentEngine) << "Failed to unlock OpenGL texture";
- if (!m_wgl.wglDXUnregisterObjectNV(m_d3dglHandle, m_glHandle))
- qCDebug(qLcEvrD3DPresentEngine) << "Failed to unregister OpenGL texture";
-
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- if (funcs)
- funcs->glDeleteTextures(1, &m_glTextureName);
- else
- qCDebug(qLcEvrD3DPresentEngine) << "Could not delete texture, OpenGL context functions missing";
- }
- if (!m_wgl.wglDXCloseDeviceNV(m_d3dglHandle))
+ if (!m_wgl.wglDXUnlockObjectsNV(m_handles.device, 1, &m_handles.texture))
+ qCDebug(qLcEvrD3DPresentEngine) << "Failed to unlock OpenGL texture";
+
+ if (!m_wgl.wglDXUnregisterObjectNV(m_handles.device, m_handles.texture))
+ qCDebug(qLcEvrD3DPresentEngine) << "Failed to unregister OpenGL texture";
+
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ if (funcs)
+ funcs->glDeleteTextures(1, &m_handles.textureName);
+ else
+ qCDebug(qLcEvrD3DPresentEngine) << "Could not delete texture, OpenGL context functions missing";
+
+ if (!m_wgl.wglDXCloseDeviceNV(m_handles.device))
qCDebug(qLcEvrD3DPresentEngine) << "Failed to close D3D-GL device";
} else {
@@ -219,87 +219,106 @@ public:
}
}
- void mapTextures() override
+ static std::unique_ptr<QVideoFrameOpenGlTextures> create(const WglNvDxInterop &wgl, QRhi *rhi,
+ IDirect3DDevice9Ex *device, IDirect3DTexture9 *texture,
+ HANDLE sharedHandle)
{
- if (!QOpenGLContext::currentContext())
- return;
-
- if (m_d3dglHandle)
- return;
-
- QWindowsIUPointer<IMFMediaBuffer> buffer;
- HRESULT hr = m_sample->GetBufferByIndex(0, buffer.address());
- if (FAILED(hr))
- return;
-
- QWindowsIUPointer<IDirect3DSurface9> surface;
- hr = MFGetService(buffer.get(), MR_BUFFER_SERVICE, IID_IDirect3DSurface9, (void **)(surface.address()));
- if (FAILED(hr))
- return;
+ if (!rhi || rhi->backend() != QRhi::OpenGLES2)
+ return {};
- hr = surface->GetContainer(IID_IDirect3DTexture9, (void **)m_texture.address());
- if (FAILED(hr))
- return;
+ if (!QOpenGLContext::currentContext())
+ return {};
- m_d3dglHandle = m_wgl.wglDXOpenDeviceNV(m_device.get());
- if (!m_d3dglHandle) {
- m_texture.reset();
+ InterOpHandles handles = {};
+ handles.device = wgl.wglDXOpenDeviceNV(device);
+ if (!handles.device) {
qCDebug(qLcEvrD3DPresentEngine) << "Failed to open D3D device";
- return;
+ return {};
}
- m_wgl.wglDXSetResourceShareHandleNV(m_texture.get(), m_sharedHandle);
+ wgl.wglDXSetResourceShareHandleNV(texture, sharedHandle);
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
if (funcs) {
- funcs->glGenTextures(1, &m_glTextureName);
- m_glHandle = m_wgl.wglDXRegisterObjectNV(m_d3dglHandle, m_texture.get(), m_glTextureName,
- GL_TEXTURE_2D, WglNvDxInterop::WGL_ACCESS_READ_ONLY_NV);
- if (m_glHandle) {
- if (m_wgl.wglDXLockObjectsNV(m_d3dglHandle, 1, &m_glHandle))
- return;
+ funcs->glGenTextures(1, &handles.textureName);
+ handles.texture = wgl.wglDXRegisterObjectNV(handles.device, texture, handles.textureName,
+ GL_TEXTURE_2D, WglNvDxInterop::WGL_ACCESS_READ_ONLY_NV);
+ if (handles.texture) {
+ if (wgl.wglDXLockObjectsNV(handles.device, 1, &handles.texture)) {
+ D3DSURFACE_DESC desc;
+ texture->GetLevelDesc(0, &desc);
+ QRhiTexture::Format format;
+ if (desc.Format == D3DFMT_A8R8G8B8)
+ format = QRhiTexture::BGRA8;
+ else if (desc.Format == D3DFMT_A8B8G8R8)
+ format = QRhiTexture::RGBA8;
+ else
+ return {};
+
+ std::unique_ptr<QRhiTexture> tex(rhi->newTexture(format, QSize{int(desc.Width), int(desc.Height)}, 1, {}));
+ tex->createFrom({quint64(handles.textureName), 0});
+ return std::make_unique<QVideoFrameOpenGlTextures>(std::move(tex), wgl, handles);
+ }
qCDebug(qLcEvrD3DPresentEngine) << "Failed to lock OpenGL texture";
- m_wgl.wglDXUnregisterObjectNV(m_d3dglHandle, m_glHandle);
- m_glHandle = nullptr;
+ wgl.wglDXUnregisterObjectNV(handles.device, handles.texture);
} else {
qCDebug(qLcEvrD3DPresentEngine) << "Could not register D3D9 texture in OpenGL";
}
- funcs->glDeleteTextures(1, &m_glTextureName);
- m_glTextureName = 0;
+ funcs->glDeleteTextures(1, &handles.textureName);
} else {
qCDebug(qLcEvrD3DPresentEngine) << "Failed generate texture names, OpenGL context functions missing";
}
+ return {};
}
- std::unique_ptr<QRhiTexture> texture(int plane) const override
+ QRhiTexture *texture(uint plane) const override
{
- if (!m_rhi || !m_texture || plane > 0)
- return {};
+ return plane == 0 ? m_tex.get() : nullptr;
+ };
+private:
+ std::unique_ptr<QRhiTexture> m_tex;
+ WglNvDxInterop m_wgl;
+ InterOpHandles m_handles;
+};
- D3DSURFACE_DESC desc;
- m_texture->GetLevelDesc(0, &desc);
- QRhiTexture::Format format;
- if (desc.Format == D3DFMT_A8R8G8B8)
- format = QRhiTexture::BGRA8;
- else if (desc.Format == D3DFMT_A8B8G8R8)
- format = QRhiTexture::RGBA8;
- else
- return {};
+class OpenGlVideoBuffer: public IMFSampleVideoBuffer
+{
+public:
+ OpenGlVideoBuffer(ComPtr<IDirect3DDevice9Ex> device, const ComPtr<IMFSample> &sample,
+ const WglNvDxInterop &wglNvDxInterop, HANDLE sharedHandle, QRhi *rhi)
+ : IMFSampleVideoBuffer(std::move(device), sample, rhi, QVideoFrame::RhiTextureHandle)
+ , m_sharedHandle(sharedHandle)
+ , m_wgl(wglNvDxInterop)
+ {}
+
+ std::unique_ptr<QVideoFrameTextures> mapTextures(QRhi *rhi) override
+ {
+ if (!m_texture) {
+ ComPtr<IMFMediaBuffer> buffer;
+ HRESULT hr = m_sample->GetBufferByIndex(0, buffer.GetAddressOf());
+ if (FAILED(hr))
+ return {};
+
+ ComPtr<IDirect3DSurface9> surface;
+ hr = MFGetService(buffer.Get(), MR_BUFFER_SERVICE, IID_IDirect3DSurface9,
+ (void **)(surface.GetAddressOf()));
+ if (FAILED(hr))
+ return {};
- std::unique_ptr<QRhiTexture> tex(m_rhi->newTexture(format, QSize{int(desc.Width), int(desc.Height)}, 1, {}));
- tex->createFrom({quint64(m_glTextureName), 0});
- return tex;
+ hr = surface->GetContainer(IID_IDirect3DTexture9, (void **)m_texture.GetAddressOf());
+ if (FAILED(hr))
+ return {};
+ }
+
+ return QVideoFrameOpenGlTextures::create(m_wgl, rhi, m_device.Get(), m_texture.Get(), m_sharedHandle);
}
private:
- GLuint m_glTextureName = 0;
- HANDLE m_d3dglHandle = nullptr;
- HANDLE m_glHandle = nullptr;
HANDLE m_sharedHandle = nullptr;
WglNvDxInterop m_wgl;
- QWindowsIUPointer<IDirect3DTexture9> m_texture;
+ ComPtr<IDirect3DTexture9> m_texture;
};
#endif
@@ -323,9 +342,9 @@ void D3DPresentEngine::setSink(QVideoSink *sink)
m_sink = sink;
releaseResources();
- m_device.reset();
- m_devices.reset();
- m_D3D9.reset();
+ m_device.Reset();
+ m_devices.Reset();
+ m_D3D9.Reset();
if (!m_sink)
return;
@@ -343,10 +362,10 @@ void D3DPresentEngine::setSink(QVideoSink *sink)
HRESULT D3DPresentEngine::initializeD3D()
{
- HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, m_D3D9.address());
+ HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, m_D3D9.GetAddressOf());
if (SUCCEEDED(hr))
- hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, m_devices.address());
+ hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, m_devices.GetAddressOf());
return hr;
}
@@ -391,8 +410,16 @@ static bool readWglNvDxInteropProc(WglNvDxInterop &f)
return false;
}
- auto dc = ::GetDC(::GetShellWindow());
- if (!strstr(wglGetExtensionsStringARB(dc), "WGL_NV_DX_interop")) {
+ HWND hwnd = ::GetShellWindow();
+ auto dc = ::GetDC(hwnd);
+
+ const char *wglExtString = wglGetExtensionsStringARB(dc);
+ if (!wglExtString)
+ qCDebug(qLcEvrD3DPresentEngine) << "WGL extensions missing (wglGetExtensionsStringARB returned null)";
+
+ bool hasExtension = wglExtString && strstr(wglExtString, "WGL_NV_DX_interop");
+ ReleaseDC(hwnd, dc);
+ if (!hasExtension) {
qCDebug(qLcEvrD3DPresentEngine) << "WGL_NV_DX_interop missing";
return false;
}
@@ -407,6 +434,20 @@ static bool readWglNvDxInteropProc(WglNvDxInterop &f)
}
#endif
+namespace {
+
+bool hwTextureRenderingEnabled() {
+ // add possibility for an user to opt-out HW video rendering
+ // using the same env. variable as for FFmpeg backend
+ static bool isDisableConversionSet = false;
+ static const int disableHwConversion = qEnvironmentVariableIntValue(
+ "QT_DISABLE_HW_TEXTURES_CONVERSION", &isDisableConversionSet);
+
+ return !isDisableConversionSet || !disableHwConversion;
+}
+
+}
+
HRESULT D3DPresentEngine::createD3DDevice()
{
if (!m_D3D9 || !m_devices)
@@ -414,23 +455,26 @@ HRESULT D3DPresentEngine::createD3DDevice()
m_useTextureRendering = false;
UINT adapterID = 0;
- QRhi *rhi = m_sink ? m_sink->rhi() : nullptr;
- if (rhi) {
- if (rhi->backend() == QRhi::D3D11) {
- m_useTextureRendering = findD3D11AdapterID(*rhi, m_D3D9.get(), adapterID);
+
+ if (hwTextureRenderingEnabled()) {
+ QRhi *rhi = m_sink ? m_sink->rhi() : nullptr;
+ if (rhi) {
+ if (rhi->backend() == QRhi::D3D11) {
+ m_useTextureRendering = findD3D11AdapterID(*rhi, m_D3D9.Get(), adapterID);
#if QT_CONFIG(opengl)
- } else if (rhi->backend() == QRhi::OpenGLES2) {
- m_useTextureRendering = readWglNvDxInteropProc(m_wglNvDxInterop);
+ } else if (rhi->backend() == QRhi::OpenGLES2) {
+ m_useTextureRendering = readWglNvDxInteropProc(m_wglNvDxInterop);
#endif
+ } else {
+ qCDebug(qLcEvrD3DPresentEngine) << "Not supported RHI backend type";
+ }
} else {
- qCDebug(qLcEvrD3DPresentEngine) << "Not supported RHI backend type";
+ qCDebug(qLcEvrD3DPresentEngine) << "No RHI associated with this sink";
}
- } else {
- qCDebug(qLcEvrD3DPresentEngine) << "No RHI associated with this sink";
- }
- if (!m_useTextureRendering)
- qCDebug(qLcEvrD3DPresentEngine) << "Could not find compatible RHI adapter, zero copy disabled";
+ if (!m_useTextureRendering)
+ qCDebug(qLcEvrD3DPresentEngine) << "Could not find compatible RHI adapter, zero copy disabled";
+ }
D3DCAPS9 ddCaps;
ZeroMemory(&ddCaps, sizeof(ddCaps));
@@ -458,7 +502,7 @@ HRESULT D3DPresentEngine::createD3DDevice()
pp.Flags = D3DPRESENTFLAG_VIDEO;
pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
- QWindowsIUPointer<IDirect3DDevice9Ex> device;
+ ComPtr<IDirect3DDevice9Ex> device;
hr = m_D3D9->CreateDeviceEx(
adapterID,
@@ -467,7 +511,7 @@ HRESULT D3DPresentEngine::createD3DDevice()
vp | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&pp,
NULL,
- device.address()
+ device.GetAddressOf()
);
if (FAILED(hr))
return hr;
@@ -476,7 +520,7 @@ HRESULT D3DPresentEngine::createD3DDevice()
if (FAILED(hr))
return hr;
- hr = m_devices->ResetDevice(device.get(), m_deviceResetToken);
+ hr = m_devices->ResetDevice(device.Get(), m_deviceResetToken);
if (FAILED(hr))
return hr;
@@ -486,7 +530,7 @@ HRESULT D3DPresentEngine::createD3DDevice()
bool D3DPresentEngine::isValid() const
{
- return m_device.get() != nullptr;
+ return m_device.Get() != nullptr;
}
void D3DPresentEngine::releaseResources()
@@ -502,7 +546,7 @@ HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid, void** ppv)
if (!m_devices) {
hr = MF_E_UNSUPPORTED_SERVICE;
} else {
- *ppv = m_devices.get();
+ *ppv = m_devices.Get();
m_devices->AddRef();
}
} else {
@@ -548,7 +592,9 @@ HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format)
return ok ? S_OK : D3DERR_NOTAVAILABLE;
}
-HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSample*> &videoSampleQueue)
+HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format,
+ QList<ComPtr<IMFSample>> &videoSampleQueue,
+ QSize frameSize)
{
if (!format || !m_device)
return MF_E_UNEXPECTED;
@@ -561,6 +607,11 @@ HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSamp
if (FAILED(hr))
return hr;
+ if (frameSize.isValid() && !frameSize.isEmpty()) {
+ width = frameSize.width();
+ height = frameSize.height();
+ }
+
DWORD d3dFormat = 0;
hr = qt_evr_getFourCC(format, &d3dFormat);
if (FAILED(hr))
@@ -575,24 +626,24 @@ HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSamp
for (int i = 0; i < PRESENTER_BUFFER_COUNT; i++) {
// texture ref cnt is increased by GetSurfaceLevel()/MFCreateVideoSampleFromSurface()
// below, so it will be destroyed only when the sample pool is released.
- QWindowsIUPointer<IDirect3DTexture9> texture;
+ ComPtr<IDirect3DTexture9> texture;
HANDLE sharedHandle = nullptr;
- hr = m_device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, (D3DFORMAT)d3dFormat, D3DPOOL_DEFAULT, texture.address(), &sharedHandle);
+ hr = m_device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, (D3DFORMAT)d3dFormat, D3DPOOL_DEFAULT, texture.GetAddressOf(), &sharedHandle);
if (FAILED(hr))
break;
- QWindowsIUPointer<IDirect3DSurface9> surface;
- hr = texture->GetSurfaceLevel(0, surface.address());
+ ComPtr<IDirect3DSurface9> surface;
+ hr = texture->GetSurfaceLevel(0, surface.GetAddressOf());
if (FAILED(hr))
break;
- QWindowsIUPointer<IMFSample> videoSample;
- hr = MFCreateVideoSampleFromSurface(surface.get(), videoSample.address());
+ ComPtr<IMFSample> videoSample;
+ hr = MFCreateVideoSampleFromSurface(surface.Get(), videoSample.GetAddressOf());
if (FAILED(hr))
break;
- m_sampleTextureHandle[i] = {videoSample.get(), sharedHandle};
- videoSampleQueue.append(videoSample.release());
+ m_sampleTextureHandle[i] = {videoSample.Get(), sharedHandle};
+ videoSampleQueue.append(videoSample);
}
if (SUCCEEDED(hr)) {
@@ -604,32 +655,21 @@ HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSamp
return hr;
}
-QVideoFrame D3DPresentEngine::makeVideoFrame(IMFSample *sample)
+QVideoFrame D3DPresentEngine::makeVideoFrame(const ComPtr<IMFSample> &sample)
{
if (!sample)
return {};
HANDLE sharedHandle = nullptr;
for (const auto &p : m_sampleTextureHandle)
- if (p.first == sample)
+ if (p.first == sample.Get())
sharedHandle = p.second;
QAbstractVideoBuffer *vb = nullptr;
QRhi *rhi = m_sink ? m_sink->rhi() : nullptr;
if (m_useTextureRendering && sharedHandle && rhi) {
if (rhi->backend() == QRhi::D3D11) {
- auto nh = static_cast<const QRhiD3D11NativeHandles*>(rhi->nativeHandles());
- if (nh) {
- auto dev = reinterpret_cast<ID3D11Device *>(nh->dev);
- if (dev) {
- QWindowsIUPointer<ID3D11Texture2D> d3d11tex;
- HRESULT hr = dev->OpenSharedResource(sharedHandle, __uuidof(ID3D11Texture2D), (void**)(d3d11tex.address()));
- if (SUCCEEDED(hr))
- vb = new D3D11TextureVideoBuffer(m_device, sample, d3d11tex, rhi);
- else
- qCDebug(qLcEvrD3DPresentEngine) << "Failed to obtain D3D11Texture2D from D3D9Texture2D handle";
- }
- }
+ vb = new D3D11TextureVideoBuffer(m_device, sample, sharedHandle, rhi);
#if QT_CONFIG(opengl)
} else if (rhi->backend() == QRhi::OpenGLES2) {
vb = new OpenGlVideoBuffer(m_device, sample, m_wglNvDxInterop, sharedHandle, rhi);
diff --git a/src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h b/src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h
index f68ae50f1..93aa90b71 100644
--- a/src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h
+++ b/src/plugins/multimedia/windows/evr/evrd3dpresentengine_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef EVRD3DPRESENTENGINE_H
#define EVRD3DPRESENTENGINE_H
@@ -52,8 +16,10 @@
//
#include <QMutex>
+#include <QSize>
#include <QVideoFrameFormat>
-#include <private/qwindowsiupointer_p.h>
+#include <private/qcomptr_p.h>
+#include <qpointer.h>
#include <d3d9.h>
@@ -141,9 +107,10 @@ public:
HRESULT checkFormat(D3DFORMAT format);
UINT refreshRate() const { return m_displayMode.RefreshRate; }
- HRESULT createVideoSamples(IMFMediaType *format, QList<IMFSample*>& videoSampleQueue);
+ HRESULT createVideoSamples(IMFMediaType *format, QList<ComPtr<IMFSample>> &videoSampleQueue,
+ QSize frameSize);
QVideoFrameFormat videoSurfaceFormat() const { return m_surfaceFormat; }
- QVideoFrame makeVideoFrame(IMFSample* sample);
+ QVideoFrame makeVideoFrame(const ComPtr<IMFSample> &sample);
void releaseResources();
void setSink(QVideoSink *sink);
@@ -159,13 +126,13 @@ private:
UINT m_deviceResetToken;
D3DDISPLAYMODE m_displayMode;
- QWindowsIUPointer<IDirect3D9Ex> m_D3D9;
- QWindowsIUPointer<IDirect3DDevice9Ex> m_device;
- QWindowsIUPointer<IDirect3DDeviceManager9> m_devices;
+ ComPtr<IDirect3D9Ex> m_D3D9;
+ ComPtr<IDirect3DDevice9Ex> m_device;
+ ComPtr<IDirect3DDeviceManager9> m_devices;
QVideoFrameFormat m_surfaceFormat;
- class QVideoSink *m_sink = nullptr;
+ QPointer<QVideoSink> m_sink;
bool m_useTextureRendering = false;
#if QT_CONFIG(opengl)
WglNvDxInterop m_wglNvDxInterop;
diff --git a/src/plugins/multimedia/windows/evr/evrhelpers.cpp b/src/plugins/multimedia/windows/evr/evrhelpers.cpp
index 53e68d619..bf4347c69 100644
--- a/src/plugins/multimedia/windows/evr/evrhelpers.cpp
+++ b/src/plugins/multimedia/windows/evr/evrhelpers.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "evrhelpers_p.h"
diff --git a/src/plugins/multimedia/windows/evr/evrhelpers_p.h b/src/plugins/multimedia/windows/evr/evrhelpers_p.h
index d7d6fb298..30779c835 100644
--- a/src/plugins/multimedia/windows/evr/evrhelpers_p.h
+++ b/src/plugins/multimedia/windows/evr/evrhelpers_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef EVRHELPERS_H
#define EVRHELPERS_H
@@ -59,6 +23,7 @@
#include <mfidl.h>
#include <mfapi.h>
#include <mferror.h>
+#include <private/quniquehandle_p.h>
QT_BEGIN_NAMESPACE
@@ -112,6 +77,16 @@ inline HRESULT qt_evr_getFrameRate(IMFMediaType *pType, MFRatio *pRatio)
QVideoFrameFormat::PixelFormat qt_evr_pixelFormatFromD3DFormat(DWORD format);
D3DFORMAT qt_evr_D3DFormatFromPixelFormat(QVideoFrameFormat::PixelFormat format);
+struct NullHandleTraits
+{
+ using Type = HANDLE;
+ static Type invalidValue() { return nullptr; }
+ static bool close(Type handle) { return CloseHandle(handle) != 0; }
+};
+
+using EventHandle = QUniqueHandle<NullHandleTraits>;
+using ThreadHandle = QUniqueHandle<NullHandleTraits>;
+
QT_END_NAMESPACE
#endif // EVRHELPERS_H
diff --git a/src/plugins/multimedia/windows/evr/evrvideowindowcontrol.cpp b/src/plugins/multimedia/windows/evr/evrvideowindowcontrol.cpp
index 4ea9fd716..854c9ddb2 100644
--- a/src/plugins/multimedia/windows/evr/evrvideowindowcontrol.cpp
+++ b/src/plugins/multimedia/windows/evr/evrvideowindowcontrol.cpp
@@ -1,44 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "evrvideowindowcontrol_p.h"
+QT_BEGIN_NAMESPACE
+
EvrVideoWindowControl::EvrVideoWindowControl(QVideoSink *parent)
: QPlatformVideoSink(parent)
, m_windowId(0)
@@ -256,3 +222,7 @@ DXVA2_Fixed32 EvrVideoWindowControl::scaleProcAmpValue(DWORD prop, float value)
return DXVA2FloatToFixed(scaledValue);
}
+
+QT_END_NAMESPACE
+
+#include "moc_evrvideowindowcontrol_p.cpp"
diff --git a/src/plugins/multimedia/windows/evr/evrvideowindowcontrol_p.h b/src/plugins/multimedia/windows/evr/evrvideowindowcontrol_p.h
index dc29e1af7..c4875d28d 100644
--- a/src/plugins/multimedia/windows/evr/evrvideowindowcontrol_p.h
+++ b/src/plugins/multimedia/windows/evr/evrvideowindowcontrol_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef EVRVIDEOWINDOWCONTROL_H
#define EVRVIDEOWINDOWCONTROL_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp
index 53e8650f7..d5e25e1c5 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowscamera.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowscamera_p.h"
@@ -133,3 +97,5 @@ void QWindowsCamera::onActiveChanged(bool active)
}
QT_END_NAMESPACE
+
+#include "moc_qwindowscamera_p.cpp"
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowscamera_p.h b/src/plugins/multimedia/windows/mediacapture/qwindowscamera_p.h
index 379a2cba3..2aec11165 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowscamera_p.h
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowscamera_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSCAMERA_H
#define QWINDOWSCAMERA_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp
index e2a1952db..ea66d561a 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsimagecapture_p.h"
@@ -117,11 +81,11 @@ void QWindowsImageCapture::setCaptureSession(QPlatformMediaCaptureSession *sessi
if (isReadyForCapture() != readyForCapture)
emit readyForCaptureChanged(isReadyForCapture());
- connect(m_mediaDeviceSession, SIGNAL(readyForCaptureChanged(bool)),
- this, SIGNAL(readyForCaptureChanged(bool)));
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::readyForCaptureChanged,
+ this, &QWindowsImageCapture::readyForCaptureChanged);
- connect(m_mediaDeviceSession, SIGNAL(videoFrameChanged(QVideoFrame)),
- this, SLOT(handleVideoFrameChanged(QVideoFrame)));
+ connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::videoFrameChanged,
+ this, &QWindowsImageCapture::handleVideoFrameChanged);
}
void QWindowsImageCapture::handleVideoFrameChanged(const QVideoFrame &frame)
@@ -130,13 +94,23 @@ void QWindowsImageCapture::handleVideoFrameChanged(const QVideoFrame &frame)
QImage image = frame.toImage();
+ QSize size = m_settings.resolution();
+ if (size.isValid() && image.size() != size) {
+ image = image.scaled(size, Qt::KeepAspectRatioByExpanding);
+ if (image.size() != size) {
+ int xoff = (image.size().width() - size.width()) / 2;
+ int yoff = (image.size().height() - size.height()) / 2;
+ image = image.copy(xoff, yoff, size.width(), size.height());
+ }
+ }
+
emit imageExposed(m_captureId);
emit imageAvailable(m_captureId, frame);
emit imageCaptured(m_captureId, image);
QMediaMetaData metaData = this->metaData();
metaData.insert(QMediaMetaData::Date, QDateTime::currentDateTime());
- metaData.insert(QMediaMetaData::Resolution, frame.size());
+ metaData.insert(QMediaMetaData::Resolution, size);
emit imageMetadataAvailable(m_captureId, metaData);
@@ -229,3 +203,5 @@ int QWindowsImageCapture::writerQuality(const QString &writerFormat,
}
QT_END_NAMESPACE
+
+#include "moc_qwindowsimagecapture_p.cpp"
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture_p.h b/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture_p.h
index 6c3bc8107..746732e73 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture_p.h
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsimagecapture_p.h
@@ -1,44 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QWindowsImageCapture_H
-#define QWindowsImageCapture_H
+#ifndef QWINDOWSIMAGECAPTURE_H
+#define QWINDOWSIMAGECAPTURE_H
//
// W A R N I N G
@@ -97,4 +61,4 @@ private:
QT_END_NAMESPACE
-#endif // QWindowsImageCapture_H
+#endif // QWINDOWSIMAGECAPTURE_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.cpp
index 7f6a061c9..d349b2c43 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.cpp
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsmediacapture_p.h"
@@ -141,3 +105,5 @@ QWindowsMediaDeviceSession *QWindowsMediaCaptureService::session() const
}
QT_END_NAMESPACE
+
+#include "moc_qwindowsmediacapture_p.cpp"
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture_p.h b/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture_p.h
index 30c3c1abd..579310afd 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture_p.h
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediacapture_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSMEDIACAPTURE_H
#define QWINDOWSMEDIACAPTURE_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp
index cbb070595..2bdc4ea7d 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsmediadevicereader_p.h"
@@ -45,7 +9,7 @@
#include <qaudiodevice.h>
#include <private/qmemoryvideobuffer_p.h>
#include <private/qwindowsmfdefs_p.h>
-#include <private/qwindowsiupointer_p.h>
+#include <private/qcomptr_p.h>
#include <QtCore/qdebug.h>
#include <mmdeviceapi.h>
@@ -58,7 +22,7 @@ QWindowsMediaDeviceReader::QWindowsMediaDeviceReader(QObject *parent)
: QObject(parent)
{
m_durationTimer.setInterval(100);
- connect(&m_durationTimer, SIGNAL(timeout()), this, SLOT(updateDuration()));
+ connect(&m_durationTimer, &QTimer::timeout, this, &QWindowsMediaDeviceReader::updateDuration);
}
QWindowsMediaDeviceReader::~QWindowsMediaDeviceReader()
@@ -246,7 +210,7 @@ HRESULT QWindowsMediaDeviceReader::prepareVideoStream(DWORD mediaTypeIndex)
// and the stride, which we need to convert the frame later
hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, m_frameWidth, &m_stride);
if (SUCCEEDED(hr)) {
-
+ m_stride = qAbs(m_stride);
UINT32 frameRateNum, frameRateDen;
hr = MFGetAttributeRatio(m_videoMediaType, MF_MT_FRAME_RATE, &frameRateNum, &frameRateDen);
if (SUCCEEDED(hr)) {
@@ -674,9 +638,9 @@ QMediaRecorder::Error QWindowsMediaDeviceReader::startRecording(
if (!m_active || m_recording || (videoFormat == GUID_NULL && audioFormat == GUID_NULL))
return QMediaRecorder::ResourceError;
- QWindowsIUPointer<IMFAttributes> writerAttributes;
+ ComPtr<IMFAttributes> writerAttributes;
- HRESULT hr = MFCreateAttributes(writerAttributes.address(), 2);
+ HRESULT hr = MFCreateAttributes(writerAttributes.GetAddressOf(), 2);
if (FAILED(hr))
return QMediaRecorder::ResourceError;
@@ -690,9 +654,9 @@ QMediaRecorder::Error QWindowsMediaDeviceReader::startRecording(
if (FAILED(hr))
return QMediaRecorder::ResourceError;
- QWindowsIUPointer<IMFSinkWriter> sinkWriter;
+ ComPtr<IMFSinkWriter> sinkWriter;
hr = MFCreateSinkWriterFromURL(reinterpret_cast<LPCWSTR>(fileName.utf16()),
- nullptr, writerAttributes.get(), sinkWriter.address());
+ nullptr, writerAttributes.Get(), sinkWriter.GetAddressOf());
if (FAILED(hr))
return QMediaRecorder::LocationNotWritable;
@@ -738,7 +702,7 @@ QMediaRecorder::Error QWindowsMediaDeviceReader::startRecording(
if (FAILED(hr))
return QMediaRecorder::ResourceError;
- m_sinkWriter = sinkWriter.release();
+ m_sinkWriter = sinkWriter.Detach();
m_lastDuration = -1;
m_currentDuration = 0;
updateDuration();
@@ -1048,3 +1012,5 @@ STDMETHODIMP QWindowsMediaDeviceReader::OnMarker(DWORD, LPVOID)
}
QT_END_NAMESPACE
+
+#include "moc_qwindowsmediadevicereader_p.cpp"
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h
index 0b78be843..4699a463a 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicereader_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSMEDIADEVICEREADER_H
#define QWINDOWSMEDIADEVICEREADER_H
@@ -53,8 +17,8 @@
#include <mfapi.h>
#include <mfidl.h>
-#include <Mferror.h>
-#include <Mfreadwrite.h>
+#include <mferror.h>
+#include <mfreadwrite.h>
#include <QtCore/qobject.h>
#include <QtCore/qmutex.h>
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp
index d47188aeb..b13599444 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsmediadevicesession_p.h"
@@ -53,14 +17,22 @@ QWindowsMediaDeviceSession::QWindowsMediaDeviceSession(QObject *parent)
: QObject(parent)
{
m_mediaDeviceReader = new QWindowsMediaDeviceReader(this);
- connect(m_mediaDeviceReader, SIGNAL(streamingStarted()), this, SLOT(handleStreamingStarted()));
- connect(m_mediaDeviceReader, SIGNAL(streamingStopped()), this, SLOT(handleStreamingStopped()));
- connect(m_mediaDeviceReader, SIGNAL(streamingError(int)), this, SLOT(handleStreamingError(int)));
- connect(m_mediaDeviceReader, SIGNAL(videoFrameChanged(QVideoFrame)), this, SLOT(handleVideoFrameChanged(QVideoFrame)));
- connect(m_mediaDeviceReader, SIGNAL(recordingStarted()), this, SIGNAL(recordingStarted()));
- connect(m_mediaDeviceReader, SIGNAL(recordingStopped()), this, SIGNAL(recordingStopped()));
- connect(m_mediaDeviceReader, SIGNAL(recordingError(int)), this, SIGNAL(recordingError(int)));
- connect(m_mediaDeviceReader, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingStarted,
+ this, &QWindowsMediaDeviceSession::handleStreamingStarted);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingStopped,
+ this, &QWindowsMediaDeviceSession::handleStreamingStopped);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingError,
+ this, &QWindowsMediaDeviceSession::handleStreamingError);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::videoFrameChanged,
+ this, &QWindowsMediaDeviceSession::handleVideoFrameChanged);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingStarted,
+ this, &QWindowsMediaDeviceSession::recordingStarted);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingStopped,
+ this, &QWindowsMediaDeviceSession::recordingStopped);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingError,
+ this, &QWindowsMediaDeviceSession::recordingError);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::durationChanged,
+ this, &QWindowsMediaDeviceSession::durationChanged);
}
QWindowsMediaDeviceSession::~QWindowsMediaDeviceSession()
@@ -137,10 +109,12 @@ void QWindowsMediaDeviceSession::setVideoSink(QVideoSink *surface)
void QWindowsMediaDeviceSession::handleStreamingStarted()
{
- m_active = true;
- m_activating = false;
- emit activeChanged(m_active);
- emit readyForCaptureChanged(m_active);
+ if (m_activating) {
+ m_active = true;
+ m_activating = false;
+ emit activeChanged(m_active);
+ emit readyForCaptureChanged(m_active);
+ }
}
void QWindowsMediaDeviceSession::handleStreamingStopped()
@@ -153,14 +127,14 @@ void QWindowsMediaDeviceSession::handleStreamingStopped()
void QWindowsMediaDeviceSession::handleStreamingError(int errorCode)
{
if (m_surface)
- emit m_surface->platformVideoSink()->setVideoFrame(QVideoFrame());
+ m_surface->platformVideoSink()->setVideoFrame(QVideoFrame());
emit streamingError(errorCode);
}
void QWindowsMediaDeviceSession::handleVideoFrameChanged(const QVideoFrame &frame)
{
if (m_surface)
- emit m_surface->platformVideoSink()->setVideoFrame(frame);
+ m_surface->platformVideoSink()->setVideoFrame(frame);
emit videoFrameChanged(frame);
}
@@ -398,3 +372,5 @@ quint32 QWindowsMediaDeviceSession::estimateAudioBitRate(const GUID &audioFormat
}
QT_END_NAMESPACE
+
+#include "moc_qwindowsmediadevicesession_p.cpp"
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession_p.h b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession_p.h
index 232167e77..c3998ce6c 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession_p.h
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSMEDIADEVICESESSION_H
#define QWINDOWSMEDIADEVICESESSION_H
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp
index 97d80c8bf..512110af6 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsmediaencoder_p.h"
@@ -44,7 +8,7 @@
#include "mfmetadata_p.h"
#include <QtCore/QUrl>
#include <QtCore/QMimeType>
-#include <Mferror.h>
+#include <mferror.h>
#include <shobjidl.h>
#include <private/qmediastoragelocation_p.h>
#include <private/qmediarecorder_p.h>
@@ -81,15 +45,15 @@ void QWindowsMediaEncoder::record(QMediaEncoderSettings &settings)
if (m_state != QMediaRecorder::StoppedState)
return;
- m_sessionWasActive = m_mediaDeviceSession->isActive();
+ m_sessionWasActive = m_mediaDeviceSession->isActive() || m_mediaDeviceSession->isActivating();
if (!m_sessionWasActive) {
m_mediaDeviceSession->setActive(true);
if (!m_mediaDeviceSession->isActivating()) {
- error(QMediaRecorder::ResourceError,
- QMediaRecorderPrivate::msgFailedStartRecording());
+ updateError(QMediaRecorder::ResourceError,
+ QMediaRecorderPrivate::msgFailedStartRecording());
return;
}
}
@@ -108,7 +72,7 @@ void QWindowsMediaEncoder::record(QMediaEncoderSettings &settings)
stateChanged(m_state);
} else {
- error(ec, QMediaRecorderPrivate::msgFailedStartRecording());
+ updateError(ec, QMediaRecorderPrivate::msgFailedStartRecording());
}
}
@@ -121,7 +85,7 @@ void QWindowsMediaEncoder::pause()
m_state = QMediaRecorder::PausedState;
stateChanged(m_state);
} else {
- error(QMediaRecorder::FormatError, tr("Failed to pause recording"));
+ updateError(QMediaRecorder::FormatError, tr("Failed to pause recording"));
}
}
@@ -134,16 +98,17 @@ void QWindowsMediaEncoder::resume()
m_state = QMediaRecorder::RecordingState;
stateChanged(m_state);
} else {
- error(QMediaRecorder::FormatError, tr("Failed to resume recording"));
+ updateError(QMediaRecorder::FormatError, tr("Failed to resume recording"));
}
}
void QWindowsMediaEncoder::stop()
{
- if (m_mediaDeviceSession && m_state != QMediaRecorder::StoppedState)
+ if (m_mediaDeviceSession && m_state != QMediaRecorder::StoppedState) {
m_mediaDeviceSession->stopRecording();
- if (!m_sessionWasActive)
- m_mediaDeviceSession->setActive(false);
+ if (!m_sessionWasActive)
+ m_mediaDeviceSession->setActive(false);
+ }
}
@@ -213,11 +178,11 @@ void QWindowsMediaEncoder::onDurationChanged(qint64 duration)
void QWindowsMediaEncoder::onStreamingError(int errorCode)
{
if (errorCode == MF_E_VIDEO_RECORDING_DEVICE_INVALIDATED)
- error(QMediaRecorder::ResourceError, tr("Camera is no longer present"));
+ updateError(QMediaRecorder::ResourceError, tr("Camera is no longer present"));
else if (errorCode == MF_E_AUDIO_RECORDING_DEVICE_INVALIDATED)
- error(QMediaRecorder::ResourceError, tr("Audio input is no longer present"));
+ updateError(QMediaRecorder::ResourceError, tr("Audio input is no longer present"));
else
- error(QMediaRecorder::ResourceError, tr("Streaming error"));
+ updateError(QMediaRecorder::ResourceError, tr("Streaming error"));
if (m_state != QMediaRecorder::StoppedState) {
m_mediaDeviceSession->stopRecording();
@@ -229,7 +194,7 @@ void QWindowsMediaEncoder::onStreamingError(int errorCode)
void QWindowsMediaEncoder::onRecordingError(int errorCode)
{
Q_UNUSED(errorCode);
- error(QMediaRecorder::ResourceError, tr("Recording error"));
+ updateError(QMediaRecorder::ResourceError, tr("Recording error"));
auto lastState = m_state;
m_state = QMediaRecorder::StoppedState;
@@ -256,3 +221,5 @@ void QWindowsMediaEncoder::onRecordingStopped()
}
QT_END_NAMESPACE
+
+#include "moc_qwindowsmediaencoder_p.cpp"
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder_p.h b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder_p.h
index 22295791a..51f35ce9d 100644
--- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder_p.h
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//
// W A R N I N G
diff --git a/src/plugins/multimedia/windows/mfstream.cpp b/src/plugins/multimedia/windows/mfstream.cpp
index c49971025..fb37ce293 100644
--- a/src/plugins/multimedia/windows/mfstream.cpp
+++ b/src/plugins/multimedia/windows/mfstream.cpp
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "mfstream_p.h"
#include <QtCore/qcoreapplication.h>
+QT_BEGIN_NAMESPACE
//MFStream is added for supporting QIODevice type of media source.
//It is used to delegate invocations from media foundation(through IMFByteStream) to QIODevice.
@@ -53,7 +18,6 @@ MFStream::MFStream(QIODevice *stream, bool ownStream)
//to make sure invocations on stream
//are happened in the same thread of stream object
this->moveToThread(stream->thread());
- connect(stream, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
}
MFStream::~MFStream()
@@ -287,12 +251,6 @@ void MFStream::doRead()
}
}
-
-void MFStream::handleReadyRead()
-{
- doRead();
-}
-
void MFStream::customEvent(QEvent *event)
{
if (event->type() != QEvent::User) {
@@ -362,3 +320,7 @@ void MFStream::AsyncReadState::setBytesRead(ULONG cbRead)
{
m_cbRead = cbRead;
}
+
+QT_END_NAMESPACE
+
+#include "moc_mfstream_p.cpp"
diff --git a/src/plugins/multimedia/windows/mfstream_p.h b/src/plugins/multimedia/windows/mfstream_p.h
index 3c8d6b296..a5221ed75 100644
--- a/src/plugins/multimedia/windows/mfstream_p.h
+++ b/src/plugins/multimedia/windows/mfstream_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFSTREAM_H
#define MFSTREAM_H
@@ -58,7 +22,7 @@
#include <QtCore/qcoreevent.h>
#include <QtCore/qpointer.h>
-QT_USE_NAMESPACE
+QT_BEGIN_NAMESPACE
class MFStream : public QObject, public IMFByteStream
{
@@ -150,12 +114,11 @@ private:
void doRead();
-private Q_SLOTS:
- void handleReadyRead();
-
protected:
void customEvent(QEvent *event) override;
IMFAsyncResult *m_currentReadResult;
};
+QT_END_NAMESPACE
+
#endif
diff --git a/src/plugins/multimedia/windows/player/mfactivate.cpp b/src/plugins/multimedia/windows/player/mfactivate.cpp
index 05d9321be..644c96529 100644
--- a/src/plugins/multimedia/windows/player/mfactivate.cpp
+++ b/src/plugins/multimedia/windows/player/mfactivate.cpp
@@ -1,49 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "mfactivate_p.h"
#include <mfapi.h>
MFAbstractActivate::MFAbstractActivate()
- : m_attributes(0)
- , m_cRef(1)
{
MFCreateAttributes(&m_attributes, 0);
}
@@ -53,35 +15,3 @@ MFAbstractActivate::~MFAbstractActivate()
if (m_attributes)
m_attributes->Release();
}
-
-
-HRESULT MFAbstractActivate::QueryInterface(REFIID riid, LPVOID *ppvObject)
-{
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFActivate) {
- *ppvObject = static_cast<IMFActivate*>(this);
- } else if (riid == IID_IMFAttributes) {
- *ppvObject = static_cast<IMFAttributes*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(static_cast<IMFActivate*>(this));
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-ULONG MFAbstractActivate::AddRef(void)
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-ULONG MFAbstractActivate::Release(void)
-{
- ULONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- delete this;
- return cRef;
-}
diff --git a/src/plugins/multimedia/windows/player/mfactivate_p.h b/src/plugins/multimedia/windows/player/mfactivate_p.h
index d3d5cf212..efe75474b 100644
--- a/src/plugins/multimedia/windows/player/mfactivate_p.h
+++ b/src/plugins/multimedia/windows/player/mfactivate_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFACTIVATE_H
#define MFACTIVATE_H
@@ -52,17 +16,27 @@
//
#include <mfidl.h>
+#include <private/qcomobject_p.h>
-class MFAbstractActivate : public IMFActivate
+QT_BEGIN_NAMESPACE
+
+namespace QtPrivate {
+
+template <>
+struct QComObjectTraits<IMFActivate>
+{
+ static constexpr bool isGuidOf(REFIID riid) noexcept
+ {
+ return QComObjectTraits<IMFActivate, IMFAttributes>::isGuidOf(riid);
+ }
+};
+
+} // namespace QtPrivate
+
+class MFAbstractActivate : public QComObject<IMFActivate>
{
public:
explicit MFAbstractActivate();
- virtual ~MFAbstractActivate();
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
- STDMETHODIMP_(ULONG) AddRef(void) override;
- STDMETHODIMP_(ULONG) Release(void) override;
//from IMFAttributes
STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT *pValue) override
@@ -215,9 +189,14 @@ public:
return m_attributes->CopyAllItems(pDest);
}
+protected:
+ // Destructor is not public. Caller should call Release.
+ ~MFAbstractActivate() override;
+
private:
- IMFAttributes *m_attributes;
- ULONG m_cRef;
+ IMFAttributes *m_attributes = nullptr;
};
+QT_END_NAMESPACE
+
#endif // MFACTIVATE_H
diff --git a/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol.cpp b/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol.cpp
index 105424253..109f7964b 100644
--- a/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol.cpp
+++ b/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "mfevrvideowindowcontrol_p.h"
diff --git a/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h b/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h
index d1855705c..1ac90e8ce 100644
--- a/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h
+++ b/src/plugins/multimedia/windows/player/mfevrvideowindowcontrol_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFEVRVIDEOWINDOWCONTROL_H
#define MFEVRVIDEOWINDOWCONTROL_H
diff --git a/src/plugins/multimedia/windows/player/mfplayercontrol.cpp b/src/plugins/multimedia/windows/player/mfplayercontrol.cpp
index 67a5caa7b..ae0022773 100644
--- a/src/plugins/multimedia/windows/player/mfplayercontrol.cpp
+++ b/src/plugins/multimedia/windows/player/mfplayercontrol.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "mfplayercontrol_p.h"
#include "mfplayersession_p.h"
@@ -110,7 +74,8 @@ void MFPlayerControl::pause()
if (m_state == QMediaPlayer::PausedState)
return;
- if (m_session->status() == QMediaPlayer::NoMedia)
+ if (m_session->status() == QMediaPlayer::NoMedia ||
+ m_session->status() == QMediaPlayer::InvalidMedia)
return;
changeState(QMediaPlayer::PausedState);
@@ -158,7 +123,7 @@ void MFPlayerControl::refreshState()
#ifdef DEBUG_MEDIAFOUNDATION
qDebug() << "MFPlayerControl::emit stateChanged" << m_state;
#endif
- emit stateChanged(m_state);
+ stateChanged(m_state);
}
void MFPlayerControl::handleStatusChanged()
@@ -184,7 +149,7 @@ void MFPlayerControl::handleStatusChanged()
default:
break;
}
- emit mediaStatusChanged(m_session->status());
+ mediaStatusChanged(m_session->status());
refreshState();
}
@@ -198,7 +163,7 @@ void MFPlayerControl::handleVideoAvailable()
if (m_videoAvailable)
return;
m_videoAvailable = true;
- emit videoAvailableChanged(m_videoAvailable);
+ videoAvailableChanged(m_videoAvailable);
}
void MFPlayerControl::handleAudioAvailable()
@@ -206,7 +171,7 @@ void MFPlayerControl::handleAudioAvailable()
if (m_audioAvailable)
return;
m_audioAvailable = true;
- emit audioAvailableChanged(m_audioAvailable);
+ audioAvailableChanged(m_audioAvailable);
}
void MFPlayerControl::resetAudioVideoAvailable()
@@ -218,10 +183,10 @@ void MFPlayerControl::resetAudioVideoAvailable()
}
if (m_audioAvailable) {
m_audioAvailable = false;
- emit audioAvailableChanged(m_audioAvailable);
+ audioAvailableChanged(m_audioAvailable);
}
if (videoDirty)
- emit videoAvailableChanged(m_videoAvailable);
+ videoAvailableChanged(m_videoAvailable);
}
void MFPlayerControl::handleDurationUpdate(qint64 duration)
@@ -229,7 +194,7 @@ void MFPlayerControl::handleDurationUpdate(qint64 duration)
if (m_duration == duration)
return;
m_duration = duration;
- emit durationChanged(m_duration);
+ durationChanged(m_duration);
}
void MFPlayerControl::handleSeekableUpdate(bool seekable)
@@ -237,7 +202,7 @@ void MFPlayerControl::handleSeekableUpdate(bool seekable)
if (m_seekable == seekable)
return;
m_seekable = seekable;
- emit seekableChanged(m_seekable);
+ seekableChanged(m_seekable);
}
QMediaPlayer::PlaybackState MFPlayerControl::state() const
@@ -316,7 +281,7 @@ void MFPlayerControl::handleError(QMediaPlayer::Error errorCode, const QString&
{
if (isFatal)
stop();
- emit error(int(errorCode), errorString);
+ error(int(errorCode), errorString);
}
void MFPlayerControl::setActiveTrack(TrackType type, int index)
diff --git a/src/plugins/multimedia/windows/player/mfplayercontrol_p.h b/src/plugins/multimedia/windows/player/mfplayercontrol_p.h
index 68c24194b..db863afaa 100644
--- a/src/plugins/multimedia/windows/player/mfplayercontrol_p.h
+++ b/src/plugins/multimedia/windows/player/mfplayercontrol_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFPLAYERCONTROL_H
#define MFPLAYERCONTROL_H
@@ -51,12 +15,12 @@
// We mean it.
//
-#include "QUrl.h"
+#include "qurl.h"
#include "private/qplatformmediaplayer_p.h"
#include <QtCore/qcoreevent.h>
-QT_USE_NAMESPACE
+QT_BEGIN_NAMESPACE
class MFPlayerSession;
@@ -134,4 +98,6 @@ private:
MFPlayerSession *m_session;
};
+QT_END_NAMESPACE
+
#endif
diff --git a/src/plugins/multimedia/windows/player/mfplayersession.cpp b/src/plugins/multimedia/windows/player/mfplayersession.cpp
index be99399ed..16cba7266 100644
--- a/src/plugins/multimedia/windows/player/mfplayersession.cpp
+++ b/src/plugins/multimedia/windows/player/mfplayersession.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "private/qplatformmediaplayer_p.h"
@@ -51,7 +15,6 @@
#include "qaudiooutput.h"
#include "mfplayercontrol_p.h"
-#include "mfevrvideowindowcontrol_p.h"
#include "mfvideorenderercontrol_p.h"
#include <mfmetadata_p.h>
#include <private/qwindowsmfdefs_p.h>
@@ -60,41 +23,30 @@
#include "mfplayersession_p.h"
#include <mferror.h>
#include <nserror.h>
+#include <winerror.h>
#include "sourceresolver_p.h"
-#include "samplegrabber_p.h"
-#include "mftvideo_p.h"
#include <wmcodecdsp.h>
#include <mmdeviceapi.h>
#include <propvarutil.h>
-#include <Functiondiscoverykeys_devpkey.h>
+#include <functiondiscoverykeys_devpkey.h>
//#define DEBUG_MEDIAFOUNDATION
+QT_BEGIN_NAMESPACE
MFPlayerSession::MFPlayerSession(MFPlayerControl *playerControl)
- : m_cRef(1)
- , m_playerControl(playerControl)
- , m_session(0)
- , m_presentationClock(0)
- , m_rateControl(0)
- , m_rateSupport(0)
- , m_volumeControl(0)
- , m_netsourceStatistics(0)
- , m_scrubbing(false)
- , m_restoreRate(1)
- , m_sourceResolver(0)
- , m_hCloseEvent(0)
- , m_closing(false)
- , m_mediaTypes(0)
- , m_pendingRate(1)
- , m_status(QMediaPlayer::NoMedia)
- , m_audioSampleGrabber(0)
- , m_audioSampleGrabberNode(0)
- , m_videoProbeMFT(0)
+ : m_cRef(1),
+ m_playerControl(playerControl),
+ m_scrubbing(false),
+ m_restoreRate(1),
+ m_closing(false),
+ m_mediaTypes(0),
+ m_pendingRate(1),
+ m_status(QMediaPlayer::NoMedia)
{
- QObject::connect(this, SIGNAL(sessionEvent(IMFMediaEvent*)), this, SLOT(handleSessionEvent(IMFMediaEvent*)));
+ connect(this, &MFPlayerSession::sessionEvent, this, &MFPlayerSession::handleSessionEvent);
m_signalPositionChangeTimer.setInterval(10);
m_signalPositionChangeTimer.setTimerType(Qt::PreciseTimer);
@@ -110,8 +62,7 @@ MFPlayerSession::MFPlayerSession(MFPlayerControl *playerControl)
m_request.prevCmd = CmdNone;
m_request.rate = 1.0f;
- m_audioSampleGrabber = new AudioSampleGrabberCallback;
- m_videoRendererControl = new MFVideoRendererControl;
+ m_videoRendererControl = new MFVideoRendererControl(this);
}
void MFPlayerSession::timeout()
@@ -153,7 +104,7 @@ void MFPlayerSession::close()
m_closing = true;
hr = m_session->Close();
if (SUCCEEDED(hr)) {
- DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent, 2000);
+ DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent.get(), 2000);
if (dwWaitResult == WAIT_TIMEOUT) {
qWarning() << "session close time out!";
}
@@ -167,35 +118,19 @@ void MFPlayerSession::close()
if (m_sourceResolver)
m_sourceResolver->shutdown();
}
- if (m_sourceResolver) {
- m_sourceResolver->Release();
- m_sourceResolver = 0;
- }
- if (m_videoProbeMFT) {
- m_videoProbeMFT->Release();
- m_videoProbeMFT = 0;
- }
+ m_sourceResolver.Reset();
m_videoRendererControl->releaseActivate();
// } else if (m_playerService->videoWindowControl()) {
// m_playerService->videoWindowControl()->releaseActivate();
// }
- if (m_session)
- m_session->Release();
- m_session = 0;
- if (m_hCloseEvent)
- CloseHandle(m_hCloseEvent);
- m_hCloseEvent = 0;
+ m_session.Reset();
+ m_hCloseEvent = {};
m_lastPosition = -1;
+ m_position = 0;
}
-MFPlayerSession::~MFPlayerSession()
-{
- m_audioSampleGrabber->Release();
-}
-
-
void MFPlayerSession::load(const QUrl &url, QIODevice *stream)
{
#ifdef DEBUG_MEDIAFOUNDATION
@@ -212,11 +147,12 @@ void MFPlayerSession::load(const QUrl &url, QIODevice *stream)
} else if (stream && (!stream->isReadable())) {
close();
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Invalid stream source."), true);
+ error(QMediaPlayer::ResourceError, tr("Invalid stream source."), true);
} else if (createSession()) {
changeStatus(QMediaPlayer::LoadingMedia);
m_sourceResolver->load(url, stream);
- m_updateRoutingOnStart = true;
+ if (url.isLocalFile())
+ m_updateRoutingOnStart = true;
}
positionChanged(position());
}
@@ -240,17 +176,28 @@ void MFPlayerSession::handleSourceError(long hr)
errorCode = QMediaPlayer::FormatError;
errorString = tr("Unsupported media type.");
break;
+ case MF_E_UNSUPPORTED_SCHEME:
+ errorCode = QMediaPlayer::ResourceError;
+ errorString = tr("Unsupported URL scheme.");
+ break;
+ case QMM_WININET_E_CANNOT_CONNECT:
+ errorCode = QMediaPlayer::NetworkError;
+ errorString = tr("Connection to server could not be established.");
+ break;
default:
+ qWarning() << "handleSourceError:"
+ << Qt::showbase << Qt::hex << Qt::uppercasedigits << static_cast<quint32>(hr);
errorString = tr("Failed to load source.");
break;
}
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(errorCode, errorString, true);
+ error(errorCode, errorString, true);
}
void MFPlayerSession::handleMediaSourceReady()
{
- if (QMediaPlayer::LoadingMedia != m_status || !m_sourceResolver || m_sourceResolver != sender())
+ if (QMediaPlayer::LoadingMedia != m_status || !m_sourceResolver
+ || m_sourceResolver.Get() != sender())
return;
#ifdef DEBUG_MEDIAFOUNDATION
qDebug() << "handleMediaSourceReady";
@@ -260,23 +207,22 @@ void MFPlayerSession::handleMediaSourceReady()
DWORD dwCharacteristics = 0;
mediaSource->GetCharacteristics(&dwCharacteristics);
- emit seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics);
+ seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics);
- IMFPresentationDescriptor* sourcePD;
+ ComPtr<IMFPresentationDescriptor> sourcePD;
hr = mediaSource->CreatePresentationDescriptor(&sourcePD);
if (SUCCEEDED(hr)) {
m_duration = 0;
m_metaData = MFMetaData::fromNative(mediaSource);
- emit metaDataChanged();
+ metaDataChanged();
sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
//convert from 100 nanosecond to milisecond
- emit durationUpdate(qint64(m_duration / 10000));
- setupPlaybackTopology(mediaSource, sourcePD);
+ durationUpdate(qint64(m_duration / 10000));
+ setupPlaybackTopology(mediaSource, sourcePD.Get());
tracksChanged();
- sourcePD->Release();
} else {
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Cannot create presentation descriptor."), true);
+ error(QMediaPlayer::ResourceError, tr("Cannot create presentation descriptor."), true);
}
}
@@ -293,7 +239,7 @@ bool MFPlayerSession::getStreamInfo(IMFStreamDescriptor *stream,
*name = QString();
*language = QString();
- IMFMediaTypeHandler *typeHandler = nullptr;
+ ComPtr<IMFMediaTypeHandler> typeHandler;
if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler))) {
@@ -321,13 +267,10 @@ bool MFPlayerSession::getStreamInfo(IMFStreamDescriptor *stream,
*type = Video;
}
- IMFMediaType *mediaType = nullptr;
+ ComPtr<IMFMediaType> mediaType;
if (SUCCEEDED(typeHandler->GetCurrentMediaType(&mediaType))) {
mediaType->GetGUID(MF_MT_SUBTYPE, format);
- mediaType->Release();
}
-
- typeHandler->Release();
}
return *type != Unknown;
@@ -341,15 +284,15 @@ void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentat
hr = sourcePD->GetStreamDescriptorCount(&cSourceStreams);
if (FAILED(hr)) {
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Failed to get stream count."), true);
+ error(QMediaPlayer::ResourceError, tr("Failed to get stream count."), true);
return;
}
- IMFTopology *topology;
+ ComPtr<IMFTopology> topology;
hr = MFCreateTopology(&topology);
if (FAILED(hr)) {
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Failed to create topology."), true);
+ error(QMediaPlayer::ResourceError, tr("Failed to create topology."), true);
return;
}
@@ -358,7 +301,7 @@ void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentat
for (DWORD i = 0; i < cSourceStreams; i++) {
BOOL selected = FALSE;
bool streamAdded = false;
- IMFStreamDescriptor *streamDesc = NULL;
+ ComPtr<IMFStreamDescriptor> streamDesc;
HRESULT hr = sourcePD->GetStreamDescriptorByIndex(i, &selected, &streamDesc);
if (SUCCEEDED(hr)) {
@@ -369,7 +312,8 @@ void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentat
QString streamLanguage;
GUID format = GUID_NULL;
- if (getStreamInfo(streamDesc, &mediaType, &streamName, &streamLanguage, &format)) {
+ if (getStreamInfo(streamDesc.Get(), &mediaType, &streamName, &streamLanguage,
+ &format)) {
QPlatformMediaPlayer::TrackType trackType = (mediaType == Audio) ?
QPlatformMediaPlayer::AudioStream : QPlatformMediaPlayer::VideoStream;
@@ -386,23 +330,19 @@ void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentat
m_trackInfo[trackType].format = format;
if (((m_mediaTypes & mediaType) == 0) && selected) { // Check if this type isn't already added
- IMFTopologyNode *sourceNode = addSourceNode(topology, source, sourcePD, streamDesc);
+ ComPtr<IMFTopologyNode> sourceNode =
+ addSourceNode(topology.Get(), source, sourcePD, streamDesc.Get());
if (sourceNode) {
- IMFTopologyNode *outputNode = addOutputNode(mediaType, topology, 0);
+ ComPtr<IMFTopologyNode> outputNode =
+ addOutputNode(mediaType, topology.Get(), 0);
if (outputNode) {
- bool connected = false;
- if (mediaType == Audio) {
- if (!m_audioSampleGrabberNode)
- connected = setupAudioSampleGrabber(topology, sourceNode, outputNode);
- }
sourceNode->GetTopoNodeID(&m_trackInfo[trackType].sourceNodeId);
outputNode->GetTopoNodeID(&m_trackInfo[trackType].outputNodeId);
- if (!connected)
- hr = sourceNode->ConnectOutput(0, outputNode, 0);
+ hr = sourceNode->ConnectOutput(0, outputNode.Get(), 0);
if (FAILED(hr)) {
- emit error(QMediaPlayer::FormatError, tr("Unable to play any stream."), false);
+ error(QMediaPlayer::FormatError, tr("Unable to play any stream."), false);
} else {
m_trackInfo[trackType].currentIndex = m_trackInfo[trackType].nativeIndexes.count() - 1;
streamAdded = true;
@@ -410,51 +350,51 @@ void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentat
m_mediaTypes |= mediaType;
switch (mediaType) {
case Audio:
- emit audioAvailable();
+ audioAvailable();
break;
case Video:
- emit videoAvailable();
+ videoAvailable();
break;
default:
break;
}
}
- outputNode->Release();
+ } else {
+ // remove the source node if the output node cannot be created
+ topology->RemoveNode(sourceNode.Get());
}
- sourceNode->Release();
}
}
}
if (selected && !streamAdded)
sourcePD->DeselectStream(i);
-
- streamDesc->Release();
}
}
if (succeededCount == 0) {
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Unable to play."), true);
+ error(QMediaPlayer::ResourceError, tr("Unable to play."), true);
} else {
if (m_trackInfo[QPlatformMediaPlayer::VideoStream].outputNodeId != TOPOID(-1))
topology = insertMFT(topology, m_trackInfo[QPlatformMediaPlayer::VideoStream].outputNodeId);
- hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
+ hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
if (SUCCEEDED(hr)) {
m_updatingTopology = true;
} else {
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Failed to set topology."), true);
+ error(QMediaPlayer::ResourceError, tr("Failed to set topology."), true);
}
}
- topology->Release();
}
-IMFTopologyNode* MFPlayerSession::addSourceNode(IMFTopology* topology, IMFMediaSource* source,
- IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc)
+ComPtr<IMFTopologyNode> MFPlayerSession::addSourceNode(IMFTopology *topology,
+ IMFMediaSource *source,
+ IMFPresentationDescriptor *presentationDesc,
+ IMFStreamDescriptor *streamDesc)
{
- IMFTopologyNode *node = NULL;
+ ComPtr<IMFTopologyNode> node;
HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
if (SUCCEEDED(hr)) {
hr = node->SetUnknown(MF_TOPONODE_SOURCE, source);
@@ -463,184 +403,82 @@ IMFTopologyNode* MFPlayerSession::addSourceNode(IMFTopology* topology, IMFMediaS
if (SUCCEEDED(hr)) {
hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc);
if (SUCCEEDED(hr)) {
- hr = topology->AddNode(node);
+ hr = topology->AddNode(node.Get());
if (SUCCEEDED(hr))
return node;
}
}
}
- node->Release();
}
return NULL;
}
-IMFTopologyNode* MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology* topology, DWORD sinkID)
+ComPtr<IMFTopologyNode> MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology *topology,
+ DWORD sinkID)
{
- IMFTopologyNode *node = NULL;
+ ComPtr<IMFTopologyNode> node;
if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node)))
return NULL;
- IMFActivate *activate = NULL;
+ ComPtr<IMFActivate> activate;
if (mediaType == Audio) {
if (m_audioOutput) {
+ auto id = m_audioOutput->device.id();
+ if (id.isEmpty()) {
+ qInfo() << "No audio output";
+ return NULL;
+ }
+
HRESULT hr = MFCreateAudioRendererActivate(&activate);
if (FAILED(hr)) {
qWarning() << "Failed to create audio renderer activate";
- node->Release();
return NULL;
}
- auto id = m_audioOutput->device.id();
- if (!id.isEmpty()) {
- QString s = QString::fromUtf8(id);
- hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, (LPCWSTR)s.utf16());
- } else {
- //This is the default one that has been inserted in updateEndpoints(),
- //so give the activate a hint that we want to use the device for multimedia playback
- //then the media foundation will choose an appropriate one.
-
- //from MSDN:
- //The ERole enumeration defines constants that indicate the role that the system has assigned to an audio endpoint device.
- //eMultimedia: Music, movies, narration, and live music recording.
- hr = activate->SetUINT32(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ROLE, eMultimedia);
- }
-
+ QString s = QString::fromUtf8(id);
+ hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, (LPCWSTR)s.utf16());
if (FAILED(hr)) {
- qWarning() << "Failed to set attribute for audio device" << m_audioOutput->device.description();
- activate->Release();
- node->Release();
+ qWarning() << "Failed to set attribute for audio device"
+ << m_audioOutput->device.description();
return NULL;
}
}
} else if (mediaType == Video) {
activate = m_videoRendererControl->createActivate();
+
+ QSize resolution = m_metaData.value(QMediaMetaData::Resolution).toSize();
+
+ if (resolution.isValid())
+ m_videoRendererControl->setCropRect(QRect(QPoint(), resolution));
+
} else {
// Unknown stream type.
- emit error(QMediaPlayer::FormatError, tr("Unknown stream type."), false);
+ error(QMediaPlayer::FormatError, tr("Unknown stream type."), false);
}
- if (!activate
- || FAILED(node->SetObject(activate))
- || FAILED(node->SetUINT32(MF_TOPONODE_STREAMID, sinkID))
- || FAILED(node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE))
- || FAILED(topology->AddNode(node))) {
- node->Release();
- node = NULL;
+ if (!activate || FAILED(node->SetObject(activate.Get()))
+ || FAILED(node->SetUINT32(MF_TOPONODE_STREAMID, sinkID))
+ || FAILED(node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE))
+ || FAILED(topology->AddNode(node.Get()))) {
+ node.Reset();
}
if (activate && mediaType == Audio)
- activate->Release();
+ activate.Reset();
return node;
}
-bool MFPlayerSession::addAudioSampleGrabberNode(IMFTopology *topology)
-{
- HRESULT hr = S_OK;
- IMFMediaType *pType = 0;
- IMFActivate *sinkActivate = 0;
- do {
- hr = MFCreateMediaType(&pType);
- if (FAILED(hr))
- break;
-
- hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
- if (FAILED(hr))
- break;
-
- hr = pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
- if (FAILED(hr))
- break;
-
- hr = MFCreateSampleGrabberSinkActivate(pType, m_audioSampleGrabber, &sinkActivate);
- if (FAILED(hr))
- break;
-
- // Note: Data is distorted if this attribute is enabled
- hr = sinkActivate->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, FALSE);
- if (FAILED(hr))
- break;
-
- hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &m_audioSampleGrabberNode);
- if (FAILED(hr))
- break;
-
- hr = m_audioSampleGrabberNode->SetObject(sinkActivate);
- if (FAILED(hr))
- break;
-
- hr = m_audioSampleGrabberNode->SetUINT32(MF_TOPONODE_STREAMID, 0); // Identifier of the stream sink.
- if (FAILED(hr))
- break;
-
- hr = m_audioSampleGrabberNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
- if (FAILED(hr))
- break;
-
- hr = topology->AddNode(m_audioSampleGrabberNode);
- if (FAILED(hr))
- break;
-
- pType->Release();
- sinkActivate->Release();
- return true;
- } while (false);
-
- if (pType)
- pType->Release();
- if (sinkActivate)
- sinkActivate->Release();
- if (m_audioSampleGrabberNode) {
- m_audioSampleGrabberNode->Release();
- m_audioSampleGrabberNode = NULL;
- }
- return false;
-}
-
-bool MFPlayerSession::setupAudioSampleGrabber(IMFTopology *topology, IMFTopologyNode *sourceNode, IMFTopologyNode *outputNode)
-{
- if (!addAudioSampleGrabberNode(topology))
- return false;
-
- HRESULT hr = S_OK;
- IMFTopologyNode *pTeeNode = NULL;
-
- IMFMediaTypeHandler *typeHandler = NULL;
- IMFMediaType *mediaType = NULL;
- do {
- hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &pTeeNode);
- if (FAILED(hr))
- break;
- hr = sourceNode->ConnectOutput(0, pTeeNode, 0);
- if (FAILED(hr))
- break;
- hr = pTeeNode->ConnectOutput(0, outputNode, 0);
- if (FAILED(hr))
- break;
- hr = pTeeNode->ConnectOutput(1, m_audioSampleGrabberNode, 0);
- if (FAILED(hr))
- break;
- } while (false);
-
- if (pTeeNode)
- pTeeNode->Release();
- if (mediaType)
- mediaType->Release();
- if (typeHandler)
- typeHandler->Release();
- return hr == S_OK;
-}
-
// BindOutputNode
// Sets the IMFStreamSink pointer on an output node.
// IMFActivate pointer in the output node must be converted to an
// IMFStreamSink pointer before the topology loader resolves the topology.
HRESULT BindOutputNode(IMFTopologyNode *pNode)
{
- IUnknown *nodeObject = NULL;
- IMFActivate *activate = NULL;
- IMFStreamSink *stream = NULL;
- IMFMediaSink *sink = NULL;
+ ComPtr<IUnknown> nodeObject;
+ ComPtr<IMFActivate> activate;
+ ComPtr<IMFStreamSink> stream;
+ ComPtr<IMFMediaSink> sink;
HRESULT hr = pNode->GetObject(&nodeObject);
if (FAILED(hr))
@@ -666,20 +504,12 @@ HRESULT BindOutputNode(IMFTopologyNode *pNode)
// Replace the node's object pointer with the stream sink.
if (SUCCEEDED(hr)) {
- hr = pNode->SetObject(stream);
+ hr = pNode->SetObject(stream.Get());
}
} else {
hr = nodeObject->QueryInterface(IID_PPV_ARGS(&stream));
}
- if (nodeObject)
- nodeObject->Release();
- if (activate)
- activate->Release();
- if (stream)
- stream->Release();
- if (sink)
- sink->Release();
return hr;
}
@@ -687,7 +517,7 @@ HRESULT BindOutputNode(IMFTopologyNode *pNode)
// Sets the IMFStreamSink pointers on all of the output nodes in a topology.
HRESULT BindOutputNodes(IMFTopology *pTopology)
{
- IMFCollection *collection;
+ ComPtr<IMFCollection> collection;
// Get the collection of output nodes.
HRESULT hr = pTopology->GetOutputNodeCollection(&collection);
@@ -699,25 +529,22 @@ HRESULT BindOutputNodes(IMFTopology *pTopology)
if (SUCCEEDED(hr)) {
for (DWORD i = 0; i < cNodes; i++) {
- IUnknown *element;
+ ComPtr<IUnknown> element;
hr = collection->GetElement(i, &element);
if (FAILED(hr))
break;
- IMFTopologyNode *node;
- hr = element->QueryInterface(IID_IMFTopologyNode, (void**)&node);
- element->Release();
+ ComPtr<IMFTopologyNode> node;
+ hr = element->QueryInterface(IID_IMFTopologyNode, &node);
if (FAILED(hr))
break;
// Bind this node.
- hr = BindOutputNode(node);
- node->Release();
+ hr = BindOutputNode(node.Get());
if (FAILED(hr))
break;
}
}
- collection->Release();
}
return hr;
@@ -726,123 +553,38 @@ HRESULT BindOutputNodes(IMFTopology *pTopology)
// This method binds output nodes to complete the topology,
// then loads the topology and inserts MFT between the output node
// and a filter connected to the output node.
-IMFTopology *MFPlayerSession::insertMFT(IMFTopology *topology, TOPOID outputNodeId)
+ComPtr<IMFTopology> MFPlayerSession::insertMFT(const ComPtr<IMFTopology> &topology,
+ TOPOID outputNodeId)
{
bool isNewTopology = false;
- IMFTopoLoader *topoLoader = 0;
- IMFTopology *resolvedTopology = 0;
- IMFCollection *outputNodes = 0;
+ ComPtr<IMFTopoLoader> topoLoader;
+ ComPtr<IMFTopology> resolvedTopology;
+ ComPtr<IMFCollection> outputNodes;
do {
- if (FAILED(BindOutputNodes(topology)))
+ if (FAILED(BindOutputNodes(topology.Get())))
break;
if (FAILED(MFCreateTopoLoader(&topoLoader)))
break;
- if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL))) {
+ if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL))) {
// Topology could not be resolved, adding ourselves a color converter
// to the topology might solve the problem
- insertColorConverter(topology, outputNodeId);
- if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL)))
+ insertColorConverter(topology.Get(), outputNodeId);
+ if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL)))
break;
}
- if (insertResizer(resolvedTopology))
+ if (insertResizer(resolvedTopology.Get()))
isNewTopology = true;
-
- // Get all output nodes and search for video output node.
- if (FAILED(resolvedTopology->GetOutputNodeCollection(&outputNodes)))
- break;
-
- DWORD elementCount = 0;
- if (FAILED(outputNodes->GetElementCount(&elementCount)))
- break;
-
- for (DWORD n = 0; n < elementCount; n++) {
- IUnknown *element = 0;
- IMFTopologyNode *node = 0;
- IUnknown *outputObject = 0;
- IMFTopologyNode *inputNode = 0;
- IMFTopologyNode *mftNode = 0;
- bool mftAdded = false;
-
- do {
- if (FAILED(outputNodes->GetElement(n, &element)))
- break;
-
- if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (void**)&node)))
- break;
-
- TOPOID id;
- if (FAILED(node->GetTopoNodeID(&id)))
- break;
-
- if (id != outputNodeId)
- break;
-
- if (FAILED(node->GetObject(&outputObject)))
- break;
-
- m_videoProbeMFT->setVideoSink(outputObject);
-
- // Insert MFT between the output node and the node connected to it.
- DWORD outputIndex = 0;
- if (FAILED(node->GetInput(0, &inputNode, &outputIndex)))
- break;
-
- if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
- break;
-
- if (FAILED(mftNode->SetObject(m_videoProbeMFT)))
- break;
-
- if (FAILED(resolvedTopology->AddNode(mftNode)))
- break;
-
- if (FAILED(inputNode->ConnectOutput(0, mftNode, 0)))
- break;
-
- if (FAILED(mftNode->ConnectOutput(0, node, 0)))
- break;
-
- mftAdded = true;
- isNewTopology = true;
- } while (false);
-
- if (mftNode)
- mftNode->Release();
- if (inputNode)
- inputNode->Release();
- if (node)
- node->Release();
- if (element)
- element->Release();
- if (outputObject)
- outputObject->Release();
-
- if (mftAdded)
- break;
- else
- m_videoProbeMFT->setVideoSink(NULL);
- }
} while (false);
- if (outputNodes)
- outputNodes->Release();
-
- if (topoLoader)
- topoLoader->Release();
-
if (isNewTopology) {
- topology->Release();
return resolvedTopology;
}
- if (resolvedTopology)
- resolvedTopology->Release();
-
return topology;
}
@@ -854,26 +596,20 @@ bool MFPlayerSession::insertResizer(IMFTopology *topology)
{
bool inserted = false;
WORD elementCount = 0;
- IMFTopologyNode *node = 0;
- IUnknown *object = 0;
- IWMColorConvProps *colorConv = 0;
- IMFTransform *resizer = 0;
- IMFTopologyNode *resizerNode = 0;
- IMFTopologyNode *inputNode = 0;
+ ComPtr<IMFTopologyNode> node;
+ ComPtr<IUnknown> object;
+ ComPtr<IWMColorConvProps> colorConv;
+ ComPtr<IMFTransform> resizer;
+ ComPtr<IMFTopologyNode> resizerNode;
+ ComPtr<IMFTopologyNode> inputNode;
HRESULT hr = topology->GetNodeCount(&elementCount);
if (FAILED(hr))
return false;
for (WORD i = 0; i < elementCount; ++i) {
- if (node) {
- node->Release();
- node = 0;
- }
- if (object) {
- object->Release();
- object = 0;
- }
+ node.Reset();
+ object.Reset();
if (FAILED(topology->GetNode(i, &node)))
break;
@@ -888,35 +624,36 @@ bool MFPlayerSession::insertResizer(IMFTopology *topology)
if (FAILED(node->GetObject(&object)))
break;
- if (FAILED(object->QueryInterface(&colorConv)))
+ if (FAILED(object->QueryInterface(IID_PPV_ARGS(&colorConv))))
continue;
- if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&resizer)))
+ if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform,
+ &resizer)))
break;
if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &resizerNode)))
break;
- if (FAILED(resizerNode->SetObject(resizer)))
+ if (FAILED(resizerNode->SetObject(resizer.Get())))
break;
- if (FAILED(topology->AddNode(resizerNode)))
+ if (FAILED(topology->AddNode(resizerNode.Get())))
break;
DWORD outputIndex = 0;
if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) {
- topology->RemoveNode(resizerNode);
+ topology->RemoveNode(resizerNode.Get());
break;
}
- if (FAILED(inputNode->ConnectOutput(0, resizerNode, 0))) {
- topology->RemoveNode(resizerNode);
+ if (FAILED(inputNode->ConnectOutput(0, resizerNode.Get(), 0))) {
+ topology->RemoveNode(resizerNode.Get());
break;
}
- if (FAILED(resizerNode->ConnectOutput(0, node, 0))) {
- inputNode->ConnectOutput(0, node, 0);
- topology->RemoveNode(resizerNode);
+ if (FAILED(resizerNode->ConnectOutput(0, node.Get(), 0))) {
+ inputNode->ConnectOutput(0, node.Get(), 0);
+ topology->RemoveNode(resizerNode.Get());
break;
}
@@ -924,19 +661,6 @@ bool MFPlayerSession::insertResizer(IMFTopology *topology)
break;
}
- if (node)
- node->Release();
- if (object)
- object->Release();
- if (colorConv)
- colorConv->Release();
- if (resizer)
- resizer->Release();
- if (resizerNode)
- resizerNode->Release();
- if (inputNode)
- inputNode->Release();
-
return inserted;
}
@@ -946,27 +670,27 @@ bool MFPlayerSession::insertResizer(IMFTopology *topology)
// for some reason it fails to do so in some cases, we then do it ourselves.
void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputNodeId)
{
- IMFCollection *outputNodes = 0;
+ ComPtr<IMFCollection> outputNodes;
if (FAILED(topology->GetOutputNodeCollection(&outputNodes)))
return;
DWORD elementCount = 0;
if (FAILED(outputNodes->GetElementCount(&elementCount)))
- goto done;
+ return;
for (DWORD n = 0; n < elementCount; n++) {
- IUnknown *element = 0;
- IMFTopologyNode *node = 0;
- IMFTopologyNode *inputNode = 0;
- IMFTopologyNode *mftNode = 0;
- IMFTransform *converter = 0;
+ ComPtr<IUnknown> element;
+ ComPtr<IMFTopologyNode> node;
+ ComPtr<IMFTopologyNode> inputNode;
+ ComPtr<IMFTopologyNode> mftNode;
+ ComPtr<IMFTransform> converter;
do {
if (FAILED(outputNodes->GetElement(n, &element)))
break;
- if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (void**)&node)))
+ if (FAILED(element->QueryInterface(IID_IMFTopologyNode, &node)))
break;
TOPOID id;
@@ -983,38 +707,24 @@ void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputN
if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
break;
- if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&converter)))
+ if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER,
+ IID_IMFTransform, &converter)))
break;
- if (FAILED(mftNode->SetObject(converter)))
+ if (FAILED(mftNode->SetObject(converter.Get())))
break;
- if (FAILED(topology->AddNode(mftNode)))
+ if (FAILED(topology->AddNode(mftNode.Get())))
break;
- if (FAILED(inputNode->ConnectOutput(0, mftNode, 0)))
+ if (FAILED(inputNode->ConnectOutput(0, mftNode.Get(), 0)))
break;
- if (FAILED(mftNode->ConnectOutput(0, node, 0)))
+ if (FAILED(mftNode->ConnectOutput(0, node.Get(), 0)))
break;
} while (false);
-
- if (mftNode)
- mftNode->Release();
- if (inputNode)
- inputNode->Release();
- if (node)
- node->Release();
- if (element)
- element->Release();
- if (converter)
- converter->Release();
}
-
-done:
- if (outputNodes)
- outputNodes->Release();
}
void MFPlayerSession::stop(bool immediate)
@@ -1040,7 +750,7 @@ void MFPlayerSession::stop(bool immediate)
positionChanged(0);
}
} else {
- emit error(QMediaPlayer::ResourceError, tr("Failed to stop."), true);
+ error(QMediaPlayer::ResourceError, tr("Failed to stop."), true);
}
}
}
@@ -1084,11 +794,8 @@ void MFPlayerSession::start()
if (SUCCEEDED(m_session->Start(&GUID_NULL, &varStart))) {
m_state.setCommand(CmdStart);
m_pendingState = CmdPending;
- m_lastSeekPos = m_position;
- m_lastSeekSysTime = MFGetSystemTime();
- m_altTiming = false;
} else {
- emit error(QMediaPlayer::ResourceError, tr("failed to start playback"), true);
+ error(QMediaPlayer::ResourceError, tr("failed to start playback"), true);
}
PropVariantClear(&varStart);
}
@@ -1109,7 +816,7 @@ void MFPlayerSession::pause()
m_state.setCommand(CmdPause);
m_pendingState = CmdPending;
} else {
- emit error(QMediaPlayer::ResourceError, tr("Failed to pause."), false);
+ error(QMediaPlayer::ResourceError, tr("Failed to pause."), false);
}
if (m_status == QMediaPlayer::EndOfMedia) {
setPosition(0);
@@ -1126,7 +833,7 @@ void MFPlayerSession::changeStatus(QMediaPlayer::MediaStatus newStatus)
qDebug() << "MFPlayerSession::changeStatus" << newStatus;
#endif
m_status = newStatus;
- emit statusChanged();
+ statusChanged();
}
QMediaPlayer::MediaStatus MFPlayerSession::status() const
@@ -1143,27 +850,25 @@ bool MFPlayerSession::createSession()
HRESULT hr = MFCreateMediaSession(NULL, &m_session);
if (FAILED(hr)) {
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Unable to create mediasession."), true);
+ error(QMediaPlayer::ResourceError, tr("Unable to create mediasession."), true);
return false;
}
- m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ m_hCloseEvent = EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
- hr = m_session->BeginGetEvent(this, m_session);
+ hr = m_session->BeginGetEvent(this, m_session.Get());
if (FAILED(hr)) {
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::ResourceError, tr("Unable to pull session events."), false);
+ error(QMediaPlayer::ResourceError, tr("Unable to pull session events."), false);
close();
return false;
}
- m_sourceResolver = new SourceResolver();
- QObject::connect(m_sourceResolver, &SourceResolver::mediaSourceReady, this, &MFPlayerSession::handleMediaSourceReady);
- QObject::connect(m_sourceResolver, &SourceResolver::error, this, &MFPlayerSession::handleSourceError);
-
- m_videoProbeMFT = new MFTransform;
-// for (int i = 0; i < m_videoProbes.size(); ++i)
-// m_videoProbeMFT->addProbe(m_videoProbes.at(i));
+ m_sourceResolver = makeComObject<SourceResolver>();
+ QObject::connect(m_sourceResolver.Get(), &SourceResolver::mediaSourceReady, this,
+ &MFPlayerSession::handleMediaSourceReady);
+ QObject::connect(m_sourceResolver.Get(), &SourceResolver::error, this,
+ &MFPlayerSession::handleSourceError);
m_position = 0;
return true;
@@ -1184,13 +889,6 @@ qint64 MFPlayerSession::position()
MFTIME time, sysTime;
if (FAILED(m_presentationClock->GetCorrelatedTime(0, &time, &sysTime)))
return m_position / 10000;
-
- if (time > 0 && qint64(time) < m_lastSeekPos)
- m_altTiming = true;
-
- if (m_altTiming)
- return (m_lastSeekPos + MFGetSystemTime() - m_lastSeekSysTime) / 10000;
-
return qint64(time / 10000);
}
return m_position / 10000;
@@ -1231,19 +929,14 @@ void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd)
PROPVARIANT varStart;
varStart.vt = VT_I8;
varStart.hVal.QuadPart = LONGLONG(position * 10000);
- if (SUCCEEDED(m_session->Start(NULL, &varStart)))
- {
- m_lastSeekPos = position * 10000;
- m_lastSeekSysTime = MFGetSystemTime();
- m_altTiming = false;
-
+ if (SUCCEEDED(m_session->Start(NULL, &varStart))) {
PropVariantClear(&varStart);
// Store the pending state.
m_state.setCommand(CmdStart);
m_state.start = position;
m_pendingState = SeekPending;
} else {
- emit error(QMediaPlayer::ResourceError, tr("Failed to seek."), true);
+ error(QMediaPlayer::ResourceError, tr("Failed to seek."), true);
}
}
@@ -1258,7 +951,7 @@ void MFPlayerSession::setPlaybackRate(qreal rate)
{
if (m_scrubbing) {
m_restoreRate = rate;
- emit playbackRateChanged(rate);
+ playbackRateChanged(rate);
return;
}
setPlaybackRateInternal(rate);
@@ -1417,7 +1110,7 @@ done:
m_pendingRate = m_request.rate = m_state.rate = rate;
if (rate != 0)
m_state.isThin = isThin;
- emit playbackRateChanged(rate);
+ playbackRateChanged(rate);
}
void MFPlayerSession::scrub(bool enableScrub)
@@ -1551,17 +1244,22 @@ ULONG MFPlayerSession::AddRef(void)
ULONG MFPlayerSession::Release(void)
{
LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- this->deleteLater();
+ if (cRef == 0) {
+ deleteLater();
+
+ // In rare cases the session has queued events to be run between deleteLater and deleting,
+ // so we set the parent control to nullptr in order to prevent crashes in the cases.
+ m_playerControl = nullptr;
+ }
return cRef;
}
HRESULT MFPlayerSession::Invoke(IMFAsyncResult *pResult)
{
- if (pResult->GetStateNoAddRef() != m_session)
+ if (pResult->GetStateNoAddRef() != m_session.Get())
return S_OK;
- IMFMediaEvent *pEvent = NULL;
+ ComPtr<IMFMediaEvent> pEvent;
// Get the event from the event queue.
HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
if (FAILED(hr)) {
@@ -1571,36 +1269,30 @@ HRESULT MFPlayerSession::Invoke(IMFAsyncResult *pResult)
MediaEventType meType = MEUnknown;
hr = pEvent->GetType(&meType);
if (FAILED(hr)) {
- pEvent->Release();
return S_OK;
}
if (meType == MESessionClosed) {
- SetEvent(m_hCloseEvent);
- pEvent->Release();
+ SetEvent(m_hCloseEvent.get());
return S_OK;
} else {
- hr = m_session->BeginGetEvent(this, m_session);
+ hr = m_session->BeginGetEvent(this, m_session.Get());
if (FAILED(hr)) {
- pEvent->Release();
return S_OK;
}
}
if (!m_closing) {
emit sessionEvent(pEvent);
- } else {
- pEvent->Release();
}
return S_OK;
}
-void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
+void MFPlayerSession::handleSessionEvent(const ComPtr<IMFMediaEvent> &sessionEvent)
{
HRESULT hrStatus = S_OK;
HRESULT hr = sessionEvent->GetStatus(&hrStatus);
if (FAILED(hr) || !m_session) {
- sessionEvent->Release();
return;
}
@@ -1620,7 +1312,7 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
sessionEvent->GetValue(&var);
qWarning() << "handleSessionEvent: non fatal error = " << var.ulVal;
PropVariantClear(&var);
- emit error(QMediaPlayer::ResourceError, tr("Media session non-fatal error."), false);
+ error(QMediaPlayer::ResourceError, tr("Media session non-fatal error."), false);
}
break;
case MESourceUnknown:
@@ -1636,8 +1328,28 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
break;
}
changeStatus(QMediaPlayer::InvalidMedia);
- qWarning() << "handleSessionEvent: serious error = " << hrStatus;
- emit error(QMediaPlayer::ResourceError, tr("Media session serious error."), true);
+ qWarning() << "handleSessionEvent: serious error = "
+ << Qt::showbase << Qt::hex << Qt::uppercasedigits << static_cast<quint32>(hrStatus);
+ switch (hrStatus) {
+ case MF_E_NET_READ:
+ error(QMediaPlayer::NetworkError, tr("Error reading from the network."), true);
+ break;
+ case MF_E_NET_WRITE:
+ error(QMediaPlayer::NetworkError, tr("Error writing to the network."), true);
+ break;
+ case NS_E_FIREWALL:
+ error(QMediaPlayer::NetworkError, tr("Network packets might be blocked by a firewall."), true);
+ break;
+ case MF_E_MEDIAPROC_WRONGSTATE:
+ error(QMediaPlayer::ResourceError, tr("Media session state error."), true);
+ break;
+ case MF_E_INVALID_STREAM_DATA:
+ error(QMediaPlayer::ResourceError, tr("Invalid stream data."), true);
+ break;
+ default:
+ error(QMediaPlayer::ResourceError, tr("Media session serious error."), true);
+ break;
+ }
break;
case MESessionRateChanged:
// If the rate change succeeded, we've already got the rate
@@ -1648,7 +1360,7 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
if (SUCCEEDED(sessionEvent->GetValue(&var)) && (var.vt == VT_R4)) {
m_state.rate = var.fltVal;
}
- emit playbackRateChanged(playbackRate());
+ playbackRateChanged(playbackRate());
}
break;
case MESessionScrubSampleComplete :
@@ -1702,28 +1414,8 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
case MESessionTopologySet:
if (FAILED(hrStatus)) {
changeStatus(QMediaPlayer::InvalidMedia);
- emit error(QMediaPlayer::FormatError, tr("Unsupported media, a codec is missing."), true);
+ error(QMediaPlayer::FormatError, tr("Unsupported media, a codec is missing."), true);
} else {
- if (m_audioSampleGrabberNode) {
- IUnknown *obj = 0;
- if (SUCCEEDED(m_audioSampleGrabberNode->GetObject(&obj))) {
- IMFStreamSink *streamSink = 0;
- if (SUCCEEDED(obj->QueryInterface(IID_PPV_ARGS(&streamSink)))) {
- IMFMediaTypeHandler *typeHandler = 0;
- if (SUCCEEDED(streamSink->GetMediaTypeHandler((&typeHandler)))) {
- IMFMediaType *mediaType = 0;
- if (SUCCEEDED(typeHandler->GetCurrentMediaType(&mediaType))) {
- m_audioSampleGrabber->setFormat(QWindowsAudioUtils::mediaTypeToFormat(mediaType));
- mediaType->Release();
- }
- typeHandler->Release();
- }
- streamSink->Release();
- }
- obj->Release();
- }
- }
-
// Topology is resolved and successfuly set, this happens only after loading a new media.
// Make sure we always start the media from the beginning
m_lastPosition = -1;
@@ -1735,18 +1427,17 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
}
if (FAILED(hrStatus)) {
- sessionEvent->Release();
return;
}
switch (meType) {
case MEBufferingStarted:
changeStatus(QMediaPlayer::StalledMedia);
- emit bufferProgressChanged(bufferProgress());
+ bufferProgressChanged(bufferProgress());
break;
case MEBufferingStopped:
changeStatus(QMediaPlayer::BufferedMedia);
- emit bufferProgressChanged(bufferProgress());
+ bufferProgressChanged(bufferProgress());
break;
case MESessionEnded:
m_pendingState = NoPending;
@@ -1767,14 +1458,15 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
UINT32 status;
if (SUCCEEDED(sessionEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status))) {
if (status == MF_TOPOSTATUS_READY) {
- IMFClock* clock;
+ ComPtr<IMFClock> clock;
if (SUCCEEDED(m_session->GetClock(&clock))) {
- clock->QueryInterface(IID_IMFPresentationClock, (void**)(&m_presentationClock));
- clock->Release();
+ clock->QueryInterface(IID_IMFPresentationClock, &m_presentationClock);
}
- if (SUCCEEDED(MFGetService(m_session, MF_RATE_CONTROL_SERVICE, IID_PPV_ARGS(&m_rateControl)))) {
- if (SUCCEEDED(MFGetService(m_session, MF_RATE_CONTROL_SERVICE, IID_PPV_ARGS(&m_rateSupport)))) {
+ if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
+ IID_PPV_ARGS(&m_rateControl)))) {
+ if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
+ IID_PPV_ARGS(&m_rateSupport)))) {
if (SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL)))
m_canScrub = true;
}
@@ -1787,9 +1479,11 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
}
}
}
- MFGetService(m_session, MFNETSOURCE_STATISTICS_SERVICE, IID_PPV_ARGS(&m_netsourceStatistics));
+ MFGetService(m_session.Get(), MFNETSOURCE_STATISTICS_SERVICE,
+ IID_PPV_ARGS(&m_netsourceStatistics));
- if (SUCCEEDED(MFGetService(m_session, MR_STREAM_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl))))
+ if (SUCCEEDED(MFGetService(m_session.Get(), MR_STREAM_VOLUME_SERVICE,
+ IID_PPV_ARGS(&m_volumeControl))))
setVolumeInternal(m_muted ? 0 : m_volume);
m_updatingTopology = false;
@@ -1801,8 +1495,6 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
default:
break;
}
-
- sessionEvent->Release();
}
void MFPlayerSession::updatePendingCommands(Command command)
@@ -1889,33 +1581,14 @@ void MFPlayerSession::clear()
if (!m_metaData.isEmpty()) {
m_metaData.clear();
- emit metaDataChanged();
+ metaDataChanged();
}
- if (m_presentationClock) {
- m_presentationClock->Release();
- m_presentationClock = NULL;
- }
- if (m_rateControl) {
- m_rateControl->Release();
- m_rateControl = NULL;
- }
- if (m_rateSupport) {
- m_rateSupport->Release();
- m_rateSupport = NULL;
- }
- if (m_volumeControl) {
- m_volumeControl->Release();
- m_volumeControl = NULL;
- }
- if (m_netsourceStatistics) {
- m_netsourceStatistics->Release();
- m_netsourceStatistics = NULL;
- }
- if (m_audioSampleGrabberNode) {
- m_audioSampleGrabberNode->Release();
- m_audioSampleGrabberNode = NULL;
- }
+ m_presentationClock.Reset();
+ m_rateControl.Reset();
+ m_rateSupport.Reset();
+ m_volumeControl.Reset();
+ m_netsourceStatistics.Reset();
}
void MFPlayerSession::setAudioOutput(QPlatformAudioOutput *device)
@@ -1968,7 +1641,7 @@ void MFPlayerSession::setActiveTrack(QPlatformMediaPlayer::TrackType type, int i
if (m_trackInfo[QPlatformMediaPlayer::VideoStream].format == MFVideoFormat_HEVC)
return;
- IMFTopology *topology = nullptr;
+ ComPtr<IMFTopology> topology;
if (SUCCEEDED(m_session->GetFullTopology(QMM_MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology))) {
@@ -1978,25 +1651,23 @@ void MFPlayerSession::setActiveTrack(QPlatformMediaPlayer::TrackType type, int i
stop();
if (m_trackInfo[type].outputNodeId != TOPOID(-1)) {
- IMFTopologyNode *node = nullptr;
+ ComPtr<IMFTopologyNode> node;
if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].outputNodeId, &node))) {
- topology->RemoveNode(node);
- node->Release();
+ topology->RemoveNode(node.Get());
m_trackInfo[type].outputNodeId = TOPOID(-1);
}
}
if (m_trackInfo[type].sourceNodeId != TOPOID(-1)) {
- IMFTopologyNode *node = nullptr;
+ ComPtr<IMFTopologyNode> node;
if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].sourceNodeId, &node))) {
- topology->RemoveNode(node);
- node->Release();
+ topology->RemoveNode(node.Get());
m_trackInfo[type].sourceNodeId = TOPOID(-1);
}
}
IMFMediaSource *mediaSource = m_sourceResolver->mediaSource();
- IMFPresentationDescriptor *sourcePD = nullptr;
+ ComPtr<IMFPresentationDescriptor> sourcePD;
if (SUCCEEDED(mediaSource->CreatePresentationDescriptor(&sourcePD))) {
if (m_trackInfo[type].currentIndex >= 0 && m_trackInfo[type].currentIndex < nativeIndexes.count())
@@ -2005,35 +1676,33 @@ void MFPlayerSession::setActiveTrack(QPlatformMediaPlayer::TrackType type, int i
m_trackInfo[type].currentIndex = index;
if (index == -1) {
- m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
+ m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
} else {
int nativeIndex = nativeIndexes.at(index);
sourcePD->SelectStream(nativeIndex);
- IMFStreamDescriptor *streamDesc = nullptr;
+ ComPtr<IMFStreamDescriptor> streamDesc;
BOOL selected = FALSE;
if (SUCCEEDED(sourcePD->GetStreamDescriptorByIndex(nativeIndex, &selected, &streamDesc))) {
- IMFTopologyNode *sourceNode = addSourceNode(topology, mediaSource, sourcePD, streamDesc);
+ ComPtr<IMFTopologyNode> sourceNode = addSourceNode(
+ topology.Get(), mediaSource, sourcePD.Get(), streamDesc.Get());
if (sourceNode) {
- IMFTopologyNode *outputNode = addOutputNode(MFPlayerSession::Audio, topology, 0);
+ ComPtr<IMFTopologyNode> outputNode =
+ addOutputNode(MFPlayerSession::Audio, topology.Get(), 0);
if (outputNode) {
- if (SUCCEEDED(sourceNode->ConnectOutput(0, outputNode, 0))) {
+ if (SUCCEEDED(sourceNode->ConnectOutput(0, outputNode.Get(), 0))) {
sourceNode->GetTopoNodeID(&m_trackInfo[type].sourceNodeId);
outputNode->GetTopoNodeID(&m_trackInfo[type].outputNodeId);
- m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
+ m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE,
+ topology.Get());
}
- outputNode->Release();
}
- sourceNode->Release();
}
- streamDesc->Release();
}
}
m_updatingTopology = true;
- sourcePD->Release();
}
- topology->Release();
}
}
@@ -2062,3 +1731,6 @@ QMediaMetaData MFPlayerSession::trackMetaData(QPlatformMediaPlayer::TrackType ty
return m_trackInfo[type].metaData.at(trackNumber);
}
+QT_END_NAMESPACE
+
+#include "moc_mfplayersession_p.cpp"
diff --git a/src/plugins/multimedia/windows/player/mfplayersession_p.h b/src/plugins/multimedia/windows/player/mfplayersession_p.h
index 1f3555ba8..50141a7fb 100644
--- a/src/plugins/multimedia/windows/player/mfplayersession_p.h
+++ b/src/plugins/multimedia/windows/player/mfplayersession_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFPLAYERSESSION_H
#define MFPLAYERSESSION_H
@@ -66,12 +30,12 @@
#include <qaudiodevice.h>
#include <qtimer.h>
#include "mfplayercontrol_p.h"
+#include <private/qcomptr_p.h>
+#include <evrhelpers_p.h>
QT_BEGIN_NAMESPACE
-class QUrl;
-QT_END_NAMESPACE
-QT_USE_NAMESPACE
+class QUrl;
class SourceResolver;
class MFVideoRendererControl;
@@ -86,7 +50,6 @@ class MFPlayerSession : public QObject, public IMFAsyncCallback
friend class SourceResolver;
public:
MFPlayerSession(MFPlayerControl *playerControl = 0);
- ~MFPlayerSession();
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override;
@@ -151,11 +114,11 @@ public Q_SLOTS:
void updateOutputRouting();
Q_SIGNALS:
- void sessionEvent(IMFMediaEvent *sessionEvent);
+ void sessionEvent(const ComPtr<IMFMediaEvent> &sessionEvent);
private Q_SLOTS:
void handleMediaSourceReady();
- void handleSessionEvent(IMFMediaEvent *sessionEvent);
+ void handleSessionEvent(const ComPtr<IMFMediaEvent> &sessionEvent);
void handleSourceError(long hr);
void timeout();
@@ -163,20 +126,17 @@ private:
long m_cRef;
MFPlayerControl *m_playerControl = nullptr;
MFVideoRendererControl *m_videoRendererControl = nullptr;
- IMFMediaSession *m_session;
- IMFPresentationClock *m_presentationClock;
- IMFRateControl *m_rateControl;
- IMFRateSupport *m_rateSupport;
- IMFAudioStreamVolume *m_volumeControl;
- IPropertyStore *m_netsourceStatistics;
+ ComPtr<IMFMediaSession> m_session;
+ ComPtr<IMFPresentationClock> m_presentationClock;
+ ComPtr<IMFRateControl> m_rateControl;
+ ComPtr<IMFRateSupport> m_rateSupport;
+ ComPtr<IMFAudioStreamVolume> m_volumeControl;
+ ComPtr<IPropertyStore> m_netsourceStatistics;
qint64 m_position = 0;
qint64 m_restorePosition = -1;
qint64 m_timeCounter = 0;
UINT64 m_duration = 0;
bool m_updatingTopology = false;
- qint64 m_lastSeekPos = 0;
- MFTIME m_lastSeekSysTime = 0;
- bool m_altTiming = false;
bool m_updateRoutingOnStart = false;
enum Command
@@ -199,8 +159,8 @@ private:
bool m_scrubbing;
float m_restoreRate;
- SourceResolver *m_sourceResolver;
- HANDLE m_hCloseEvent;
+ ComPtr<SourceResolver> m_sourceResolver;
+ EventHandle m_hCloseEvent;
bool m_closing;
enum MediaType
@@ -260,26 +220,21 @@ private:
bool createSession();
void setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD);
bool getStreamInfo(IMFStreamDescriptor *stream, MFPlayerSession::MediaType *type, QString *name, QString *language, GUID *format) const;
- IMFTopologyNode* addSourceNode(IMFTopology* topology, IMFMediaSource* source,
- IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc);
- IMFTopologyNode* addOutputNode(MediaType mediaType, IMFTopology* topology, DWORD sinkID);
+ ComPtr<IMFTopologyNode> addSourceNode(IMFTopology *topology, IMFMediaSource *source,
+ IMFPresentationDescriptor *presentationDesc,
+ IMFStreamDescriptor *streamDesc);
+ ComPtr<IMFTopologyNode> addOutputNode(MediaType mediaType, IMFTopology *topology, DWORD sinkID);
- bool addAudioSampleGrabberNode(IMFTopology* topology);
- bool setupAudioSampleGrabber(IMFTopology *topology, IMFTopologyNode *sourceNode, IMFTopologyNode *outputNode);
QAudioFormat audioFormatForMFMediaType(IMFMediaType *mediaType) const;
- // ### Below can be used to monitor the audio channel. Currently unused.
- AudioSampleGrabberCallback *m_audioSampleGrabber;
- IMFTopologyNode *m_audioSampleGrabberNode;
- IMFTopology *insertMFT(IMFTopology *topology, TOPOID outputNodeId);
+ ComPtr<IMFTopology> insertMFT(const ComPtr<IMFTopology> &topology, TOPOID outputNodeId);
bool insertResizer(IMFTopology *topology);
void insertColorConverter(IMFTopology *topology, TOPOID outputNodeId);
- // ### Below can be used to monitor the video channel. Functionality currently unused.
- MFTransform *m_videoProbeMFT;
QTimer m_signalPositionChangeTimer;
qint64 m_lastPosition = -1;
};
+QT_END_NAMESPACE
#endif
diff --git a/src/plugins/multimedia/windows/player/mftvideo.cpp b/src/plugins/multimedia/windows/player/mftvideo.cpp
deleted file mode 100644
index fcc8d17c5..000000000
--- a/src/plugins/multimedia/windows/player/mftvideo.cpp
+++ /dev/null
@@ -1,728 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "mftvideo_p.h"
-#include <private/qmemoryvideobuffer_p.h>
-#include <private/qwindowsmultimediautils_p.h>
-#include <mferror.h>
-#include <strmif.h>
-#include <uuids.h>
-#include <InitGuid.h>
-#include <d3d9.h>
-#include <qdebug.h>
-
-// This MFT sends all samples it processes to connected video probes.
-// Sample is sent to probes in ProcessInput.
-// In ProcessOutput this MFT simply returns the original sample.
-
-// The implementation is based on a boilerplate from the MF SDK example.
-
-MFTransform::MFTransform():
- m_cRef(1),
- m_inputType(0),
- m_outputType(0),
- m_sample(0),
- m_videoSinkTypeHandler(0),
- m_bytesPerLine(0)
-{
-}
-
-MFTransform::~MFTransform()
-{
- if (m_inputType)
- m_inputType->Release();
-
- if (m_outputType)
- m_outputType->Release();
-
- if (m_videoSinkTypeHandler)
- m_videoSinkTypeHandler->Release();
-}
-
-//void MFTransform::addProbe(MFVideoProbeControl *probe)
-//{
-// QMutexLocker locker(&m_videoProbeMutex);
-
-// if (m_videoProbes.contains(probe))
-// return;
-
-// m_videoProbes.append(probe);
-//}
-
-//void MFTransform::removeProbe(MFVideoProbeControl *probe)
-//{
-// QMutexLocker locker(&m_videoProbeMutex);
-// m_videoProbes.removeOne(probe);
-//}
-
-void MFTransform::setVideoSink(IUnknown *videoSink)
-{
- // This transform supports the same input types as the video sink.
- // Store its type handler interface in order to report the correct supported types.
-
- if (m_videoSinkTypeHandler) {
- m_videoSinkTypeHandler->Release();
- m_videoSinkTypeHandler = NULL;
- }
-
- if (videoSink)
- videoSink->QueryInterface(IID_PPV_ARGS(&m_videoSinkTypeHandler));
-}
-
-STDMETHODIMP MFTransform::QueryInterface(REFIID riid, void** ppv)
-{
- if (!ppv)
- return E_POINTER;
- if (riid == IID_IMFTransform) {
- *ppv = static_cast<IMFTransform*>(this);
- } else if (riid == IID_IUnknown) {
- *ppv = static_cast<IUnknown*>(this);
- } else {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-STDMETHODIMP_(ULONG) MFTransform::AddRef()
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-STDMETHODIMP_(ULONG) MFTransform::Release()
-{
- ULONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0) {
- delete this;
- }
- return cRef;
-}
-
-STDMETHODIMP MFTransform::GetStreamLimits(DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum)
-{
- if (!pdwInputMinimum || !pdwInputMaximum || !pdwOutputMinimum || !pdwOutputMaximum)
- return E_POINTER;
- *pdwInputMinimum = 1;
- *pdwInputMaximum = 1;
- *pdwOutputMinimum = 1;
- *pdwOutputMaximum = 1;
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::GetStreamCount(DWORD *pcInputStreams, DWORD *pcOutputStreams)
-{
- if (!pcInputStreams || !pcOutputStreams)
- return E_POINTER;
-
- *pcInputStreams = 1;
- *pcOutputStreams = 1;
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::GetStreamIDs(DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs)
-{
- // streams are numbered consecutively
- Q_UNUSED(dwInputIDArraySize);
- Q_UNUSED(pdwInputIDs);
- Q_UNUSED(dwOutputIDArraySize);
- Q_UNUSED(pdwOutputIDs);
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFTransform::GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO *pStreamInfo)
-{
- QMutexLocker locker(&m_mutex);
-
- if (dwInputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (!pStreamInfo)
- return E_POINTER;
-
- pStreamInfo->cbSize = 0;
- pStreamInfo->hnsMaxLatency = 0;
- pStreamInfo->cbMaxLookahead = 0;
- pStreamInfo->cbAlignment = 0;
- pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES
- | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER
- | MFT_INPUT_STREAM_PROCESSES_IN_PLACE;
-
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO *pStreamInfo)
-{
- QMutexLocker locker(&m_mutex);
-
- if (dwOutputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (!pStreamInfo)
- return E_POINTER;
-
- pStreamInfo->cbSize = 0;
- pStreamInfo->cbAlignment = 0;
- pStreamInfo->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES
- | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER
- | MFT_OUTPUT_STREAM_PROVIDES_SAMPLES
- | MFT_OUTPUT_STREAM_DISCARDABLE;
-
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::GetAttributes(IMFAttributes **pAttributes)
-{
- // This MFT does not support attributes.
- Q_UNUSED(pAttributes);
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFTransform::GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes **pAttributes)
-{
- // This MFT does not support input stream attributes.
- Q_UNUSED(dwInputStreamID);
- Q_UNUSED(pAttributes);
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFTransform::GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes **pAttributes)
-{
- // This MFT does not support output stream attributes.
- Q_UNUSED(dwOutputStreamID);
- Q_UNUSED(pAttributes);
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFTransform::DeleteInputStream(DWORD dwStreamID)
-{
- // This MFT has a fixed number of input streams.
- Q_UNUSED(dwStreamID);
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFTransform::AddInputStreams(DWORD cStreams, DWORD *adwStreamIDs)
-{
- // This MFT has a fixed number of input streams.
- Q_UNUSED(cStreams);
- Q_UNUSED(adwStreamIDs);
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFTransform::GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType)
-{
- // We support the same input types as the video sink
- if (!m_videoSinkTypeHandler)
- return E_NOTIMPL;
-
- if (dwInputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (!ppType)
- return E_POINTER;
-
- return m_videoSinkTypeHandler->GetMediaTypeByIndex(dwTypeIndex, ppType);
-}
-
-STDMETHODIMP MFTransform::GetOutputAvailableType(DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType)
-{
- // Since we don't modify the samples, the output type must be the same as the input type.
- // Report our input type as the only available output type.
-
- if (dwOutputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (!ppType)
- return E_POINTER;
-
- // Input type must be set first
- if (!m_inputType)
- return MF_E_TRANSFORM_TYPE_NOT_SET;
-
- if (dwTypeIndex > 0)
- return MF_E_NO_MORE_TYPES;
-
- // Return a copy to make sure our type is not modified
- if (FAILED(MFCreateMediaType(ppType)))
- return E_OUTOFMEMORY;
-
- return m_inputType->CopyAllItems(*ppType);
-}
-
-STDMETHODIMP MFTransform::SetInputType(DWORD dwInputStreamID, IMFMediaType *pType, DWORD dwFlags)
-{
- if (dwInputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- QMutexLocker locker(&m_mutex);
-
- if (m_sample)
- return MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING;
-
- if (!isMediaTypeSupported(pType))
- return MF_E_INVALIDMEDIATYPE;
-
- if (dwFlags == MFT_SET_TYPE_TEST_ONLY)
- return pType ? S_OK : E_POINTER;
-
- if (m_inputType) {
- m_inputType->Release();
- // Input type has changed, discard output type (if it's set) so it's reset later on
- DWORD flags = 0;
- if (m_outputType && m_outputType->IsEqual(pType, &flags) != S_OK) {
- m_outputType->Release();
- m_outputType = 0;
- }
- }
-
- m_inputType = pType;
-
- if (m_inputType)
- m_inputType->AddRef();
-
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::SetOutputType(DWORD dwOutputStreamID, IMFMediaType *pType, DWORD dwFlags)
-{
- if (dwOutputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (dwFlags == MFT_SET_TYPE_TEST_ONLY && !pType)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
-
- // Input type must be set first
- if (!m_inputType)
- return MF_E_TRANSFORM_TYPE_NOT_SET;
-
- if (m_sample)
- return MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING;
-
- DWORD flags = 0;
- if (pType && m_inputType->IsEqual(pType, &flags) != S_OK)
- return MF_E_INVALIDMEDIATYPE;
-
- if (dwFlags == MFT_SET_TYPE_TEST_ONLY)
- return pType ? S_OK : E_POINTER;
-
- if (m_outputType)
- m_outputType->Release();
-
- m_outputType = pType;
-
- if (m_outputType) {
- m_outputType->AddRef();
- m_format = videoFormatForMFMediaType(m_outputType, &m_bytesPerLine);
- }
-
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::GetInputCurrentType(DWORD dwInputStreamID, IMFMediaType **ppType)
-{
- if (dwInputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (ppType == NULL)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
-
- if (!m_inputType)
- return MF_E_TRANSFORM_TYPE_NOT_SET;
-
- // Return a copy to make sure our type is not modified
- if (FAILED(MFCreateMediaType(ppType)))
- return E_OUTOFMEMORY;
-
- return m_inputType->CopyAllItems(*ppType);
-}
-
-STDMETHODIMP MFTransform::GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType)
-{
- if (dwOutputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (ppType == NULL)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
-
- if (!m_outputType)
- return MF_E_TRANSFORM_TYPE_NOT_SET;
-
- // Return a copy to make sure our type is not modified
- if (FAILED(MFCreateMediaType(ppType)))
- return E_OUTOFMEMORY;
-
- return m_outputType->CopyAllItems(*ppType);
-}
-
-STDMETHODIMP MFTransform::GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags)
-{
- if (dwInputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (!pdwFlags)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
-
- if (!m_inputType || !m_outputType)
- return MF_E_TRANSFORM_TYPE_NOT_SET;
-
- if (m_sample)
- *pdwFlags = 0;
- else
- *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA;
-
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::GetOutputStatus(DWORD *pdwFlags)
-{
- if (!pdwFlags)
- return E_POINTER;
-
- QMutexLocker locker(&m_mutex);
-
- if (!m_inputType || !m_outputType)
- return MF_E_TRANSFORM_TYPE_NOT_SET;
-
- if (m_sample)
- *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY;
- else
- *pdwFlags = 0;
-
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::SetOutputBounds(LONGLONG hnsLowerBound, LONGLONG hnsUpperBound)
-{
- Q_UNUSED(hnsLowerBound);
- Q_UNUSED(hnsUpperBound);
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFTransform::ProcessEvent(DWORD dwInputStreamID, IMFMediaEvent *pEvent)
-{
- // This MFT ignores all events, and the pipeline should send all events downstream.
- Q_UNUSED(dwInputStreamID);
- Q_UNUSED(pEvent);
- return E_NOTIMPL;
-}
-
-STDMETHODIMP MFTransform::ProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam)
-{
- Q_UNUSED(ulParam);
-
- HRESULT hr = S_OK;
-
- switch (eMessage)
- {
- case MFT_MESSAGE_COMMAND_FLUSH:
- hr = OnFlush();
- break;
-
- case MFT_MESSAGE_COMMAND_DRAIN:
- // Drain: Tells the MFT not to accept any more input until
- // all of the pending output has been processed. That is our
- // default behevior already, so there is nothing to do.
- break;
-
- case MFT_MESSAGE_SET_D3D_MANAGER:
- // The pipeline should never send this message unless the MFT
- // has the MF_SA_D3D_AWARE attribute set to TRUE. However, if we
- // do get this message, it's invalid and we don't implement it.
- hr = E_NOTIMPL;
- break;
-
- // The remaining messages do not require any action from this MFT.
- case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
- case MFT_MESSAGE_NOTIFY_END_STREAMING:
- case MFT_MESSAGE_NOTIFY_END_OF_STREAM:
- case MFT_MESSAGE_NOTIFY_START_OF_STREAM:
- break;
-
- default:
- break;
- }
-
- return hr;
-}
-
-STDMETHODIMP MFTransform::ProcessInput(DWORD dwInputStreamID, IMFSample *pSample, DWORD dwFlags)
-{
- if (dwInputStreamID > 0)
- return MF_E_INVALIDSTREAMNUMBER;
-
- if (dwFlags != 0)
- return E_INVALIDARG; // dwFlags is reserved and must be zero.
-
- QMutexLocker locker(&m_mutex);
-
- if (!m_inputType)
- return MF_E_TRANSFORM_TYPE_NOT_SET;
-
- if (m_sample)
- return MF_E_NOTACCEPTING;
-
- // Validate the number of buffers. There should only be a single buffer to hold the video frame.
- DWORD dwBufferCount = 0;
- HRESULT hr = pSample->GetBufferCount(&dwBufferCount);
- if (FAILED(hr))
- return hr;
-
- if (dwBufferCount == 0)
- return E_FAIL;
-
- if (dwBufferCount > 1)
- return MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS;
-
- m_sample = pSample;
- m_sample->AddRef();
-
- QMutexLocker lockerProbe(&m_videoProbeMutex);
-
-// if (!m_videoProbes.isEmpty()) {
-// QVideoFrame frame = makeVideoFrame();
-
-// for (MFVideoProbeControl* probe : qAsConst(m_videoProbes))
-// probe->bufferProbed(frame);
-// }
-
- return S_OK;
-}
-
-STDMETHODIMP MFTransform::ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus)
-{
- if (pOutputSamples == NULL || pdwStatus == NULL)
- return E_POINTER;
-
- if (cOutputBufferCount != 1)
- return E_INVALIDARG;
-
- QMutexLocker locker(&m_mutex);
-
- if (!m_inputType)
- return MF_E_TRANSFORM_TYPE_NOT_SET;
-
- if (!m_outputType) {
- pOutputSamples[0].dwStatus = MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE;
- return MF_E_TRANSFORM_STREAM_CHANGE;
- }
-
- IMFMediaBuffer *input = NULL;
- IMFMediaBuffer *output = NULL;
-
- if (dwFlags == MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER)
- goto done;
- else if (dwFlags != 0)
- return E_INVALIDARG;
-
- if (!m_sample)
- return MF_E_TRANSFORM_NEED_MORE_INPUT;
-
- // Since the MFT_OUTPUT_STREAM_PROVIDES_SAMPLES flag is set, the client
- // should not be providing samples here
- if (pOutputSamples[0].pSample != NULL)
- return E_INVALIDARG;
-
- pOutputSamples[0].pSample = m_sample;
- pOutputSamples[0].pSample->AddRef();
-
- // Send video frame to probes
- // We do it here (instead of inside ProcessInput) to make sure samples discarded by the renderer
- // are not sent.
- m_videoProbeMutex.lock();
-// if (!m_videoProbes.isEmpty()) {
-// QVideoFrame frame = makeVideoFrame();
-
-// for (MFVideoProbeControl* probe : qAsConst(m_videoProbes))
-// probe->bufferProbed(frame);
-// }
- m_videoProbeMutex.unlock();
-
-done:
- pOutputSamples[0].dwStatus = 0;
- *pdwStatus = 0;
-
- m_sample->Release();
- m_sample = 0;
-
- if (input)
- input->Release();
- if (output)
- output->Release();
-
- return S_OK;
-}
-
-HRESULT MFTransform::OnFlush()
-{
- QMutexLocker locker(&m_mutex);
-
- if (m_sample) {
- m_sample->Release();
- m_sample = 0;
- }
- return S_OK;
-}
-
-QVideoFrameFormat MFTransform::videoFormatForMFMediaType(IMFMediaType *mediaType, int *bytesPerLine)
-{
- UINT32 stride;
- if (FAILED(mediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride))) {
- *bytesPerLine = 0;
- return QVideoFrameFormat();
- }
-
- *bytesPerLine = (int)stride;
-
- QSize size;
- UINT32 width, height;
- if (FAILED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height)))
- return QVideoFrameFormat();
-
- size.setWidth(width);
- size.setHeight(height);
-
- GUID subtype = GUID_NULL;
- if (FAILED(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype)))
- return QVideoFrameFormat();
-
- QVideoFrameFormat::PixelFormat pixelFormat =
- QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
- QVideoFrameFormat format(size, pixelFormat);
-
- quint32 num, den;
- if (SUCCEEDED(MFGetAttributeRatio(mediaType, MF_MT_FRAME_RATE, &num, &den))) {
- format.setFrameRate(qreal(num)/den);
- }
-
- return format;
-}
-
-QVideoFrame MFTransform::makeVideoFrame()
-{
- QVideoFrame frame;
-
- if (!m_format.isValid())
- return frame;
-
- IMFMediaBuffer *buffer = 0;
-
- do {
- if (FAILED(m_sample->ConvertToContiguousBuffer(&buffer)))
- break;
-
- QByteArray array = dataFromBuffer(buffer, m_format.frameHeight(), &m_bytesPerLine);
- if (array.isEmpty())
- break;
-
- // Wrapping IMFSample or IMFMediaBuffer in a QVideoFrame is not possible because we cannot hold
- // IMFSample for a "long" time without affecting the rest of the topology.
- // If IMFSample is held for more than 5 frames decoder starts to reuse it even though it hasn't been released it yet.
- // That is why we copy data from IMFMediaBuffer here.
- frame = QVideoFrame(new QMemoryVideoBuffer(array, m_bytesPerLine), m_format);
-
- // WMF uses 100-nanosecond units, Qt uses microseconds
- LONGLONG startTime = -1;
- if (SUCCEEDED(m_sample->GetSampleTime(&startTime))) {
- frame.setStartTime(startTime * 0.1);
-
- LONGLONG duration = -1;
- if (SUCCEEDED(m_sample->GetSampleDuration(&duration)))
- frame.setEndTime((startTime + duration) * 0.1);
- }
- } while (false);
-
- if (buffer)
- buffer->Release();
-
- return frame;
-}
-
-QByteArray MFTransform::dataFromBuffer(IMFMediaBuffer *buffer, int height, int *bytesPerLine)
-{
- QByteArray array;
- BYTE *bytes;
- DWORD length;
- HRESULT hr = buffer->Lock(&bytes, NULL, &length);
- if (SUCCEEDED(hr)) {
- array = QByteArray((const char *)bytes, (int)length);
- buffer->Unlock();
- } else {
- // try to lock as Direct3DSurface
- IDirect3DSurface9 *surface = 0;
- do {
- if (FAILED(MFGetService(buffer, MR_BUFFER_SERVICE, IID_IDirect3DSurface9, (void**)&surface)))
- break;
-
- D3DLOCKED_RECT rect;
- if (FAILED(surface->LockRect(&rect, NULL, D3DLOCK_READONLY)))
- break;
-
- if (bytesPerLine)
- *bytesPerLine = (int)rect.Pitch;
-
- array = QByteArray((const char *)rect.pBits, rect.Pitch * height);
- surface->UnlockRect();
- } while (false);
-
- if (surface) {
- surface->Release();
- surface = 0;
- }
- }
-
- return array;
-}
-
-bool MFTransform::isMediaTypeSupported(IMFMediaType *type)
-{
- // If we don't have the video sink's type handler,
- // assume it supports anything...
- if (!m_videoSinkTypeHandler || !type)
- return true;
-
- return m_videoSinkTypeHandler->IsMediaTypeSupported(type, NULL) == S_OK;
-}
diff --git a/src/plugins/multimedia/windows/player/mftvideo_p.h b/src/plugins/multimedia/windows/player/mftvideo_p.h
deleted file mode 100644
index 6c0c2e6e7..000000000
--- a/src/plugins/multimedia/windows/player/mftvideo_p.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef MFTRANSFORM_H
-#define MFTRANSFORM_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <mfapi.h>
-#include <mfidl.h>
-#include <QtCore/qlist.h>
-#include <QtCore/qmutex.h>
-#include <QtMultimedia/qvideoframeformat.h>
-
-QT_USE_NAMESPACE
-
-class MFVideoProbeControl;
-
-QT_BEGIN_NAMESPACE
-class QVideoFrame;
-QT_END_NAMESPACE
-
-class MFTransform: public IMFTransform
-{
-public:
- MFTransform();
- virtual ~MFTransform();
-
- void addProbe(MFVideoProbeControl* probe);
- void removeProbe(MFVideoProbeControl* probe);
-
- void setVideoSink(IUnknown *videoSink);
-
- // IUnknown methods
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override;
- STDMETHODIMP_(ULONG) AddRef() override;
- STDMETHODIMP_(ULONG) Release() override;
-
- // IMFTransform methods
- STDMETHODIMP GetStreamLimits(DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum) override;
- STDMETHODIMP GetStreamCount(DWORD *pcInputStreams, DWORD *pcOutputStreams) override;
- STDMETHODIMP GetStreamIDs(DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs) override;
- STDMETHODIMP GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO *pStreamInfo) override;
- STDMETHODIMP GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO *pStreamInfo) override;
- STDMETHODIMP GetAttributes(IMFAttributes **pAttributes) override;
- STDMETHODIMP GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes **pAttributes) override;
- STDMETHODIMP GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes **pAttributes) override;
- STDMETHODIMP DeleteInputStream(DWORD dwStreamID) override;
- STDMETHODIMP AddInputStreams(DWORD cStreams, DWORD *adwStreamIDs) override;
- STDMETHODIMP GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType) override;
- STDMETHODIMP GetOutputAvailableType(DWORD dwOutputStreamID,DWORD dwTypeIndex, IMFMediaType **ppType) override;
- STDMETHODIMP SetInputType(DWORD dwInputStreamID, IMFMediaType *pType, DWORD dwFlags) override;
- STDMETHODIMP SetOutputType(DWORD dwOutputStreamID, IMFMediaType *pType, DWORD dwFlags) override;
- STDMETHODIMP GetInputCurrentType(DWORD dwInputStreamID, IMFMediaType **ppType) override;
- STDMETHODIMP GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType) override;
- STDMETHODIMP GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags) override;
- STDMETHODIMP GetOutputStatus(DWORD *pdwFlags) override;
- STDMETHODIMP SetOutputBounds(LONGLONG hnsLowerBound, LONGLONG hnsUpperBound) override;
- STDMETHODIMP ProcessEvent(DWORD dwInputStreamID, IMFMediaEvent *pEvent) override;
- STDMETHODIMP ProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) override;
- STDMETHODIMP ProcessInput(DWORD dwInputStreamID, IMFSample *pSample, DWORD dwFlags) override;
- STDMETHODIMP ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus) override;
-
-private:
- HRESULT OnFlush();
- static QVideoFrameFormat videoFormatForMFMediaType(IMFMediaType *mediaType, int *bytesPerLine);
- QVideoFrame makeVideoFrame();
- QByteArray dataFromBuffer(IMFMediaBuffer *buffer, int height, int *bytesPerLine);
- bool isMediaTypeSupported(IMFMediaType *type);
-
- long m_cRef;
- IMFMediaType *m_inputType;
- IMFMediaType *m_outputType;
- IMFSample *m_sample;
- QMutex m_mutex;
-
- IMFMediaTypeHandler *m_videoSinkTypeHandler;
-
-// QList<MFVideoProbeControl*> m_videoProbes;
- QMutex m_videoProbeMutex;
-
- QVideoFrameFormat m_format;
- int m_bytesPerLine;
-};
-
-#endif
diff --git a/src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp b/src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp
index af16663e0..7c79c3a8a 100644
--- a/src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp
+++ b/src/plugins/multimedia/windows/player/mfvideorenderercontrol.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "mfvideorenderercontrol_p.h"
#include "mfactivate_p.h"
@@ -43,2103 +7,28 @@
#include "evrcustompresenter_p.h"
#include <private/qplatformvideosink_p.h>
-#include <private/qabstractvideobuffer_p.h>
-#include <private/qwindowsmfdefs_p.h>
-#include <qvideosink.h>
-#include <qvideoframeformat.h>
-#include <qtimer.h>
-#include <qmutex.h>
-#include <qcoreevent.h>
-#include <qcoreapplication.h>
-#include <qthread.h>
-#include "guiddef.h"
-#include <qdebug.h>
-
-//#define DEBUG_MEDIAFOUNDATION
-#define PAD_TO_DWORD(x) (((x) + 3) & ~3)
-
-namespace
-{
- class MediaSampleVideoBuffer : public QAbstractVideoBuffer
- {
- public:
- MediaSampleVideoBuffer(IMFMediaBuffer *buffer, int bytesPerLine)
- : QAbstractVideoBuffer(QVideoFrame::NoHandle)
- , m_buffer(buffer)
- , m_bytesPerLine(bytesPerLine)
- , m_mapMode(QVideoFrame::NotMapped)
- {
- buffer->AddRef();
- }
-
- ~MediaSampleVideoBuffer()
- {
- m_buffer->Release();
- }
-
- MapData map(QVideoFrame::MapMode mode) override
- {
- MapData mapData;
- if (m_mapMode == QVideoFrame::NotMapped && mode != QVideoFrame::NotMapped) {
- BYTE *bytes;
- DWORD length;
- HRESULT hr = m_buffer->Lock(&bytes, NULL, &length);
- if (SUCCEEDED(hr)) {
- mapData.nPlanes = 1;
- mapData.bytesPerLine[0] = m_bytesPerLine;
- mapData.data[0] = reinterpret_cast<uchar *>(bytes);
- mapData.size[0] = qsizetype(length);
- m_mapMode = mode;
- } else {
- qWarning("Faild to lock mf buffer!");
- }
- }
- return mapData;
- }
-
- void unmap() override
- {
- if (m_mapMode == QVideoFrame::NotMapped)
- return;
- m_mapMode = QVideoFrame::NotMapped;
- m_buffer->Unlock();
- }
-
- QVideoFrame::MapMode mapMode() const override
- {
- return m_mapMode;
- }
-
- private:
- IMFMediaBuffer *m_buffer;
- int m_bytesPerLine;
- QVideoFrame::MapMode m_mapMode;
- };
-
- // Custom interface for handling IMFStreamSink::PlaceMarker calls asynchronously.
- static const GUID IID_IMarker = {0xa3ff32de, 0x1031, 0x438a, {0x8b, 0x47, 0x82, 0xf8, 0xac, 0xda, 0x59, 0xb7}};
- MIDL_INTERFACE("a3ff32de-1031-438a-8b47-82f8acda59b7")
- IMarker : public IUnknown
- {
- virtual STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType) = 0;
- virtual STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar) = 0;
- virtual STDMETHODIMP GetContext(PROPVARIANT *pvar) = 0;
- };
-
- class Marker : public IMarker
- {
- public:
- static HRESULT Create(
- MFSTREAMSINK_MARKER_TYPE eMarkerType,
- const PROPVARIANT* pvarMarkerValue, // Can be NULL.
- const PROPVARIANT* pvarContextValue, // Can be NULL.
- IMarker **ppMarker)
- {
- if (ppMarker == NULL)
- return E_POINTER;
-
- HRESULT hr = S_OK;
- Marker *pMarker = new Marker(eMarkerType);
- if (pMarker == NULL)
- hr = E_OUTOFMEMORY;
-
- // Copy the marker data.
- if (SUCCEEDED(hr) && pvarMarkerValue)
- hr = PropVariantCopy(&pMarker->m_varMarkerValue, pvarMarkerValue);
-
- if (SUCCEEDED(hr) && pvarContextValue)
- hr = PropVariantCopy(&pMarker->m_varContextValue, pvarContextValue);
-
- if (SUCCEEDED(hr)) {
- *ppMarker = pMarker;
- (*ppMarker)->AddRef();
- }
-
- if (pMarker)
- pMarker->Release();
-
- return hr;
- }
-
- // IUnknown methods.
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override
- {
- if (!ppv)
- return E_POINTER;
- if (iid == IID_IUnknown) {
- *ppv = static_cast<IUnknown*>(this);
- } else if (iid == IID_IMarker) {
- *ppv = static_cast<IMarker*>(this);
- } else {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
- }
-
- STDMETHODIMP_(ULONG) AddRef() override
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release() override
- {
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- delete this;
- // For thread safety, return a temporary variable.
- return cRef;
- }
-
- STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType) override
- {
- if (pType == NULL)
- return E_POINTER;
- *pType = m_eMarkerType;
- return S_OK;
- }
-
- STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar) override
- {
- if (pvar == NULL)
- return E_POINTER;
- return PropVariantCopy(pvar, &m_varMarkerValue);
- }
-
- STDMETHODIMP GetContext(PROPVARIANT *pvar) override
- {
- if (pvar == NULL)
- return E_POINTER;
- return PropVariantCopy(pvar, &m_varContextValue);
- }
-
- protected:
- MFSTREAMSINK_MARKER_TYPE m_eMarkerType;
- PROPVARIANT m_varMarkerValue;
- PROPVARIANT m_varContextValue;
-
- private:
- long m_cRef = 1;
-
- Marker(MFSTREAMSINK_MARKER_TYPE eMarkerType) : m_eMarkerType(eMarkerType)
- {
- PropVariantInit(&m_varMarkerValue);
- PropVariantInit(&m_varContextValue);
- }
-
- virtual ~Marker()
- {
- PropVariantClear(&m_varMarkerValue);
- PropVariantClear(&m_varContextValue);
- }
- };
-
- class MediaStream : public QObject, public IMFStreamSink, public IMFMediaTypeHandler
- {
- Q_OBJECT
- friend class MFVideoRendererControl;
- public:
- static const DWORD DEFAULT_MEDIA_STREAM_ID = 0x0;
-
- MediaStream(IMFMediaSink *parent, MFVideoRendererControl *rendererControl)
- : m_workQueueCB(this, &MediaStream::onDispatchWorkItem)
- , m_rendererControl(rendererControl)
- {
- m_sink = parent;
-
- if (FAILED(MFCreateEventQueue(&m_eventQueue)))
- qWarning("Failed to create mf event queue!");
- if (FAILED(MFAllocateWorkQueue(&m_workQueueId)))
- qWarning("Failed to allocated mf work queue!");
- }
-
- ~MediaStream()
- {
- Q_ASSERT(m_shutdown);
- }
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override
- {
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFStreamSink) {
- *ppvObject = static_cast<IMFStreamSink*>(this);
- } else if (riid == IID_IMFMediaEventGenerator) {
- *ppvObject = static_cast<IMFMediaEventGenerator*>(this);
- } else if (riid == IID_IMFMediaTypeHandler) {
- *ppvObject = static_cast<IMFMediaTypeHandler*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(static_cast<IMFStreamSink*>(this));
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
- }
-
- STDMETHODIMP_(ULONG) AddRef(void) override
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release(void) override
- {
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- delete this;
- // For thread safety, return a temporary variable.
- return cRef;
- }
-
- //from IMFMediaEventGenerator
- STDMETHODIMP GetEvent(
- DWORD dwFlags,
- IMFMediaEvent **ppEvent) override
- {
- // GetEvent can block indefinitely, so we don't hold the lock.
- // This requires some juggling with the event queue pointer.
- HRESULT hr = S_OK;
- IMFMediaEventQueue *queue = NULL;
-
- m_mutex.lock();
- if (m_shutdown)
- hr = MF_E_SHUTDOWN;
- if (SUCCEEDED(hr)) {
- queue = m_eventQueue;
- queue->AddRef();
- }
- m_mutex.unlock();
-
- // Now get the event.
- if (SUCCEEDED(hr)) {
- hr = queue->GetEvent(dwFlags, ppEvent);
- queue->Release();
- }
-
- return hr;
- }
-
- STDMETHODIMP BeginGetEvent(
- IMFAsyncCallback *pCallback,
- IUnknown *punkState) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_eventQueue->BeginGetEvent(pCallback, punkState);
- }
-
- STDMETHODIMP EndGetEvent(
- IMFAsyncResult *pResult,
- IMFMediaEvent **ppEvent) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_eventQueue->EndGetEvent(pResult, ppEvent);
- }
-
- STDMETHODIMP QueueEvent(
- MediaEventType met,
- REFGUID guidExtendedType,
- HRESULT hrStatus,
- const PROPVARIANT *pvValue) override
- {
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MediaStream::QueueEvent" << met;
-#endif
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_eventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue);
- }
-
- //from IMFStreamSink
- STDMETHODIMP GetMediaSink(
- IMFMediaSink **ppMediaSink) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- else if (!ppMediaSink)
- return E_INVALIDARG;
-
- m_sink->AddRef();
- *ppMediaSink = m_sink;
- return S_OK;
- }
-
- STDMETHODIMP GetIdentifier(
- DWORD *pdwIdentifier) override
- {
- *pdwIdentifier = MediaStream::DEFAULT_MEDIA_STREAM_ID;
- return S_OK;
- }
-
- STDMETHODIMP GetMediaTypeHandler(
- IMFMediaTypeHandler **ppHandler) override
- {
- LPVOID handler = NULL;
- HRESULT hr = QueryInterface(IID_IMFMediaTypeHandler, &handler);
- *ppHandler = (IMFMediaTypeHandler*)(handler);
- return hr;
- }
-
- STDMETHODIMP ProcessSample(
- IMFSample *pSample) override
- {
- if (pSample == NULL)
- return E_INVALIDARG;
- HRESULT hr = S_OK;
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- if (!m_prerolling) {
- hr = validateOperation(OpProcessSample);
- if (FAILED(hr))
- return hr;
- }
-
- pSample->AddRef();
- m_sampleQueue.push_back(pSample);
-
- // Unless we are paused, start an async operation to dispatch the next sample.
- if (m_state != State_Paused)
- hr = queueAsyncOperation(OpProcessSample);
-
- return hr;
- }
-
- STDMETHODIMP PlaceMarker(
- MFSTREAMSINK_MARKER_TYPE eMarkerType,
- const PROPVARIANT *pvarMarkerValue,
- const PROPVARIANT *pvarContextValue) override
- {
- HRESULT hr = S_OK;
- QMutexLocker locker(&m_mutex);
- IMarker *pMarker = NULL;
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- hr = validateOperation(OpPlaceMarker);
- if (FAILED(hr))
- return hr;
-
- // Create a marker object and put it on the sample queue.
- hr = Marker::Create(eMarkerType, pvarMarkerValue, pvarContextValue, &pMarker);
- if (FAILED(hr))
- return hr;
-
- m_sampleQueue.push_back(pMarker);
-
- // Unless we are paused, start an async operation to dispatch the next sample/marker.
- if (m_state != State_Paused)
- hr = queueAsyncOperation(OpPlaceMarker); // Increments ref count on pOp.
- return hr;
- }
-
- STDMETHODIMP Flush(void) override
- {
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MediaStream::Flush";
-#endif
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- // Note: Even though we are flushing data, we still need to send
- // any marker events that were queued.
- clearBufferCache();
- return processSamplesFromQueue(DropSamples);
- }
-
- //from IMFMediaTypeHandler
- STDMETHODIMP IsMediaTypeSupported(
- IMFMediaType *pMediaType,
- IMFMediaType **ppMediaType) override
- {
- if (ppMediaType)
- *ppMediaType = NULL;
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- int index = getMediaTypeIndex(pMediaType);
- if (index < 0) {
- if (ppMediaType && m_mediaTypes.size() > 0) {
- *ppMediaType = m_mediaTypes[0];
- (*ppMediaType)->AddRef();
- }
- return MF_E_INVALIDMEDIATYPE;
- }
-
- BOOL compressed = TRUE;
- pMediaType->IsCompressedFormat(&compressed);
- if (compressed) {
- if (ppMediaType && (SUCCEEDED(MFCreateMediaType(ppMediaType)))) {
- (*ppMediaType)->CopyAllItems(pMediaType);
- (*ppMediaType)->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE);
- (*ppMediaType)->SetUINT32(MF_MT_COMPRESSED, FALSE);
- (*ppMediaType)->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
- }
- return MF_E_INVALIDMEDIATYPE;
- }
-
- return S_OK;
- }
-
- STDMETHODIMP GetMediaTypeCount(
- DWORD *pdwTypeCount) override
- {
- if (pdwTypeCount == NULL)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- *pdwTypeCount = DWORD(m_mediaTypes.size());
- return S_OK;
- }
-
- STDMETHODIMP GetMediaTypeByIndex(
- DWORD dwIndex,
- IMFMediaType **ppType) override
- {
- if (ppType == NULL)
- return E_INVALIDARG;
- HRESULT hr = S_OK;
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- hr = MF_E_SHUTDOWN;
-
- if (SUCCEEDED(hr)) {
- if (dwIndex >= DWORD(m_mediaTypes.size()))
- hr = MF_E_NO_MORE_TYPES;
- }
-
- if (SUCCEEDED(hr)) {
- *ppType = m_mediaTypes[dwIndex];
- (*ppType)->AddRef();
- }
- return hr;
- }
-
- STDMETHODIMP SetCurrentMediaType(
- IMFMediaType *pMediaType) override
- {
- HRESULT hr = S_OK;
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- DWORD flag = MF_MEDIATYPE_EQUAL_MAJOR_TYPES |
- MF_MEDIATYPE_EQUAL_FORMAT_TYPES |
- MF_MEDIATYPE_EQUAL_FORMAT_DATA;
-
- if (m_currentMediaType && (m_currentMediaType->IsEqual(pMediaType, &flag) == S_OK))
- return S_OK;
-
- hr = validateOperation(OpSetMediaType);
-
- if (SUCCEEDED(hr)) {
- int index = getMediaTypeIndex(pMediaType);
- if (index >= 0) {
- UINT64 size;
- hr = pMediaType->GetUINT64(MF_MT_FRAME_SIZE, &size);
- if (SUCCEEDED(hr)) {
- m_currentFormatIndex = index;
- int width = int(HI32(size));
- int height = int(LO32(size));
- QVideoFrameFormat format(QSize(width, height), m_pixelFormats[index]);
- m_surfaceFormat = format;
-
- MFVideoArea viewport;
- if (SUCCEEDED(pMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
- reinterpret_cast<UINT8*>(&viewport),
- sizeof(MFVideoArea),
- NULL))) {
-
- m_surfaceFormat.setViewport(QRect(viewport.OffsetX.value,
- viewport.OffsetY.value,
- viewport.Area.cx,
- viewport.Area.cy));
- }
-
- if (FAILED(pMediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&m_bytesPerLine))) {
- m_bytesPerLine = getBytesPerLine(format);
- }
-
- m_state = State_Ready;
- if (m_currentMediaType)
- m_currentMediaType->Release();
- m_currentMediaType = pMediaType;
- pMediaType->AddRef();
- }
- } else {
- hr = MF_E_INVALIDREQUEST;
- }
- }
- return hr;
- }
-
- STDMETHODIMP GetCurrentMediaType(
- IMFMediaType **ppMediaType) override
- {
- if (ppMediaType == NULL)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- if (m_currentFormatIndex < 0)
- return MF_E_NOT_INITIALIZED;
- *ppMediaType = m_currentMediaType;
- (*ppMediaType)->AddRef();
- return S_OK;
- }
-
- STDMETHODIMP GetMajorType(
- GUID *pguidMajorType) override
- {
- if (pguidMajorType == NULL)
- return E_INVALIDARG;
- *pguidMajorType = MFMediaType_Video;
- return S_OK;
- }
-
- //
- void setSink(QVideoSink *sink)
- {
- m_mutex.lock();
- m_videoSink = sink;
- m_mutex.unlock();
- supportedFormatsChanged();
- }
-
- void setClock(IMFPresentationClock *presentationClock)
- {
- QMutexLocker locker(&m_mutex);
- if (!m_shutdown) {
- if (m_presentationClock)
- m_presentationClock->Release();
- m_presentationClock = presentationClock;
- if (m_presentationClock)
- m_presentationClock->AddRef();
- }
- }
-
- void shutdown()
- {
- QMutexLocker locker(&m_mutex);
- Q_ASSERT(!m_shutdown);
-
- if (m_currentMediaType) {
- m_currentMediaType->Release();
- m_currentMediaType = NULL;
- m_currentFormatIndex = -1;
- }
-
- if (m_eventQueue)
- m_eventQueue->Shutdown();
-
- MFUnlockWorkQueue(m_workQueueId);
-
- if (m_presentationClock) {
- m_presentationClock->Release();
- m_presentationClock = NULL;
- }
-
- clearMediaTypes();
- clearSampleQueue();
- clearBufferCache();
-
- if (m_eventQueue) {
- m_eventQueue->Release();
- m_eventQueue = NULL;
- }
-
- m_shutdown = true;
- }
-
- HRESULT startPreroll(MFTIME hnsUpcomingStartTime)
- {
- QMutexLocker locker(&m_mutex);
- HRESULT hr = validateOperation(OpPreroll);
- if (SUCCEEDED(hr)) {
- m_state = State_Prerolling;
- m_prerollTargetTime = hnsUpcomingStartTime;
- hr = queueAsyncOperation(OpPreroll);
- }
- return hr;
- }
-
- HRESULT finalize(IMFAsyncCallback *pCallback, IUnknown *punkState)
- {
- QMutexLocker locker(&m_mutex);
- HRESULT hr = S_OK;
- hr = validateOperation(OpFinalize);
- if (SUCCEEDED(hr) && m_finalizeResult != NULL)
- hr = MF_E_INVALIDREQUEST; // The operation is already pending.
-
- // Create and store the async result object.
- if (SUCCEEDED(hr))
- hr = MFCreateAsyncResult(NULL, pCallback, punkState, &m_finalizeResult);
-
- if (SUCCEEDED(hr)) {
- m_state = State_Finalized;
- hr = queueAsyncOperation(OpFinalize);
- }
- return hr;
- }
-
- HRESULT start(MFTIME start)
- {
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MediaStream::start" << start;
-#endif
- HRESULT hr = S_OK;
- QMutexLocker locker(&m_mutex);
- if (m_rate != 0)
- hr = validateOperation(OpStart);
-
- if (SUCCEEDED(hr)) {
- MFTIME sysTime;
- if (start != QMM_PRESENTATION_CURRENT_POSITION)
- m_startTime = start; // Cache the start time.
- else
- m_presentationClock->GetCorrelatedTime(0, &m_startTime, &sysTime);
- m_state = State_Started;
- hr = queueAsyncOperation(OpStart);
- }
- return hr;
- }
-
- HRESULT restart()
- {
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MediaStream::restart";
-#endif
- QMutexLocker locker(&m_mutex);
- HRESULT hr = validateOperation(OpRestart);
- if (SUCCEEDED(hr)) {
- m_state = State_Started;
- hr = queueAsyncOperation(OpRestart);
- }
- return hr;
- }
-
- HRESULT stop()
- {
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MediaStream::stop";
-#endif
- QMutexLocker locker(&m_mutex);
- HRESULT hr = validateOperation(OpStop);
- if (SUCCEEDED(hr)) {
- m_state = State_Stopped;
- hr = queueAsyncOperation(OpStop);
- }
- return hr;
- }
-
- HRESULT pause()
- {
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MediaStream::pause";
-#endif
- QMutexLocker locker(&m_mutex);
- HRESULT hr = validateOperation(OpPause);
- if (SUCCEEDED(hr)) {
- m_state = State_Paused;
- hr = queueAsyncOperation(OpPause);
- }
- return hr;
- }
-
- HRESULT setRate(float rate)
- {
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "MediaStream::setRate" << rate;
-#endif
- QMutexLocker locker(&m_mutex);
- HRESULT hr = validateOperation(OpSetRate);
- if (SUCCEEDED(hr)) {
- m_rate = rate;
- hr = queueAsyncOperation(OpSetRate);
- }
- return hr;
- }
-
- void supportedFormatsChanged()
- {
- QMutexLocker locker(&m_mutex);
- m_pixelFormats.clear();
- clearMediaTypes();
- if (!m_videoSink)
- return;
- for (int f = 0; f < QVideoFrameFormat::NPixelFormats; ++f) {
- QVideoFrameFormat::PixelFormat format = QVideoFrameFormat::PixelFormat(f);
- IMFMediaType *mediaType;
- if (FAILED(MFCreateMediaType(&mediaType))) {
- qWarning("Failed to create mf media type!");
- continue;
- }
- mediaType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE);
- mediaType->SetUINT32(MF_MT_COMPRESSED, FALSE);
- mediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
- mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
- switch (format) {
- case QVideoFrameFormat::Format_BGRA8888:
- case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
- break;
- case QVideoFrameFormat::Format_BGRX8888:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
- break;
- case QVideoFrameFormat::Format_AYUV:
- case QVideoFrameFormat::Format_AYUV_Premultiplied:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_AYUV);
- break;
- case QVideoFrameFormat::Format_YUV420P:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_I420);
- break;
- case QVideoFrameFormat::Format_UYVY:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY);
- break;
- case QVideoFrameFormat::Format_YV12:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12);
- break;
- case QVideoFrameFormat::Format_NV12:
- mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
- break;
- default:
- mediaType->Release();
- continue;
- }
- // #### QAbstractVideoSurface::supportedPixelFormats() returns formats in descending
- // order of preference, while IMFMediaTypeHandler is supposed to return supported
- // formats in ascending order of preference. We need to reverse the list.
- m_pixelFormats.prepend(format);
- m_mediaTypes.prepend(mediaType);
- }
- }
-
- void present()
- {
- QMutexLocker locker(&m_mutex);
- if (!m_scheduledBuffer)
- return;
- QVideoFrame frame = QVideoFrame(
- new MediaSampleVideoBuffer(m_scheduledBuffer, m_bytesPerLine), m_surfaceFormat);
- frame.setStartTime(m_bufferStartTime * 0.1);
- frame.setEndTime((m_bufferStartTime + m_bufferDuration) * 0.1);
- m_videoSink->platformVideoSink()->setVideoFrame(frame);
- m_scheduledBuffer->Release();
- m_scheduledBuffer = NULL;
- if (m_rate != 0)
- schedulePresentation(true);
- }
-
- void clearScheduledFrame()
- {
- QMutexLocker locker(&m_mutex);
- if (m_scheduledBuffer) {
- m_scheduledBuffer->Release();
- m_scheduledBuffer = NULL;
- schedulePresentation(true);
- }
- }
-
- enum
- {
- PresentSurface
- };
-
- class PresentEvent : public QEvent
- {
- public:
- PresentEvent(MFTIME targetTime)
- : QEvent(QEvent::Type(PresentSurface))
- , m_time(targetTime)
- {
- }
-
- MFTIME targetTime()
- {
- return m_time;
- }
-
- private:
- MFTIME m_time;
- };
-
- protected:
- HRESULT m_startResult;
-
- private:
- enum FlushState
- {
- DropSamples = 0,
- WriteSamples
- };
-
- // State enum: Defines the current state of the stream.
- enum State
- {
- State_TypeNotSet = 0, // No media type is set
- State_Ready, // Media type is set, Start has never been called.
- State_Prerolling,
- State_Started,
- State_Paused,
- State_Stopped,
- State_WaitForSurfaceStart,
- State_Finalized,
- State_Count = State_Finalized + 1 // Number of states
- };
-
- // StreamOperation: Defines various operations that can be performed on the stream.
- enum StreamOperation
- {
- OpSetMediaType = 0,
- OpStart,
- OpPreroll,
- OpRestart,
- OpPause,
- OpStop,
- OpSetRate,
- OpProcessSample,
- OpPlaceMarker,
- OpFinalize,
-
- Op_Count = OpFinalize + 1 // Number of operations
- };
-
- // AsyncOperation:
- // Used to queue asynchronous operations. When we call MFPutWorkItem, we use this
- // object for the callback state (pState). Then, when the callback is invoked,
- // we can use the object to determine which asynchronous operation to perform.
- class AsyncOperation : public IUnknown
- {
- public:
- AsyncOperation(StreamOperation op)
- :m_op(op)
- {
- }
-
- StreamOperation m_op; // The operation to perform.
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override
- {
- if (!ppv)
- return E_POINTER;
- if (iid == IID_IUnknown) {
- *ppv = static_cast<IUnknown*>(this);
- } else {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
- }
- STDMETHODIMP_(ULONG) AddRef() override
- {
- return InterlockedIncrement(&m_cRef);
- }
- STDMETHODIMP_(ULONG) Release() override
- {
- ULONG uCount = InterlockedDecrement(&m_cRef);
- if (uCount == 0)
- delete this;
- // For thread safety, return a temporary variable.
- return uCount;
- }
-
- private:
- long m_cRef = 1;
- virtual ~AsyncOperation()
- {
- Q_ASSERT(m_cRef == 0);
- }
- };
-
- // ValidStateMatrix: Defines a look-up table that says which operations
- // are valid from which states.
- static BOOL ValidStateMatrix[State_Count][Op_Count];
-
- long m_cRef = 1;
- QMutex m_mutex;
-
- IMFMediaType *m_currentMediaType = nullptr;
- State m_state = State_TypeNotSet;
- IMFMediaSink *m_sink = nullptr;
- IMFMediaEventQueue *m_eventQueue = nullptr;
- DWORD m_workQueueId = 0;
- AsyncCallback<MediaStream> m_workQueueCB;
- QList<IUnknown*> m_sampleQueue;
- IMFAsyncResult *m_finalizeResult = nullptr; // Result object for Finalize operation.
- MFTIME m_startTime = 0; // Presentation time when the clock started.
-
- bool m_shutdown = false;
- QList<IMFMediaType*> m_mediaTypes;
- QList<QVideoFrameFormat::PixelFormat> m_pixelFormats;
- int m_currentFormatIndex = -1;
- int m_bytesPerLine = 0;
- QVideoFrameFormat m_surfaceFormat;
- QVideoSink *m_videoSink = nullptr;
- MFVideoRendererControl *m_rendererControl = nullptr;
-
- void clearMediaTypes()
- {
- for (IMFMediaType* mediaType : qAsConst(m_mediaTypes))
- mediaType->Release();
- m_mediaTypes.clear();
- }
-
- int getMediaTypeIndex(IMFMediaType *mt)
- {
- GUID majorType;
- if (FAILED(mt->GetMajorType(&majorType)))
- return -1;
- if (majorType != MFMediaType_Video)
- return -1;
-
- GUID subType;
- if (FAILED(mt->GetGUID(MF_MT_SUBTYPE, &subType)))
- return -1;
-
- for (int index = 0; index < m_mediaTypes.size(); ++index) {
- GUID st;
- m_mediaTypes[index]->GetGUID(MF_MT_SUBTYPE, &st);
- if (st == subType)
- return index;
- }
- return -1;
- }
-
- int getBytesPerLine(const QVideoFrameFormat &format)
- {
- switch (format.pixelFormat()) {
- // 32 bpp packed formats.
- case QVideoFrameFormat::Format_XBGR8888:
- case QVideoFrameFormat::Format_BGRX8888:
- case QVideoFrameFormat::Format_XRGB8888:
- case QVideoFrameFormat::Format_RGBX8888:
- case QVideoFrameFormat::Format_AYUV:
- return format.frameWidth() * 4;
- // 16 bpp packed formats.
- case QVideoFrameFormat::Format_YUYV:
- case QVideoFrameFormat::Format_UYVY:
- return PAD_TO_DWORD(format.frameWidth() * 2);
- // Planar formats.
- case QVideoFrameFormat::Format_IMC1:
- case QVideoFrameFormat::Format_IMC2:
- case QVideoFrameFormat::Format_IMC3:
- case QVideoFrameFormat::Format_IMC4:
- case QVideoFrameFormat::Format_YV12:
- case QVideoFrameFormat::Format_NV12:
- case QVideoFrameFormat::Format_YUV420P:
- return PAD_TO_DWORD(format.frameWidth());
- default:
- return 0;
- }
- }
-
- // Callback for MFPutWorkItem.
- HRESULT onDispatchWorkItem(IMFAsyncResult* pAsyncResult)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- HRESULT hr = S_OK;
- IUnknown *pState = NULL;
- hr = pAsyncResult->GetState(&pState);
- if (SUCCEEDED(hr)) {
- // The state object is an AsncOperation object.
- AsyncOperation *pOp = (AsyncOperation*)pState;
- StreamOperation op = pOp->m_op;
- switch (op) {
- case OpStart:
- endPreroll(S_FALSE);
- schedulePresentation(true);
- // fallthrough
- case OpRestart:
- endPreroll(S_FALSE);
- if (SUCCEEDED(hr)) {
- // Send MEStreamSinkStarted.
- hr = queueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL);
- // Kick things off by requesting samples...
- schedulePresentation(true);
- // There might be samples queue from earlier (ie, while paused).
- if (SUCCEEDED(hr))
- hr = processSamplesFromQueue(WriteSamples);
- }
- break;
- case OpPreroll:
- beginPreroll();
- break;
- case OpStop:
- // Drop samples from queue.
- hr = processSamplesFromQueue(DropSamples);
- clearBufferCache();
- // Send the event even if the previous call failed.
- hr = queueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL);
- break;
- case OpPause:
- hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL);
- break;
- case OpSetRate:
- hr = queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL);
- break;
- case OpProcessSample:
- case OpPlaceMarker:
- hr = dispatchProcessSample(pOp);
- break;
- case OpFinalize:
- endPreroll(S_FALSE);
- hr = dispatchFinalize(pOp);
- break;
- default:
- break;
- }
- }
-
- if (pState)
- pState->Release();
- return hr;
- }
-
-
- HRESULT queueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue)
- {
- HRESULT hr = S_OK;
- if (m_shutdown)
- hr = MF_E_SHUTDOWN;
- if (SUCCEEDED(hr))
- hr = m_eventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue);
- return hr;
- }
-
- HRESULT validateOperation(StreamOperation op)
- {
- Q_ASSERT(!m_shutdown);
- if (ValidStateMatrix[m_state][op])
- return S_OK;
- else
- return MF_E_INVALIDREQUEST;
- }
-
- HRESULT queueAsyncOperation(StreamOperation op)
- {
- HRESULT hr = S_OK;
- AsyncOperation *asyncOp = new AsyncOperation(op);
- if (asyncOp == NULL)
- hr = E_OUTOFMEMORY;
-
- if (SUCCEEDED(hr))
- hr = MFPutWorkItem(m_workQueueId, &m_workQueueCB, asyncOp);
-
- if (asyncOp)
- asyncOp->Release();
-
- return hr;
- }
-
- HRESULT processSamplesFromQueue(FlushState bFlushData)
- {
- HRESULT hr = S_OK;
- QList<IUnknown*>::Iterator pos = m_sampleQueue.begin();
- // Enumerate all of the samples/markers in the queue.
- while (pos != m_sampleQueue.end()) {
- IUnknown *pUnk = NULL;
- IMarker *pMarker = NULL;
- IMFSample *pSample = NULL;
- pUnk = *pos;
- // Figure out if this is a marker or a sample.
- if (SUCCEEDED(hr)) {
- hr = pUnk->QueryInterface(IID_IMarker, (void**)&pMarker);
- if (hr == E_NOINTERFACE)
- hr = pUnk->QueryInterface(IID_IMFSample, (void**)&pSample);
- }
-
- // Now handle the sample/marker appropriately.
- if (SUCCEEDED(hr)) {
- if (pMarker) {
- hr = sendMarkerEvent(pMarker, bFlushData);
- } else {
- Q_ASSERT(pSample != NULL); // Not a marker, must be a sample
- if (bFlushData == WriteSamples)
- hr = processSampleData(pSample);
- }
- }
- if (pMarker)
- pMarker->Release();
- if (pSample)
- pSample->Release();
-
- if (FAILED(hr))
- break;
-
- pos++;
- }
-
- clearSampleQueue();
- return hr;
- }
-
- void beginPreroll()
- {
- if (m_prerolling)
- return;
- m_prerolling = true;
- clearSampleQueue();
- clearBufferCache();
- queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
- }
-
- void endPreroll(HRESULT hrStatus)
- {
- if (!m_prerolling)
- return;
- m_prerolling = false;
- queueEvent(MEStreamSinkPrerolled, GUID_NULL, hrStatus, NULL);
- }
- MFTIME m_prerollTargetTime = 0;
- bool m_prerolling = false;
-
- void clearSampleQueue() {
- for (IUnknown* sample : qAsConst(m_sampleQueue))
- sample->Release();
- m_sampleQueue.clear();
- }
-
- HRESULT sendMarkerEvent(IMarker *pMarker, FlushState FlushState)
- {
- HRESULT hr = S_OK;
- HRESULT hrStatus = S_OK; // Status code for marker event.
- if (FlushState == DropSamples)
- hrStatus = E_ABORT;
-
- PROPVARIANT var;
- PropVariantInit(&var);
-
- // Get the context data.
- hr = pMarker->GetContext(&var);
-
- if (SUCCEEDED(hr))
- hr = queueEvent(MEStreamSinkMarker, GUID_NULL, hrStatus, &var);
-
- PropVariantClear(&var);
- return hr;
- }
-
- HRESULT dispatchProcessSample(AsyncOperation* pOp)
- {
- HRESULT hr = S_OK;
- Q_ASSERT(pOp != NULL);
- Q_UNUSED(pOp);
- hr = processSamplesFromQueue(WriteSamples);
- // We are in the middle of an asynchronous operation, so if something failed, send an error.
- if (FAILED(hr))
- hr = queueEvent(MEError, GUID_NULL, hr, NULL);
-
- return hr;
- }
-
- HRESULT dispatchFinalize(AsyncOperation*)
- {
- HRESULT hr = S_OK;
- // Write any samples left in the queue...
- hr = processSamplesFromQueue(WriteSamples);
-
- // Set the async status and invoke the callback.
- m_finalizeResult->SetStatus(hr);
- hr = MFInvokeCallback(m_finalizeResult);
- return hr;
- }
-
- HRESULT processSampleData(IMFSample *pSample)
- {
- m_sampleRequested = false;
-
- LONGLONG time, duration = -1;
- HRESULT hr = pSample->GetSampleTime(&time);
- if (SUCCEEDED(hr))
- pSample->GetSampleDuration(&duration);
-
- if (m_prerolling) {
- if (SUCCEEDED(hr) && ((time - m_prerollTargetTime) * m_rate) >= 0) {
- IMFMediaBuffer *pBuffer = NULL;
- hr = pSample->ConvertToContiguousBuffer(&pBuffer);
- if (SUCCEEDED(hr)) {
- SampleBuffer sb;
- sb.m_buffer = pBuffer;
- sb.m_time = time;
- sb.m_duration = duration;
- m_bufferCache.push_back(sb);
- endPreroll(S_OK);
- }
- } else {
- queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
- }
- } else {
- bool requestSample = true;
- // If the time stamp is too early, just discard this sample.
- if (SUCCEEDED(hr) && ((time - m_startTime) * m_rate) >= 0) {
- IMFMediaBuffer *pBuffer = NULL;
- hr = pSample->ConvertToContiguousBuffer(&pBuffer);
- if (SUCCEEDED(hr)) {
- SampleBuffer sb;
- sb.m_buffer = pBuffer;
- sb.m_time = time;
- sb.m_duration = duration;
- m_bufferCache.push_back(sb);
- }
- if (m_rate == 0)
- requestSample = false;
- }
- schedulePresentation(requestSample);
- }
- return hr;
- }
-
- class SampleBuffer
- {
- public:
- IMFMediaBuffer *m_buffer;
- LONGLONG m_time;
- LONGLONG m_duration;
- };
- QList<SampleBuffer> m_bufferCache;
- static const int BUFFER_CACHE_SIZE = 2;
-
- void clearBufferCache()
- {
- for (SampleBuffer sb : qAsConst(m_bufferCache))
- sb.m_buffer->Release();
- m_bufferCache.clear();
-
- if (m_scheduledBuffer) {
- m_scheduledBuffer->Release();
- m_scheduledBuffer = NULL;
- }
- }
-
- void schedulePresentation(bool requestSample)
- {
- if (m_state == State_Paused || m_state == State_Prerolling)
- return;
- if (!m_scheduledBuffer) {
- //get time from presentation time
- MFTIME currentTime = m_startTime, sysTime;
- bool timeOK = true;
- if (m_rate != 0) {
- if (FAILED(m_presentationClock->GetCorrelatedTime(0, &currentTime, &sysTime)))
- timeOK = false;
- }
- while (!m_bufferCache.isEmpty()) {
- SampleBuffer sb = m_bufferCache.takeFirst();
- if (timeOK && ((sb.m_time - currentTime) * m_rate) < 0) {
- sb.m_buffer->Release();
-#ifdef DEBUG_MEDIAFOUNDATION
- qDebug() << "currentPresentTime =" << float(currentTime / 10000) * 0.001f << " and sampleTime is" << float(sb.m_time / 10000) * 0.001f;
-#endif
- continue;
- }
- m_scheduledBuffer = sb.m_buffer;
- m_bufferStartTime = sb.m_time;
- m_bufferDuration = sb.m_duration;
- QCoreApplication::postEvent(m_rendererControl, new PresentEvent(sb.m_time));
- if (m_rate == 0)
- queueEvent(MEStreamSinkScrubSampleComplete, GUID_NULL, S_OK, NULL);
- break;
- }
- }
- if (requestSample && !m_sampleRequested && m_bufferCache.size() < BUFFER_CACHE_SIZE) {
- m_sampleRequested = true;
- queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
- }
- }
- IMFMediaBuffer *m_scheduledBuffer = nullptr;
- MFTIME m_bufferStartTime = -1;
- MFTIME m_bufferDuration = -1;
- IMFPresentationClock *m_presentationClock = nullptr;
- bool m_sampleRequested = false;
- float m_rate = 1.f;
- };
-
- BOOL MediaStream::ValidStateMatrix[MediaStream::State_Count][MediaStream::Op_Count] =
- {
- // States: Operations:
- // SetType Start Preroll, Restart Pause Stop SetRate Sample Marker Finalize
- /* NotSet */ TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
-
- /* Ready */ TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE,
-
- /* Prerolling */ TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
-
- /* Start */ FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
-
- /* Pause */ FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
-
- /* Stop */ FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE,
-
- /*WaitForSurfaceStart*/ FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE,
-
- /* Final */ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
-
- // Note about states:
- // 1. OnClockRestart should only be called from paused state.
- // 2. While paused, the sink accepts samples but does not process them.
- };
-
- class MediaSink : public IMFFinalizableMediaSink,
- public IMFClockStateSink,
- public IMFMediaSinkPreroll,
- public IMFGetService,
- public IMFRateSupport
- {
- public:
- MediaSink(MFVideoRendererControl *rendererControl)
- {
- m_stream = new MediaStream(this, rendererControl);
- }
-
- virtual ~MediaSink()
- {
- Q_ASSERT(m_shutdown);
- }
-
- void setSurface(QVideoSink *surface)
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return;
- m_stream->setSink(surface);
- }
-
- void present()
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return;
- m_stream->present();
- }
-
- void clearScheduledFrame()
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return;
- m_stream->clearScheduledFrame();
- }
-
- MFTIME getTime()
- {
- QMutexLocker locker(&m_mutex);
- if (!m_presentationClock)
- return 0;
- MFTIME time, sysTime;
- m_presentationClock->GetCorrelatedTime(0, &time, &sysTime);
- return time;
- }
-
- float getPlayRate()
- {
- QMutexLocker locker(&m_mutex);
- return m_playRate;
- }
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override
- {
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFMediaSink) {
- *ppvObject = static_cast<IMFMediaSink*>(this);
- } else if (riid == IID_IMFGetService) {
- *ppvObject = static_cast<IMFGetService*>(this);
- } else if (riid == IID_IMFMediaSinkPreroll) {
- *ppvObject = static_cast<IMFMediaSinkPreroll*>(this);
- } else if (riid == IID_IMFClockStateSink) {
- *ppvObject = static_cast<IMFClockStateSink*>(this);
- } else if (riid == IID_IMFRateSupport) {
- *ppvObject = static_cast<IMFRateSupport*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this));
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
- }
-
- STDMETHODIMP_(ULONG) AddRef(void) override
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release(void) override
- {
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- delete this;
- // For thread safety, return a temporary variable.
- return cRef;
- }
-
- // IMFGetService methods
- STDMETHODIMP GetService(const GUID &guidService,
- const IID &riid,
- LPVOID *ppvObject) override
- {
- if (!ppvObject)
- return E_POINTER;
-
- if (guidService != MF_RATE_CONTROL_SERVICE)
- return MF_E_UNSUPPORTED_SERVICE;
-
- return QueryInterface(riid, ppvObject);
- }
-
- //IMFMediaSinkPreroll
- STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->startPreroll(hnsUpcomingStartTime);
- }
-
- //from IMFFinalizableMediaSink
- STDMETHODIMP BeginFinalize(IMFAsyncCallback *pCallback, IUnknown *punkState) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->finalize(pCallback, punkState);
- }
-
- STDMETHODIMP EndFinalize(IMFAsyncResult *pResult) override
- {
- HRESULT hr = S_OK;
- // Return the status code from the async result.
- if (pResult == NULL)
- hr = E_INVALIDARG;
- else
- hr = pResult->GetStatus();
- return hr;
- }
-
- //from IMFMediaSink
- STDMETHODIMP GetCharacteristics(
- DWORD *pdwCharacteristics) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- *pdwCharacteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_CAN_PREROLL;
- return S_OK;
- }
-
- STDMETHODIMP AddStreamSink(
- DWORD,
- IMFMediaType *,
- IMFStreamSink **) override
- {
- QMutexLocker locker(&m_mutex);
- return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
- }
-
- STDMETHODIMP RemoveStreamSink(
- DWORD) override
- {
- QMutexLocker locker(&m_mutex);
- return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
- }
-
- STDMETHODIMP GetStreamSinkCount(
- DWORD *pcStreamSinkCount) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- *pcStreamSinkCount = 1;
- return S_OK;
- }
-
- STDMETHODIMP GetStreamSinkByIndex(
- DWORD dwIndex,
- IMFStreamSink **ppStreamSink) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- if (dwIndex != 0)
- return MF_E_INVALIDINDEX;
-
- *ppStreamSink = m_stream;
- m_stream->AddRef();
- return S_OK;
- }
-
- STDMETHODIMP GetStreamSinkById(
- DWORD dwStreamSinkIdentifier,
- IMFStreamSink **ppStreamSink) override
- {
- if (ppStreamSink == NULL)
- return E_INVALIDARG;
- if (dwStreamSinkIdentifier != MediaStream::DEFAULT_MEDIA_STREAM_ID)
- return MF_E_INVALIDSTREAMNUMBER;
-
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- *ppStreamSink = m_stream;
- m_stream->AddRef();
- return S_OK;
- }
-
- STDMETHODIMP SetPresentationClock(
- IMFPresentationClock *pPresentationClock) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- if (m_presentationClock) {
- m_presentationClock->RemoveClockStateSink(this);
- m_presentationClock->Release();
- }
- m_presentationClock = pPresentationClock;
- if (m_presentationClock) {
- m_presentationClock->AddRef();
- m_presentationClock->AddClockStateSink(this);
- }
- m_stream->setClock(m_presentationClock);
- return S_OK;
- }
-
- STDMETHODIMP GetPresentationClock(
- IMFPresentationClock **ppPresentationClock) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- *ppPresentationClock = m_presentationClock;
- if (m_presentationClock) {
- m_presentationClock->AddRef();
- return S_OK;
- }
- return MF_E_NO_CLOCK;
- }
-
- STDMETHODIMP Shutdown(void) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
-
- m_stream->shutdown();
- if (m_presentationClock) {
- m_presentationClock->Release();
- m_presentationClock = NULL;
- }
- m_stream->Release();
- m_stream = NULL;
- m_shutdown = true;
- return S_OK;
- }
-
- // IMFClockStateSink methods
- STDMETHODIMP OnClockStart(MFTIME, LONGLONG llClockStartOffset) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->start(llClockStartOffset);
- }
-
- STDMETHODIMP OnClockStop(MFTIME) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->stop();
- }
-
- STDMETHODIMP OnClockPause(MFTIME) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->pause();
- }
-
- STDMETHODIMP OnClockRestart(MFTIME) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- return m_stream->restart();
- }
-
- STDMETHODIMP OnClockSetRate(MFTIME, float flRate) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_shutdown)
- return MF_E_SHUTDOWN;
- m_playRate = flRate;
- return m_stream->setRate(flRate);
- }
-
- // IMFRateSupport methods
- STDMETHODIMP GetFastestRate(MFRATE_DIRECTION eDirection,
- BOOL fThin,
- float *pflRate) override
- {
- if (!pflRate)
- return E_POINTER;
-
- *pflRate = (fThin ? 8.f : 2.0f) * (eDirection == MFRATE_FORWARD ? 1 : -1) ;
-
- return S_OK;
- }
-
- STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION eDirection,
- BOOL fThin,
- float *pflRate) override
- {
- Q_UNUSED(eDirection);
- Q_UNUSED(fThin);
-
- if (!pflRate)
- return E_POINTER;
-
- // we support any rate
- *pflRate = 0.f;
-
- return S_OK;
- }
-
- STDMETHODIMP IsRateSupported(BOOL fThin,
- float flRate,
- float *pflNearestSupportedRate) override
- {
- HRESULT hr = S_OK;
-
- if (!qFuzzyIsNull(flRate)) {
- MFRATE_DIRECTION direction = flRate > 0.f ? MFRATE_FORWARD
- : MFRATE_REVERSE;
-
- float fastestRate = 0.f;
- float slowestRate = 0.f;
- GetFastestRate(direction, fThin, &fastestRate);
- GetSlowestRate(direction, fThin, &slowestRate);
-
- if (direction == MFRATE_REVERSE)
- qSwap(fastestRate, slowestRate);
-
- if (flRate < slowestRate || flRate > fastestRate) {
- hr = MF_E_UNSUPPORTED_RATE;
- if (pflNearestSupportedRate) {
- *pflNearestSupportedRate = qBound(slowestRate,
- flRate,
- fastestRate);
- }
- }
- } else if (pflNearestSupportedRate) {
- *pflNearestSupportedRate = flRate;
- }
-
- return hr;
- }
-
- private:
- long m_cRef = 1;
- QMutex m_mutex;
- bool m_shutdown = false;
- IMFPresentationClock *m_presentationClock = nullptr;
- MediaStream *m_stream = nullptr;
- float m_playRate = 1;
- };
-
- class VideoRendererActivate : public IMFActivate
- {
- public:
- VideoRendererActivate(MFVideoRendererControl *rendererControl)
- : m_cRef(1)
- , m_sink(0)
- , m_rendererControl(rendererControl)
- , m_attributes(0)
- , m_videoSink(0)
- {
- MFCreateAttributes(&m_attributes, 0);
- m_sink = new MediaSink(rendererControl);
- }
-
- virtual ~VideoRendererActivate()
- {
- m_attributes->Release();
- }
-
- //from IUnknown
- STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override
- {
- if (!ppvObject)
- return E_POINTER;
- if (riid == IID_IMFActivate) {
- *ppvObject = static_cast<IMFActivate*>(this);
- } else if (riid == IID_IMFAttributes) {
- *ppvObject = static_cast<IMFAttributes*>(this);
- } else if (riid == IID_IUnknown) {
- *ppvObject = static_cast<IUnknown*>(static_cast<IMFActivate*>(this));
- } else {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
- }
-
- STDMETHODIMP_(ULONG) AddRef(void) override
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release(void) override
- {
- LONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0)
- delete this;
- // For thread safety, return a temporary variable.
- return cRef;
- }
-
- //from IMFActivate
- STDMETHODIMP ActivateObject(REFIID riid, void **ppv) override
- {
- if (!ppv)
- return E_INVALIDARG;
- QMutexLocker locker(&m_mutex);
- if (!m_sink) {
- m_sink = new MediaSink(m_rendererControl);
- if (m_videoSink)
- m_sink->setSurface(m_videoSink);
- }
- return m_sink->QueryInterface(riid, ppv);
- }
-
- STDMETHODIMP ShutdownObject(void) override
- {
- QMutexLocker locker(&m_mutex);
- HRESULT hr = S_OK;
- if (m_sink) {
- hr = m_sink->Shutdown();
- m_sink->Release();
- m_sink = NULL;
- }
- return hr;
- }
-
- STDMETHODIMP DetachObject(void) override
- {
- QMutexLocker locker(&m_mutex);
- if (m_sink) {
- m_sink->Release();
- m_sink = NULL;
- }
- return S_OK;
- }
-
- //from IMFAttributes
- STDMETHODIMP GetItem(
- REFGUID guidKey,
- PROPVARIANT *pValue) override
- {
- return m_attributes->GetItem(guidKey, pValue);
- }
-
- STDMETHODIMP GetItemType(
- REFGUID guidKey,
- MF_ATTRIBUTE_TYPE *pType) override
- {
- return m_attributes->GetItemType(guidKey, pType);
- }
-
- STDMETHODIMP CompareItem(
- REFGUID guidKey,
- REFPROPVARIANT Value,
- BOOL *pbResult) override
- {
- return m_attributes->CompareItem(guidKey, Value, pbResult);
- }
-
- STDMETHODIMP Compare(
- IMFAttributes *pTheirs,
- MF_ATTRIBUTES_MATCH_TYPE MatchType,
- BOOL *pbResult) override
- {
- return m_attributes->Compare(pTheirs, MatchType, pbResult);
- }
-
- STDMETHODIMP GetUINT32(
- REFGUID guidKey,
- UINT32 *punValue) override
- {
- return m_attributes->GetUINT32(guidKey, punValue);
- }
-
- STDMETHODIMP GetUINT64(
- REFGUID guidKey,
- UINT64 *punValue) override
- {
- return m_attributes->GetUINT64(guidKey, punValue);
- }
-
- STDMETHODIMP GetDouble(
- REFGUID guidKey,
- double *pfValue) override
- {
- return m_attributes->GetDouble(guidKey, pfValue);
- }
-
- STDMETHODIMP GetGUID(
- REFGUID guidKey,
- GUID *pguidValue) override
- {
- return m_attributes->GetGUID(guidKey, pguidValue);
- }
-
- STDMETHODIMP GetStringLength(
- REFGUID guidKey,
- UINT32 *pcchLength) override
- {
- return m_attributes->GetStringLength(guidKey, pcchLength);
- }
-
- STDMETHODIMP GetString(
- REFGUID guidKey,
- LPWSTR pwszValue,
- UINT32 cchBufSize,
- UINT32 *pcchLength) override
- {
- return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
- }
-
- STDMETHODIMP GetAllocatedString(
- REFGUID guidKey,
- LPWSTR *ppwszValue,
- UINT32 *pcchLength) override
- {
- return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
- }
-
- STDMETHODIMP GetBlobSize(
- REFGUID guidKey,
- UINT32 *pcbBlobSize) override
- {
- return m_attributes->GetBlobSize(guidKey, pcbBlobSize);
- }
-
- STDMETHODIMP GetBlob(
- REFGUID guidKey,
- UINT8 *pBuf,
- UINT32 cbBufSize,
- UINT32 *pcbBlobSize) override
- {
- return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
- }
-
- STDMETHODIMP GetAllocatedBlob(
- REFGUID guidKey,
- UINT8 **ppBuf,
- UINT32 *pcbSize) override
- {
- return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
- }
-
- STDMETHODIMP GetUnknown(
- REFGUID guidKey,
- REFIID riid,
- LPVOID *ppv) override
- {
- return m_attributes->GetUnknown(guidKey, riid, ppv);
- }
-
- STDMETHODIMP SetItem(
- REFGUID guidKey,
- REFPROPVARIANT Value) override
- {
- return m_attributes->SetItem(guidKey, Value);
- }
-
- STDMETHODIMP DeleteItem(
- REFGUID guidKey) override
- {
- return m_attributes->DeleteItem(guidKey);
- }
-
- STDMETHODIMP DeleteAllItems(void) override
- {
- return m_attributes->DeleteAllItems();
- }
-
- STDMETHODIMP SetUINT32(
- REFGUID guidKey,
- UINT32 unValue) override
- {
- return m_attributes->SetUINT32(guidKey, unValue);
- }
-
- STDMETHODIMP SetUINT64(
- REFGUID guidKey,
- UINT64 unValue) override
- {
- return m_attributes->SetUINT64(guidKey, unValue);
- }
-
- STDMETHODIMP SetDouble(
- REFGUID guidKey,
- double fValue) override
- {
- return m_attributes->SetDouble(guidKey, fValue);
- }
-
- STDMETHODIMP SetGUID(
- REFGUID guidKey,
- REFGUID guidValue) override
- {
- return m_attributes->SetGUID(guidKey, guidValue);
- }
-
- STDMETHODIMP SetString(
- REFGUID guidKey,
- LPCWSTR wszValue) override
- {
- return m_attributes->SetString(guidKey, wszValue);
- }
-
- STDMETHODIMP SetBlob(
- REFGUID guidKey,
- const UINT8 *pBuf,
- UINT32 cbBufSize) override
- {
- return m_attributes->SetBlob(guidKey, pBuf, cbBufSize);
- }
-
- STDMETHODIMP SetUnknown(
- REFGUID guidKey,
- IUnknown *pUnknown) override
- {
- return m_attributes->SetUnknown(guidKey, pUnknown);
- }
-
- STDMETHODIMP LockStore(void) override
- {
- return m_attributes->LockStore();
- }
-
- STDMETHODIMP UnlockStore(void) override
- {
- return m_attributes->UnlockStore();
- }
-
- STDMETHODIMP GetCount(
- UINT32 *pcItems) override
- {
- return m_attributes->GetCount(pcItems);
- }
-
- STDMETHODIMP GetItemByIndex(
- UINT32 unIndex,
- GUID *pguidKey,
- PROPVARIANT *pValue) override
- {
- return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue);
- }
-
- STDMETHODIMP CopyAllItems(
- IMFAttributes *pDest) override
- {
- return m_attributes->CopyAllItems(pDest);
- }
-
- /////////////////////////////////
- void setSink(QVideoSink *sink)
- {
- QMutexLocker locker(&m_mutex);
- if (m_videoSink == sink)
- return;
-
- m_videoSink = sink;
-
- if (!m_sink)
- return;
- m_sink->setSurface(m_videoSink);
- }
-
- void present()
- {
- QMutexLocker locker(&m_mutex);
- if (!m_sink)
- return;
- m_sink->present();
- }
-
- void clearScheduledFrame()
- {
- QMutexLocker locker(&m_mutex);
- if (!m_sink)
- return;
- m_sink->clearScheduledFrame();
- }
-
- MFTIME getTime()
- {
- if (m_sink)
- return m_sink->getTime();
- return 0;
- }
-
- float getPlayRate()
- {
- if (m_sink)
- return m_sink->getPlayRate();
- return 1;
- }
-
- private:
- long m_cRef;
- bool m_shutdown;
- MediaSink *m_sink;
- MFVideoRendererControl *m_rendererControl;
- IMFAttributes *m_attributes;
- QVideoSink *m_videoSink;
- QMutex m_mutex;
- };
-}
+QT_BEGIN_NAMESPACE
class EVRCustomPresenterActivate : public MFAbstractActivate
{
public:
EVRCustomPresenterActivate(QVideoSink *sink);
- ~EVRCustomPresenterActivate()
- { }
STDMETHODIMP ActivateObject(REFIID riid, void **ppv) override;
STDMETHODIMP ShutdownObject() override;
STDMETHODIMP DetachObject() override;
void setSink(QVideoSink *sink);
+ void setCropRect(QRect cropRect);
private:
+ // Destructor is not public. Caller should call Release.
+ ~EVRCustomPresenterActivate() override { }
+
EVRCustomPresenter *m_presenter;
QVideoSink *m_videoSink;
+ QRect m_cropRect;
QMutex m_mutex;
};
@@ -2151,10 +40,10 @@ MFVideoRendererControl::MFVideoRendererControl(QObject *parent)
MFVideoRendererControl::~MFVideoRendererControl()
{
- clear();
+ releaseActivate();
}
-void MFVideoRendererControl::clear()
+void MFVideoRendererControl::releaseActivate()
{
if (m_sink)
m_sink->platformVideoSink()->setVideoFrame(QVideoFrame());
@@ -2172,82 +61,35 @@ void MFVideoRendererControl::clear()
m_currentActivate = NULL;
}
-void MFVideoRendererControl::releaseActivate()
-{
- clear();
-}
-
-QVideoSink *MFVideoRendererControl::sink() const
-{
- return m_sink;
-}
-
void MFVideoRendererControl::setSink(QVideoSink *sink)
{
m_sink = sink;
if (m_presenterActivate)
m_presenterActivate->setSink(m_sink);
- else if (m_currentActivate)
- static_cast<VideoRendererActivate*>(m_currentActivate)->setSink(m_sink);
-}
-
-void MFVideoRendererControl::customEvent(QEvent *event)
-{
- if (m_presenterActivate)
- return;
-
- if (!m_currentActivate)
- return;
-
- if (event->type() == QEvent::Type(MediaStream::PresentSurface)) {
- MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime();
- MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime();
- float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate();
- if (!qFuzzyIsNull(playRate) && targetTime != currentTime) {
- // If the scheduled frame is too late, skip it
- const int interval = ((targetTime - currentTime) / 10000) / playRate;
- if (interval < 0)
- static_cast<VideoRendererActivate*>(m_currentActivate)->clearScheduledFrame();
- else
- QTimer::singleShot(interval, this, SLOT(present()));
- } else {
- present();
- }
- return;
- }
- QObject::customEvent(event);
}
-void MFVideoRendererControl::present()
+void MFVideoRendererControl::setCropRect(const QRect &cropRect)
{
if (m_presenterActivate)
- return;
-
- if (m_currentActivate)
- static_cast<VideoRendererActivate*>(m_currentActivate)->present();
+ m_presenterActivate->setCropRect(cropRect);
}
IMFActivate* MFVideoRendererControl::createActivate()
{
- clear();
+ releaseActivate();
if (m_sink) {
// Create the EVR media sink, but replace the presenter with our own
if (SUCCEEDED(MFCreateVideoRendererActivate(::GetShellWindow(), &m_currentActivate))) {
m_presenterActivate = new EVRCustomPresenterActivate(m_sink);
m_currentActivate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, m_presenterActivate);
- } else {
- m_currentActivate = new VideoRendererActivate(this);
}
}
- setSink(m_sink);
-
return m_currentActivate;
}
-
EVRCustomPresenterActivate::EVRCustomPresenterActivate(QVideoSink *sink)
: MFAbstractActivate()
, m_presenter(0)
@@ -2261,6 +103,7 @@ HRESULT EVRCustomPresenterActivate::ActivateObject(REFIID riid, void **ppv)
QMutexLocker locker(&m_mutex);
if (!m_presenter) {
m_presenter = new EVRCustomPresenter(m_videoSink);
+ m_presenter->setCropRect(m_cropRect);
}
return m_presenter->QueryInterface(riid, ppv);
}
@@ -2294,5 +137,16 @@ void EVRCustomPresenterActivate::setSink(QVideoSink *sink)
m_presenter->setSink(sink);
}
-#include "moc_mfvideorenderercontrol_p.cpp"
-#include "mfvideorenderercontrol.moc"
+void EVRCustomPresenterActivate::setCropRect(QRect cropRect)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_cropRect == cropRect)
+ return;
+
+ m_cropRect = cropRect;
+
+ if (m_presenter)
+ m_presenter->setCropRect(cropRect);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/windows/player/mfvideorenderercontrol_p.h b/src/plugins/multimedia/windows/player/mfvideorenderercontrol_p.h
index 9b48803d9..ed5195240 100644
--- a/src/plugins/multimedia/windows/player/mfvideorenderercontrol_p.h
+++ b/src/plugins/multimedia/windows/player/mfvideorenderercontrol_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef MFVIDEORENDERERCONTROL_H
#define MFVIDEORENDERERCONTROL_H
@@ -51,45 +15,33 @@
// We mean it.
//
-#include "qobject.h"
-#include <mfapi.h>
-#include <mfidl.h>
-
-QT_USE_NAMESPACE
-
-class EVRCustomPresenterActivate;
+#include <qobject.h>
+#include <qpointer.h>
+#include <qrect.h>
+#include <mfobjects.h>
QT_BEGIN_NAMESPACE
+class EVRCustomPresenterActivate;
class QVideoSink;
-QT_END_NAMESPACE
class MFVideoRendererControl : public QObject
{
- Q_OBJECT
public:
MFVideoRendererControl(QObject *parent = 0);
~MFVideoRendererControl();
- QVideoSink *sink() const;
void setSink(QVideoSink *surface);
+ void setCropRect(const QRect &cropRect);
IMFActivate* createActivate();
void releaseActivate();
-protected:
- void customEvent(QEvent *event) override;
-
-private Q_SLOTS:
- void present();
-
private:
- void clear();
-
- QVideoSink *m_sink = nullptr;
+ QPointer<QVideoSink> m_sink;
IMFActivate *m_currentActivate = nullptr;
- IMFSampleGrabberSinkCallback *m_callback = nullptr;
-
EVRCustomPresenterActivate *m_presenterActivate = nullptr;
};
+QT_END_NAMESPACE
+
#endif
diff --git a/src/plugins/multimedia/windows/player/samplegrabber.cpp b/src/plugins/multimedia/windows/player/samplegrabber.cpp
deleted file mode 100644
index 93bec4ad6..000000000
--- a/src/plugins/multimedia/windows/player/samplegrabber.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "samplegrabber_p.h"
-
-STDMETHODIMP SampleGrabberCallback::QueryInterface(REFIID riid, void** ppv)
-{
- if (!ppv)
- return E_POINTER;
- if (riid == IID_IMFSampleGrabberSinkCallback) {
- *ppv = static_cast<IMFSampleGrabberSinkCallback*>(this);
- } else if (riid == IID_IMFClockStateSink) {
- *ppv = static_cast<IMFClockStateSink*>(this);
- } else if (riid == IID_IUnknown) {
- *ppv = static_cast<IUnknown*>(this);
- } else {
- *ppv = NULL;
- return E_NOINTERFACE;
- }
- AddRef();
- return S_OK;
-}
-
-STDMETHODIMP_(ULONG) SampleGrabberCallback::AddRef()
-{
- return InterlockedIncrement(&m_cRef);
-}
-
-STDMETHODIMP_(ULONG) SampleGrabberCallback::Release()
-{
- ULONG cRef = InterlockedDecrement(&m_cRef);
- if (cRef == 0) {
- delete this;
- }
- return cRef;
-
-}
-
-// IMFClockStateSink methods.
-
-STDMETHODIMP SampleGrabberCallback::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset)
-{
- Q_UNUSED(hnsSystemTime);
- Q_UNUSED(llClockStartOffset);
- return S_OK;
-}
-
-STDMETHODIMP SampleGrabberCallback::OnClockStop(MFTIME hnsSystemTime)
-{
- Q_UNUSED(hnsSystemTime);
- return S_OK;
-}
-
-STDMETHODIMP SampleGrabberCallback::OnClockPause(MFTIME hnsSystemTime)
-{
- Q_UNUSED(hnsSystemTime);
- return S_OK;
-}
-
-STDMETHODIMP SampleGrabberCallback::OnClockRestart(MFTIME hnsSystemTime)
-{
- Q_UNUSED(hnsSystemTime);
- return S_OK;
-}
-
-STDMETHODIMP SampleGrabberCallback::OnClockSetRate(MFTIME hnsSystemTime, float flRate)
-{
- Q_UNUSED(hnsSystemTime);
- Q_UNUSED(flRate);
- return S_OK;
-}
-
-// IMFSampleGrabberSink methods.
-
-STDMETHODIMP SampleGrabberCallback::OnSetPresentationClock(IMFPresentationClock* pClock)
-{
- Q_UNUSED(pClock);
- return S_OK;
-}
-
-STDMETHODIMP SampleGrabberCallback::OnShutdown()
-{
- return S_OK;
-}
-
-//void AudioSampleGrabberCallback::addProbe(MFAudioProbeControl* probe)
-//{
-// QMutexLocker locker(&m_audioProbeMutex);
-
-// if (m_audioProbes.contains(probe))
-// return;
-
-// m_audioProbes.append(probe);
-//}
-
-//void AudioSampleGrabberCallback::removeProbe(MFAudioProbeControl* probe)
-//{
-// QMutexLocker locker(&m_audioProbeMutex);
-// m_audioProbes.removeOne(probe);
-//}
-
-void AudioSampleGrabberCallback::setFormat(const QAudioFormat& format)
-{
- m_format = format;
-}
-
-STDMETHODIMP AudioSampleGrabberCallback::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags,
- LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
- DWORD dwSampleSize)
-{
- Q_UNUSED(dwSampleFlags);
- Q_UNUSED(llSampleTime);
- Q_UNUSED(llSampleDuration);
- Q_UNUSED(pSampleBuffer);
- Q_UNUSED(dwSampleSize);
-
- if (guidMajorMediaType != GUID_NULL && guidMajorMediaType != MFMediaType_Audio)
- return S_OK;
-
- QMutexLocker locker(&m_audioProbeMutex);
-
-// if (m_audioProbes.isEmpty())
- return S_OK;
-
- // Check if sample has a presentation time
- if (llSampleTime == _I64_MAX) {
- // Set default QAudioBuffer start time
- llSampleTime = -1;
- } else {
- // WMF uses 100-nanosecond units, Qt uses microseconds
- llSampleTime /= 10;
- }
-
-// for (MFAudioProbeControl* probe : qAsConst(m_audioProbes))
-// probe->bufferProbed((const char*)pSampleBuffer, dwSampleSize, m_format, llSampleTime);
-
- return S_OK;
-}
diff --git a/src/plugins/multimedia/windows/player/samplegrabber_p.h b/src/plugins/multimedia/windows/player/samplegrabber_p.h
deleted file mode 100644
index ad522ade8..000000000
--- a/src/plugins/multimedia/windows/player/samplegrabber_p.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef SAMPLEGRABBER_H
-#define SAMPLEGRABBER_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qmutex.h>
-#include <QtCore/qlist.h>
-#include <QtMultimedia/qaudioformat.h>
-#include <mfapi.h>
-#include <mfidl.h>
-
-class SampleGrabberCallback : public IMFSampleGrabberSinkCallback
-{
-public:
- // IUnknown methods
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override;
- STDMETHODIMP_(ULONG) AddRef() override;
- STDMETHODIMP_(ULONG) Release() override;
-
- // IMFClockStateSink methods
- STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) override;
- STDMETHODIMP OnClockStop(MFTIME hnsSystemTime) override;
- STDMETHODIMP OnClockPause(MFTIME hnsSystemTime) override;
- STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime) override;
- STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate) override;
-
- // IMFSampleGrabberSinkCallback methods
- STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock) override;
- STDMETHODIMP OnShutdown() override;
-
-protected:
- SampleGrabberCallback() : m_cRef(1) {}
-
-public:
- virtual ~SampleGrabberCallback() {}
-
-private:
- long m_cRef;
-};
-
-class AudioSampleGrabberCallback: public SampleGrabberCallback {
-public:
- void setFormat(const QAudioFormat& format);
-
- STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags,
- LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
- DWORD dwSampleSize) override;
-
-private:
-// QList<MFAudioProbeControl*> m_audioProbes;
- QMutex m_audioProbeMutex;
- QAudioFormat m_format;
-};
-
-#endif // SAMPLEGRABBER_H
diff --git a/src/plugins/multimedia/windows/qwindowsformatinfo.cpp b/src/plugins/multimedia/windows/qwindowsformatinfo.cpp
index 13f87161e..6ef1f7f7f 100644
--- a/src/plugins/multimedia/windows/qwindowsformatinfo.cpp
+++ b/src/plugins/multimedia/windows/qwindowsformatinfo.cpp
@@ -1,116 +1,85 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsformatinfo_p.h"
#include <mfapi.h>
#include <mftransform.h>
-#include <private/qwindowsiupointer_p.h>
+#include <private/qcomptr_p.h>
#include <private/qwindowsmultimediautils_p.h>
+#include <private/qcomtaskresource_p.h>
#include <QtCore/qlist.h>
#include <QtCore/qset.h>
+#include <QtCore/qhash.h>
#include <QtGui/qimagewriter.h>
QT_BEGIN_NAMESPACE
+namespace {
+
template<typename T>
-static T codecForFormat(GUID format) = delete;
+using CheckedCodecs = QHash<QPair<T, QMediaFormat::ConversionMode>, bool>;
-template<>
-QMediaFormat::AudioCodec codecForFormat(GUID format)
+bool isSupportedMFT(const GUID &category, const MFT_REGISTER_TYPE_INFO &type, QMediaFormat::ConversionMode mode)
{
- return QWindowsMultimediaUtils::codecForAudioFormat(format);
+ UINT32 count = 0;
+ IMFActivate **activateArrayRaw = nullptr;
+ HRESULT hr = MFTEnumEx(
+ category,
+ MFT_ENUM_FLAG_ALL,
+ (mode == QMediaFormat::Encode) ? nullptr : &type, // Input type
+ (mode == QMediaFormat::Encode) ? &type : nullptr, // Output type
+ &activateArrayRaw,
+ &count
+ );
+
+ if (FAILED(hr))
+ return false;
+
+ QComTaskResource<IMFActivate *[], QComDeleter> activateArray(activateArrayRaw, count);
+ for (UINT32 i = 0; i < count; ++i) {
+ ComPtr<IMFTransform> transform;
+ hr = activateArray[i]->ActivateObject(IID_PPV_ARGS(transform.GetAddressOf()));
+ if (SUCCEEDED(hr))
+ return true;
+ }
+
+ return false;
}
-template<>
-QMediaFormat::VideoCodec codecForFormat(GUID format)
+bool isSupportedCodec(QMediaFormat::AudioCodec codec, QMediaFormat::ConversionMode mode)
{
- return QWindowsMultimediaUtils::codecForVideoFormat(format);
+ return isSupportedMFT((mode == QMediaFormat::Encode) ? MFT_CATEGORY_AUDIO_ENCODER : MFT_CATEGORY_AUDIO_DECODER,
+ { MFMediaType_Audio, QWindowsMultimediaUtils::audioFormatForCodec(codec) },
+ mode);
}
-template<typename T>
-static QSet<T> getCodecSet(GUID category)
+bool isSupportedCodec(QMediaFormat::VideoCodec codec, QMediaFormat::ConversionMode mode)
{
- QSet<T> codecSet;
- IMFActivate **activateArray = nullptr;
- UINT32 num = 0;
-
- HRESULT hr = MFTEnumEx(category, MFT_ENUM_FLAG_ALL, nullptr, nullptr, &activateArray, &num);
-
- if (SUCCEEDED(hr)) {
- for (UINT32 i = 0; i < num; ++i) {
- QWindowsIUPointer<IMFTransform> transform;
- UINT32 typeIndex = 0;
-
- hr = activateArray[i]->ActivateObject(IID_PPV_ARGS(transform.address()));
-
- while (SUCCEEDED(hr)) {
- QWindowsIUPointer<IMFMediaType> mediaType;
-
- if (category == MFT_CATEGORY_AUDIO_ENCODER || category == MFT_CATEGORY_VIDEO_ENCODER)
- hr = transform->GetOutputAvailableType(0, typeIndex++, mediaType.address());
- else
- hr = transform->GetInputAvailableType(0, typeIndex++, mediaType.address());
-
- if (SUCCEEDED(hr)) {
- GUID subtype = GUID_NULL;
- hr = mediaType->GetGUID(MF_MT_SUBTYPE, &subtype);
- if (SUCCEEDED(hr))
- codecSet.insert(codecForFormat<T>(subtype));
- }
- }
- }
+ return isSupportedMFT((mode == QMediaFormat::Encode) ? MFT_CATEGORY_VIDEO_ENCODER : MFT_CATEGORY_VIDEO_DECODER,
+ { MFMediaType_Video, QWindowsMultimediaUtils::videoFormatForCodec(codec) },
+ mode);
+}
+
+template <typename T>
+bool isSupportedCodec(T codec, QMediaFormat::ConversionMode m, CheckedCodecs<T> &checkedCodecs)
+{
+ if (auto it = checkedCodecs.constFind(qMakePair(codec, m)); it != checkedCodecs.constEnd())
+ return it.value();
- for (UINT32 i = 0; i < num; ++i)
- activateArray[i]->Release();
+ const bool supported = isSupportedCodec(codec, m);
- CoTaskMemFree(activateArray);
- }
+ checkedCodecs.insert(qMakePair(codec, m), supported);
+ return supported;
+}
- return codecSet;
}
static QList<QImageCapture::FileFormat> getImageFormatList()
{
QList<QImageCapture::FileFormat> list;
- auto formats = QImageWriter::supportedImageFormats();
+ const auto formats = QImageWriter::supportedImageFormats();
for (const auto &f : formats) {
auto format = QString::fromUtf8(f);
@@ -185,31 +154,25 @@ QWindowsFormatInfo::QWindowsFormatInfo()
QMediaFormat::WMV,
};
- const auto audioDecoders = getCodecSet<QMediaFormat::AudioCodec>(MFT_CATEGORY_AUDIO_DECODER);
- const auto audioEncoders = getCodecSet<QMediaFormat::AudioCodec>(MFT_CATEGORY_AUDIO_ENCODER);
- const auto videoDecoders = getCodecSet<QMediaFormat::VideoCodec>(MFT_CATEGORY_VIDEO_DECODER);
- const auto videoEncoders = getCodecSet<QMediaFormat::VideoCodec>(MFT_CATEGORY_VIDEO_ENCODER);
+ CheckedCodecs<QMediaFormat::AudioCodec> checkedAudioCodecs;
+ CheckedCodecs<QMediaFormat::VideoCodec> checkedVideoCodecs;
- for (const auto &codecMap : containerTable) {
-
- const QSet<QMediaFormat::AudioCodec> mapAudioSet(codecMap.audio.cbegin(), codecMap.audio.cend());
- const QSet<QMediaFormat::VideoCodec> mapVideoSet(codecMap.video.cbegin(), codecMap.video.cend());
+ auto ensureCodecs = [&] (CodecMap &codecs, QMediaFormat::ConversionMode mode) {
+ codecs.audio.removeIf([&] (auto codec) { return !isSupportedCodec(codec, mode, checkedAudioCodecs); });
+ codecs.video.removeIf([&] (auto codec) { return !isSupportedCodec(codec, mode, checkedVideoCodecs); });
+ return !codecs.video.empty() || !codecs.audio.empty();
+ };
+ for (const auto &codecMap : containerTable) {
if (decoderFormats.contains(codecMap.format)) {
- CodecMap m;
- m.format = codecMap.format;
- m.audio = (audioDecoders & mapAudioSet).values();
- m.video = (videoDecoders & mapVideoSet).values();
- if (!m.video.empty() || !m.audio.empty())
+ auto m = codecMap;
+ if (ensureCodecs(m, QMediaFormat::Decode))
decoders.append(m);
}
if (encoderFormats.contains(codecMap.format)) {
- CodecMap m;
- m.format = codecMap.format;
- m.audio = (audioEncoders & mapAudioSet).values();
- m.video = (videoEncoders & mapVideoSet).values();
- if (!m.video.empty() || !m.audio.empty())
+ auto m = codecMap;
+ if (ensureCodecs(m, QMediaFormat::Encode))
encoders.append(m);
}
}
diff --git a/src/plugins/multimedia/windows/qwindowsformatinfo_p.h b/src/plugins/multimedia/windows/qwindowsformatinfo_p.h
index eeca80a6e..31e6dd986 100644
--- a/src/plugins/multimedia/windows/qwindowsformatinfo_p.h
+++ b/src/plugins/multimedia/windows/qwindowsformatinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSFORMATSINFO_H
#define QWINDOWSFORMATSINFO_H
diff --git a/src/plugins/multimedia/windows/qwindowsintegration.cpp b/src/plugins/multimedia/windows/qwindowsintegration.cpp
index d9238ac1c..1053f3c95 100644
--- a/src/plugins/multimedia/windows/qwindowsintegration.cpp
+++ b/src/plugins/multimedia/windows/qwindowsintegration.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsintegration_p.h"
#include <private/qwindowsmediadevices_p.h>
@@ -63,66 +27,66 @@ public:
QPlatformMediaIntegration* create(const QString &name) override
{
- if (name == QLatin1String("windows"))
+ if (name == u"windows")
return new QWindowsMediaIntegration;
return nullptr;
}
};
QWindowsMediaIntegration::QWindowsMediaIntegration()
+ : QPlatformMediaIntegration(QLatin1String("windows"))
{
CoInitialize(NULL);
MFStartup(MF_VERSION);
-
- m_videoDevices = new QWindowsVideoDevices(this);
}
QWindowsMediaIntegration::~QWindowsMediaIntegration()
{
- delete m_formatInfo;
-
MFShutdown();
CoUninitialize();
}
-QPlatformMediaFormatInfo *QWindowsMediaIntegration::formatInfo()
+QPlatformMediaFormatInfo *QWindowsMediaIntegration::createFormatInfo()
+{
+ return new QWindowsFormatInfo();
+}
+
+QPlatformVideoDevices *QWindowsMediaIntegration::createVideoDevices()
{
- if (!m_formatInfo)
- m_formatInfo = new QWindowsFormatInfo();
- return m_formatInfo;
+ return new QWindowsVideoDevices(this);
}
-QPlatformMediaCaptureSession *QWindowsMediaIntegration::createCaptureSession()
+QMaybe<QPlatformMediaCaptureSession *> QWindowsMediaIntegration::createCaptureSession()
{
return new QWindowsMediaCaptureService();
}
-QPlatformAudioDecoder *QWindowsMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
+QMaybe<QPlatformAudioDecoder *> QWindowsMediaIntegration::createAudioDecoder(QAudioDecoder *decoder)
{
return new MFAudioDecoderControl(decoder);
}
-QPlatformMediaPlayer *QWindowsMediaIntegration::createPlayer(QMediaPlayer *parent)
+QMaybe<QPlatformMediaPlayer *> QWindowsMediaIntegration::createPlayer(QMediaPlayer *parent)
{
return new MFPlayerControl(parent);
}
-QPlatformCamera *QWindowsMediaIntegration::createCamera(QCamera *camera)
+QMaybe<QPlatformCamera *> QWindowsMediaIntegration::createCamera(QCamera *camera)
{
return new QWindowsCamera(camera);
}
-QPlatformMediaRecorder *QWindowsMediaIntegration::createRecorder(QMediaRecorder *recorder)
+QMaybe<QPlatformMediaRecorder *> QWindowsMediaIntegration::createRecorder(QMediaRecorder *recorder)
{
return new QWindowsMediaEncoder(recorder);
}
-QPlatformImageCapture *QWindowsMediaIntegration::createImageCapture(QImageCapture *imageCapture)
+QMaybe<QPlatformImageCapture *> QWindowsMediaIntegration::createImageCapture(QImageCapture *imageCapture)
{
return new QWindowsImageCapture(imageCapture);
}
-QPlatformVideoSink *QWindowsMediaIntegration::createVideoSink(QVideoSink *sink)
+QMaybe<QPlatformVideoSink *> QWindowsMediaIntegration::createVideoSink(QVideoSink *sink)
{
return new MFEvrVideoWindowControl(sink);
}
diff --git a/src/plugins/multimedia/windows/qwindowsintegration_p.h b/src/plugins/multimedia/windows/qwindowsintegration_p.h
index a685298ab..29498fa42 100644
--- a/src/plugins/multimedia/windows/qwindowsintegration_p.h
+++ b/src/plugins/multimedia/windows/qwindowsintegration_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSINTEGRATION_H
#define QWINDOWSINTEGRATION_H
@@ -61,23 +25,25 @@ class QWindowsFormatInfo;
class QWindowsMediaIntegration : public QPlatformMediaIntegration
{
+ Q_OBJECT
public:
QWindowsMediaIntegration();
~QWindowsMediaIntegration();
- QPlatformMediaFormatInfo *formatInfo() override;
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
- QPlatformMediaCaptureSession *createCaptureSession() override;
+ QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *decoder) override;
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *parent) override;
+ QMaybe<QPlatformCamera *> createCamera(QCamera *camera) override;
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *recorder) override;
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *imageCapture) override;
- QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *decoder) override;
- QPlatformMediaPlayer *createPlayer(QMediaPlayer *parent) override;
- QPlatformCamera *createCamera(QCamera *camera) override;
- QPlatformMediaRecorder *createRecorder(QMediaRecorder *recorder) override;
- QPlatformImageCapture *createImageCapture(QImageCapture *imageCapture) override;
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *sink) override;
- QPlatformVideoSink *createVideoSink(QVideoSink *sink) override;
+protected:
+ QPlatformMediaFormatInfo *createFormatInfo() override;
- QWindowsFormatInfo *m_formatInfo = nullptr;
+ QPlatformVideoDevices *createVideoDevices() override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/windows/qwindowsvideodevices.cpp b/src/plugins/multimedia/windows/qwindowsvideodevices.cpp
index e35ab3597..8e5081d3b 100644
--- a/src/plugins/multimedia/windows/qwindowsvideodevices.cpp
+++ b/src/plugins/multimedia/windows/qwindowsvideodevices.cpp
@@ -1,53 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindowsvideodevices_p.h"
#include <private/qcameradevice_p.h>
#include <private/qwindowsmfdefs_p.h>
#include <private/qwindowsmultimediautils_p.h>
+#include <private/qcomptr_p.h>
+#include <private/qcomtaskresource_p.h>
-#include <Dbt.h>
+#include <dbt.h>
#include <mfapi.h>
#include <mfreadwrite.h>
-#include <Mferror.h>
+#include <mferror.h>
+
+QT_BEGIN_NAMESPACE
LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
@@ -57,7 +25,7 @@ LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND hWnd, UINT message, WPARA
auto wmd = reinterpret_cast<QWindowsVideoDevices *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
if (wmd) {
if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) {
- wmd->videoInputsChanged();
+ emit wmd->videoInputsChanged();
}
}
}
@@ -92,6 +60,8 @@ static HWND createMessageOnlyWindow()
QWindowsVideoDevices::QWindowsVideoDevices(QPlatformMediaIntegration *integration)
: QPlatformVideoDevices(integration)
{
+ CoInitialize(nullptr);
+
m_videoDeviceMsgWindow = createMessageOnlyWindow();
if (m_videoDeviceMsgWindow) {
SetWindowLongPtr(m_videoDeviceMsgWindow, GWLP_USERDATA, (LONG_PTR)this);
@@ -126,127 +96,133 @@ QWindowsVideoDevices::~QWindowsVideoDevices()
DestroyWindow(m_videoDeviceMsgWindow);
UnregisterClass(windowClassName, GetModuleHandle(nullptr));
}
+
+ CoUninitialize();
}
-QList<QCameraDevice> QWindowsVideoDevices::videoDevices() const
+static std::optional<QCameraFormat> createCameraFormat(IMFMediaType *mediaFormat)
{
- QList<QCameraDevice> cameras;
+ GUID subtype = GUID_NULL;
+ if (FAILED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype)))
+ return {};
- IMFAttributes *pAttributes = NULL;
- IMFActivate **ppDevices = NULL;
+ auto pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
+ if (pixelFormat == QVideoFrameFormat::Format_Invalid)
+ return {};
- // Create an attribute store to specify the enumeration parameters.
- HRESULT hr = MFCreateAttributes(&pAttributes, 1);
+ UINT32 width = 0u;
+ UINT32 height = 0u;
+ if (FAILED(MFGetAttributeSize(mediaFormat, MF_MT_FRAME_SIZE, &width, &height)))
+ return {};
+ QSize resolution{ int(width), int(height) };
+
+ UINT32 num = 0u;
+ UINT32 den = 0u;
+ float minFr = 0.f;
+ float maxFr = 0.f;
+
+ if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MIN, &num, &den)))
+ minFr = float(num) / float(den);
+
+ if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MAX, &num, &den)))
+ maxFr = float(num) / float(den);
+
+ auto *f = new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution, minFr, maxFr };
+ return f->create();
+}
+
+static QString getString(IMFActivate *device, const IID &id)
+{
+ QComTaskResource<WCHAR> str;
+ UINT32 length = 0;
+ HRESULT hr = device->GetAllocatedString(id, str.address(), &length);
if (SUCCEEDED(hr)) {
- // Source type: video capture devices
- hr = pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
- MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
-
- if (SUCCEEDED(hr)) {
- // Enumerate devices.
- UINT32 count;
- hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count);
- if (SUCCEEDED(hr)) {
- // Iterate through devices.
- for (int index = 0; index < int(count); index++) {
- QCameraDevicePrivate *info = new QCameraDevicePrivate;
-
- IMFMediaSource *pSource = NULL;
- IMFSourceReader *reader = NULL;
-
- WCHAR *deviceName = NULL;
- UINT32 deviceNameLength = 0;
- UINT32 deviceIdLength = 0;
- WCHAR *deviceId = NULL;
-
- hr = ppDevices[index]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
- &deviceName, &deviceNameLength);
- if (SUCCEEDED(hr))
- info->description = QString::fromWCharArray(deviceName);
- CoTaskMemFree(deviceName);
-
- hr = ppDevices[index]->GetAllocatedString(
- MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &deviceId,
- &deviceIdLength);
- if (SUCCEEDED(hr))
- info->id = QString::fromWCharArray(deviceId).toUtf8();
- CoTaskMemFree(deviceId);
-
- // Create the media source object.
- ppDevices[index]->ActivateObject(IID_PPV_ARGS(&pSource));
- // Create the media source reader.
- hr = MFCreateSourceReaderFromMediaSource(pSource, NULL, &reader);
- if (SUCCEEDED(hr)) {
- QList<QSize> photoResolutions;
- QList<QCameraFormat> videoFormats;
-
- DWORD dwMediaTypeIndex = 0;
- IMFMediaType *mediaFormat = NULL;
- GUID subtype = GUID_NULL;
- HRESULT mediaFormatResult = S_OK;
-
- UINT32 frameRateMin = 0u;
- UINT32 frameRateMax = 0u;
- UINT32 denominator = 0u;
- UINT32 width = 0u;
- UINT32 height = 0u;
-
- while (SUCCEEDED(mediaFormatResult)) {
- // Loop through the supported formats for the video device
- mediaFormatResult = reader->GetNativeMediaType(
- (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, dwMediaTypeIndex,
- &mediaFormat);
- if (mediaFormatResult == MF_E_NO_MORE_TYPES)
- break;
- else if (SUCCEEDED(mediaFormatResult)) {
- QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid;
- QSize resolution;
- float minFr = .0;
- float maxFr = .0;
-
- if (SUCCEEDED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype)))
- pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
-
- if (SUCCEEDED(MFGetAttributeSize(mediaFormat, MF_MT_FRAME_SIZE, &width,
- &height))) {
- resolution.rheight() = (int)height;
- resolution.rwidth() = (int)width;
- photoResolutions << resolution;
- }
-
- if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MIN,
- &frameRateMin, &denominator)))
- minFr = qreal(frameRateMin) / denominator;
- if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MAX,
- &frameRateMax, &denominator)))
- maxFr = qreal(frameRateMax) / denominator;
-
- auto *f = new QCameraFormatPrivate { QSharedData(), pixelFormat,
- resolution, minFr, maxFr };
- videoFormats << f->create();
- }
- ++dwMediaTypeIndex;
- }
- if (mediaFormat)
- mediaFormat->Release();
-
- info->videoFormats = videoFormats;
- info->photoResolutions = photoResolutions;
- }
- if (reader)
- reader->Release();
- cameras.append(info->create());
- }
- }
- for (DWORD i = 0; i < count; i++) {
- if (ppDevices[i])
- ppDevices[i]->Release();
+ return QString::fromWCharArray(str.get());
+ } else {
+ return {};
+ }
+}
+
+static std::optional<QCameraDevice> createCameraDevice(IMFActivate *device)
+{
+ auto info = std::make_unique<QCameraDevicePrivate>();
+ info->description = getString(device, MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME);
+ info->id = getString(device, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK).toUtf8();
+
+ IMFMediaSource *source = NULL;
+ HRESULT hr = device->ActivateObject(IID_PPV_ARGS(&source));
+ if (FAILED(hr))
+ return {};
+
+ ComPtr<IMFSourceReader> reader;
+ hr = MFCreateSourceReaderFromMediaSource(source, NULL, reader.GetAddressOf());
+ if (FAILED(hr))
+ return {};
+
+ QList<QSize> photoResolutions;
+ QList<QCameraFormat> videoFormats;
+ for (DWORD i = 0;; ++i) {
+ // Loop through the supported formats for the video device
+ ComPtr<IMFMediaType> mediaFormat;
+ hr = reader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
+ mediaFormat.GetAddressOf());
+ if (FAILED(hr))
+ break;
+
+ auto maybeCamera = createCameraFormat(mediaFormat.Get());
+ if (maybeCamera) {
+ videoFormats << *maybeCamera;
+ photoResolutions << maybeCamera->resolution();
+ }
+ }
+
+ info->videoFormats = videoFormats;
+ info->photoResolutions = photoResolutions;
+ return info.release()->create();
+}
+
+static QList<QCameraDevice> readCameraDevices(IMFAttributes *attr)
+{
+ QList<QCameraDevice> cameras;
+ UINT32 count = 0;
+ IMFActivate **devicesRaw = nullptr;
+ HRESULT hr = MFEnumDeviceSources(attr, &devicesRaw, &count);
+ if (SUCCEEDED(hr)) {
+ QComTaskResource<IMFActivate *[], QComDeleter> devices(devicesRaw, count);
+
+ for (UINT32 i = 0; i < count; i++) {
+ IMFActivate *device = devices[i];
+ if (device) {
+ auto maybeCamera = createCameraDevice(device);
+ if (maybeCamera)
+ cameras << *maybeCamera;
}
- CoTaskMemFree(ppDevices);
}
}
- if (pAttributes)
- pAttributes->Release();
+ return cameras;
+}
+
+QList<QCameraDevice> QWindowsVideoDevices::videoDevices() const
+{
+ QList<QCameraDevice> cameras;
+
+ ComPtr<IMFAttributes> attr;
+ HRESULT hr = MFCreateAttributes(attr.GetAddressOf(), 2);
+ if (FAILED(hr))
+ return {};
+
+ hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
+ MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
+ if (SUCCEEDED(hr)) {
+ cameras << readCameraDevices(attr.Get());
+
+ hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY,
+ QMM_KSCATEGORY_SENSOR_CAMERA);
+ if (SUCCEEDED(hr))
+ cameras << readCameraDevices(attr.Get());
+ }
return cameras;
}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/multimedia/windows/qwindowsvideodevices_p.h b/src/plugins/multimedia/windows/qwindowsvideodevices_p.h
index 10d2eabc5..f8f5ed920 100644
--- a/src/plugins/multimedia/windows/qwindowsvideodevices_p.h
+++ b/src/plugins/multimedia/windows/qwindowsvideodevices_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QWINDOWSVIDEODEVICES_H
#define QWINDOWSVIDEODEVICES_H
diff --git a/src/plugins/multimedia/windows/sourceresolver.cpp b/src/plugins/multimedia/windows/sourceresolver.cpp
index 93af15a74..52fb024be 100644
--- a/src/plugins/multimedia/windows/sourceresolver.cpp
+++ b/src/plugins/multimedia/windows/sourceresolver.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "mfstream_p.h"
#include "sourceresolver_p.h"
@@ -45,6 +9,8 @@
#include <QtCore/qdebug.h>
#include <QtMultimedia/qmediaplayer.h>
+QT_BEGIN_NAMESPACE
+
/*
SourceResolver is separated from MFPlayerSession to handle the work of resolving a media source
asynchronously. You call SourceResolver::load to request resolving a media source asynchronously,
@@ -323,3 +289,6 @@ bool SourceResolver::State::fromStream() const
return m_fromStream;
}
+QT_END_NAMESPACE
+
+#include "moc_sourceresolver_p.cpp"
diff --git a/src/plugins/multimedia/windows/sourceresolver_p.h b/src/plugins/multimedia/windows/sourceresolver_p.h
index 0aab4cc19..57ac6fc9d 100644
--- a/src/plugins/multimedia/windows/sourceresolver_p.h
+++ b/src/plugins/multimedia/windows/sourceresolver_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef SOURCERESOLVER_H
#define SOURCERESOLVER_H
@@ -54,6 +18,8 @@
#include "mfstream_p.h"
#include <QUrl>
+QT_BEGIN_NAMESPACE
+
class SourceResolver: public QObject, public IMFAsyncCallback
{
Q_OBJECT
@@ -112,4 +78,6 @@ private:
QMutex m_mutex;
};
+QT_END_NAMESPACE
+
#endif
diff --git a/src/plugins/videonode/CMakeLists.txt b/src/plugins/videonode/CMakeLists.txt
index 8b19f7f1f..176628276 100644
--- a/src/plugins/videonode/CMakeLists.txt
+++ b/src/plugins/videonode/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from videonode.pro.
if(QT_FEATURE_gpu_vivante)
diff --git a/src/plugins/videonode/imx6/CMakeLists.txt b/src/plugins/videonode/imx6/CMakeLists.txt
index 2a959db97..659f73469 100644
--- a/src/plugins/videonode/imx6/CMakeLists.txt
+++ b/src/plugins/videonode/imx6/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from imx6.pro.
#####################################################################
diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp
index 4e8a4e5d2..7ef983120 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp
+++ b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Pelagicore AG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Pelagicore AG
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
@@ -52,7 +16,9 @@
#include <QtMultimedia/private/qtmultimediaglobal_p.h>
#include "private/qgstvideobuffer_p.h"
+#if GST_CHECK_VERSION(1,14,0)
#include <gst/allocators/gstphysmemory.h>
+#endif
//#define QT_VIVANTE_VIDEO_DEBUG
diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.h b/src/plugins/videonode/imx6/qsgvivantevideomaterial.h
index 36397eaa9..0c3976e3a 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideomaterial.h
+++ b/src/plugins/videonode/imx6/qsgvivantevideomaterial.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Pelagicore AG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Pelagicore AG
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSGVIDEOMATERIAL_VIVMAP_H
#define QSGVIDEOMATERIAL_VIVMAP_H
diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp b/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp
index 9e529c3df..ed78192b6 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp
+++ b/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Pelagicore AG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Pelagicore AG
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsgvivantevideomaterialshader.h"
#include "qsgvivantevideonode.h"
diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h b/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h
index b0e19d28e..73edff7d2 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h
+++ b/src/plugins/videonode/imx6/qsgvivantevideomaterialshader.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Pelagicore AG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Pelagicore AG
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSGVIDEOMATERIALSHADER_VIVANTE_H
#define QSGVIDEOMATERIALSHADER_VIVANTE_H
diff --git a/src/plugins/videonode/imx6/qsgvivantevideonode.cpp b/src/plugins/videonode/imx6/qsgvivantevideonode.cpp
index b879c1435..a800c0e62 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideonode.cpp
+++ b/src/plugins/videonode/imx6/qsgvivantevideonode.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Pelagicore AG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Pelagicore AG
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
diff --git a/src/plugins/videonode/imx6/qsgvivantevideonode.h b/src/plugins/videonode/imx6/qsgvivantevideonode.h
index 24b9ee492..05ab4f25b 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideonode.h
+++ b/src/plugins/videonode/imx6/qsgvivantevideonode.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Pelagicore AG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Pelagicore AG
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSGVIDEONODE_VIVANTE_H
#define QSGVIDEONODE_VIVANTE_H
diff --git a/src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp b/src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp
index 8086f0190..ca6550776 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp
+++ b/src/plugins/videonode/imx6/qsgvivantevideonodefactory.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Pelagicore AG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Pelagicore AG
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsgvivantevideonodefactory.h"
#include "qsgvivantevideonode.h"
diff --git a/src/plugins/videonode/imx6/qsgvivantevideonodefactory.h b/src/plugins/videonode/imx6/qsgvivantevideonodefactory.h
index 0e90d89d8..d81582788 100644
--- a/src/plugins/videonode/imx6/qsgvivantevideonodefactory.h
+++ b/src/plugins/videonode/imx6/qsgvivantevideonodefactory.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Pelagicore AG
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Pelagicore AG
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSGVIDEONODEFACTORY_VIVANTE_H
#define QSGVIDEONODEFACTORY_VIVANTE_H
diff --git a/src/plugins/videonode/imx6/shaders/compile.bat b/src/plugins/videonode/imx6/shaders/compile.bat
index 712bee6c5..5827ee9c0 100755
--- a/src/plugins/videonode/imx6/shaders/compile.bat
+++ b/src/plugins/videonode/imx6/shaders/compile.bat
@@ -1,41 +1,5 @@
-:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
-::
:: Copyright (C) 2020 The Qt Company Ltd.
-:: Contact: https://www.qt.io/licensing/
-::
-:: This file is part of the QtQuick module of the Qt Toolkit.
-::
-:: $QT_BEGIN_LICENSE:LGPL$
-:: Commercial License Usage
-:: Licensees holding valid commercial Qt licenses may use this file in
-:: accordance with the commercial license agreement provided with the
-:: Software or, alternatively, in accordance with the terms contained in
-:: a written agreement between you and The Qt Company. For licensing terms
-:: and conditions see https://www.qt.io/terms-conditions. For further
-:: information use the contact form at https://www.qt.io/contact-us.
-::
-:: GNU Lesser General Public License Usage
-:: Alternatively, this file may be used under the terms of the GNU Lesser
-:: General Public License version 3 as published by the Free Software
-:: Foundation and appearing in the file LICENSE.LGPL3 included in the
-:: packaging of this file. Please review the following information to
-:: ensure the GNU Lesser General Public License version 3 requirements
-:: will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-::
-:: GNU General Public License Usage
-:: Alternatively, this file may be used under the terms of the GNU
-:: General Public License version 2.0 or (at your option) the GNU General
-:: Public license version 3 or any later version approved by the KDE Free
-:: Qt Foundation. The licenses are as published by the Free Software
-:: Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-:: included in the packaging of this file. Please review the following
-:: information to ensure the GNU General Public License requirements will
-:: be met: https://www.gnu.org/licenses/gpl-2.0.html and
-:: https://www.gnu.org/licenses/gpl-3.0.html.
-::
-:: $QT_END_LICENSE$
-::
-:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o rgba.vert.qsb rgba.vert
qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o rgba.frag.qsb rgba.frag
diff --git a/src/resonance-audio/CMakeLists.txt b/src/resonance-audio/CMakeLists.txt
index 3f959464c..1e967a117 100644
--- a/src/resonance-audio/CMakeLists.txt
+++ b/src/resonance-audio/CMakeLists.txt
@@ -1,3 +1,10 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(MINGW AND CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(NO_SIMD_DEFINES PFFFT_SIMD_DISABLE DISABLE_SIMD)
+endif()
+
set(PFFFT_DIR "../3rdparty/pffft/" CACHE PATH "Path to pffft library")
set(PFFFT_INCLUDE_DIR ${PFFFT_DIR})
set(PFFFT_SOURCE
@@ -198,7 +205,7 @@ qt_internal_add_3rdparty_library(BundledResonanceAudio
${RA_SOURCES}
${PFFFT_SOURCE}
${SADIE_HRTFS_SOURCE}
- resonance_audio_api_extensions.h resonance_audio_api_extensions.cpp
+ resonance_audio.h resonance_audio.cpp
INCLUDE_DIRECTORIES
${RA_TOPLEVEL_DIR}
${RA_SOURCE_DIR}
@@ -206,6 +213,25 @@ qt_internal_add_3rdparty_library(BundledResonanceAudio
${SADIE_HRTFS_DIR}
../3rdparty/eigen
)
+
+# Required by pffft on certain PowerPC archs
+qt_internal_extend_target(BundledResonanceAudio CONDITION GCC AND (${CMAKE_SYSTEM_PROCESSOR} MATCHES "(ppc|ppc64)$")
+ COMPILE_OPTIONS
+ -maltivec
+)
+
+# Required by eigen on certain PowerPC archs
+qt_internal_extend_target(BundledResonanceAudio CONDITION (${CMAKE_SYSTEM_PROCESSOR} MATCHES "(ppc|ppc64)$")
+ COMPILE_OPTIONS
+ -mvsx
+)
+
+# Use fallback mode if SSE is not available
+qt_internal_extend_target(BundledResonanceAudio CONDITION (${CMAKE_SYSTEM_PROCESSOR} MATCHES "i[3-6]86$")
+ COMPILE_OPTIONS
+ -DPFFFT_SIMD_DISABLE
+)
+
qt_disable_warnings(BundledResonanceAudio)
qt_set_symbol_visibility_hidden(BundledResonanceAudio)
diff --git a/src/resonance-audio/resonance_audio.cpp b/src/resonance-audio/resonance_audio.cpp
new file mode 100644
index 000000000..5216da048
--- /dev/null
+++ b/src/resonance-audio/resonance_audio.cpp
@@ -0,0 +1,42 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include "resonance_audio.h"
+#include "graph/resonance_audio_api_impl.h"
+#include "graph/graph_manager.h"
+
+namespace vraudio
+{
+
+ResonanceAudio::ResonanceAudio(size_t num_channels, size_t frames_per_buffer, int sample_rate_hz)
+{
+ api = CreateResonanceAudioApi(num_channels, frames_per_buffer, sample_rate_hz);
+ impl = static_cast<ResonanceAudioApiImpl *>(api);
+}
+
+ResonanceAudio::~ResonanceAudio()
+{
+ delete api;
+}
+
+int ResonanceAudio::getAmbisonicOutput(const float *buffers[], const float *reverb[], int nChannels)
+{
+ impl->ProcessNextBuffer();
+ auto *buffer = impl->GetAmbisonicOutputBuffer();
+ if (!buffer || nChannels != buffer->num_channels())
+ return -1;
+
+ for (int i = 0; i < nChannels; ++i) {
+ buffers[i] = buffer->begin()[i].begin();
+ }
+
+ if (roomEffectsEnabled) {
+ const vraudio::AudioBuffer *reverbBuffer = impl->GetReverbBuffer();
+ for (int i = 0; i < 2; ++i) {
+ reverb[i] = reverbBuffer->begin()[i].begin();
+ }
+ }
+
+ return buffer->num_frames();
+}
+
+}
diff --git a/src/resonance-audio/resonance_audio.h b/src/resonance-audio/resonance_audio.h
new file mode 100644
index 000000000..435fa16df
--- /dev/null
+++ b/src/resonance-audio/resonance_audio.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef RESONANCE_AUDIO_H
+#define RESONANCE_AUDIO_H
+
+#include <api/resonance_audio_api.h>
+
+namespace vraudio
+{
+
+class ResonanceAudioExtensions;
+class ResonanceAudioApiImpl;
+
+class EXPORT_API ResonanceAudio
+{
+public:
+ ResonanceAudio(size_t num_channels, size_t frames_per_buffer, int sample_rate_hz);
+ ~ResonanceAudio();
+
+ // reverb is only calculated in stereo. We get it here as well, and our ambisonic
+ // decoder will then add it to the generated surround signal.
+ int getAmbisonicOutput(const float *buffers[], const float *reverb[], int nChannels);
+
+ ResonanceAudioApi *api = nullptr;
+ ResonanceAudioApiImpl *impl = nullptr;
+ bool roomEffectsEnabled = true;
+};
+
+
+
+}
+
+#endif
diff --git a/src/resonance-audio/resonance_audio_api_extensions.cpp b/src/resonance-audio/resonance_audio_api_extensions.cpp
deleted file mode 100644
index 46009f64b..000000000
--- a/src/resonance-audio/resonance_audio_api_extensions.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "resonance_audio_api_extensions.h"
-#include "graph/resonance_audio_api_impl.h"
-
-namespace vraudio
-{
-
-int getAmbisonicOutput(ResonanceAudioApi *api, const float *buffers[], int nChannels)
-{
- ResonanceAudioApiImpl *impl = static_cast<ResonanceAudioApiImpl *>(api);
-
- impl->ProcessNextBuffer();
- auto *buffer = impl->GetAmbisonicOutputBuffer();
- if (nChannels != buffer->num_channels())
- return -1;
-
- for (int i = 0; i < nChannels; ++i) {
- buffers[i] = buffer->begin()[i].begin();
- }
- return buffer->num_frames();
-}
-
-}
diff --git a/src/resonance-audio/resonance_audio_api_extensions.h b/src/resonance-audio/resonance_audio_api_extensions.h
deleted file mode 100644
index 2c26c0911..000000000
--- a/src/resonance-audio/resonance_audio_api_extensions.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef RESONANCE_AUDIO_API_EXTENSIONS_H
-#define RESONANCE_AUDIO_API_EXTENSIONS_H
-
-#include <api/resonance_audio_api.h>
-
-namespace vraudio
-{
-
-EXPORT_API int getAmbisonicOutput(ResonanceAudioApi *api, const float *buffers[], int nChannels);
-
-}
-
-#endif
diff --git a/src/spatialaudio/CMakeLists.txt b/src/spatialaudio/CMakeLists.txt
new file mode 100644
index 000000000..d0d005e1e
--- /dev/null
+++ b/src/spatialaudio/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_module(SpatialAudio
+ SOURCES
+ qambisonicdecoder.cpp qambisonicdecoder_p.h qambisonicdecoderdata_p.h
+ qaudioengine.cpp qaudioengine.h qaudioengine_p.h
+ qaudiolistener.cpp qaudiolistener.h
+ qaudioroom.cpp qaudioroom.h qaudioroom_p.h
+ qspatialsound.cpp qspatialsound.h qspatialsound_p.h
+ qambientsound.cpp qambientsound.h qambientsound_p.h
+ qtspatialaudioglobal.h qtspatialaudioglobal_p.h
+ INCLUDE_DIRECTORIES
+ "../3rdparty/resonance-audio/resonance_audio"
+ "../3rdparty/resonance-audio"
+ "../resonance-audio"
+ "../3rdparty/eigen"
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::BundledResonanceAudio
+ PUBLIC_LIBRARIES
+ Qt::Multimedia
+ GENERATE_CPP_EXPORTS
+)
+
+
+qt_internal_add_docs(SpatialAudio
+ doc/qtspatialaudio.qdocconf
+)
diff --git a/src/spatialaudio/doc/qtspatialaudio.qdocconf b/src/spatialaudio/doc/qtspatialaudio.qdocconf
new file mode 100644
index 000000000..3c8916907
--- /dev/null
+++ b/src/spatialaudio/doc/qtspatialaudio.qdocconf
@@ -0,0 +1,62 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+project = QtSpatialAudio
+description = Qt Spatial Audio Documentation
+version = $QT_VERSION
+buildversion = "Technology Preview"
+
+moduleheader = QtSpatialAudio
+includepaths += .
+
+examplesinstallpath = spatialaudio
+
+# The following parameters are for creating a qhp file, the qhelpgenerator
+# program can convert the qhp file into a qch file which can be opened in
+# Qt Assistant and/or Qt Creator.
+
+# Defines the name of the project. You cannot use operators (+, =, -) in
+# the name. Properties for this project are set using a qhp.<projectname>.property
+# format.
+qhp.projects = QtSpatialAudio
+qhp.QtSpatialAudio.file = qtspatialaudio.qhp
+qhp.QtSpatialAudio.namespace = org.qt-project.qtspatialaudio.$QT_VERSION_TAG
+qhp.QtSpatialAudio.indexTitle = Qt Spatial Audio
+qhp.QtSpatialAudio.virtualFolder = qtspatialaudio
+
+# For listing child nodes in Qt Creator or Assistant.
+qhp.QtSpatialAudio.subprojects = classes qmltypes examples
+
+qhp.QtSpatialAudio.subprojects.classes.title = Qt Spatial Audio Classes
+qhp.QtSpatialAudio.subprojects.classes.indexTitle = Qt Spatial Audio C++ Classes
+qhp.QtSpatialAudio.subprojects.classes.selectors = module:QtSpatialAudio
+qhp.QtSpatialAudio.subprojects.classes.sortPages = true
+
+qhp.QtSpatialAudio.subprojects.qmltypes.title = QML Types
+qhp.QtSpatialAudio.subprojects.qmltypes.indexTitle = Qt Spatial Audio QML Types
+qhp.QtSpatialAudio.subprojects.qmltypes.selectors = qmlclass
+qhp.QtSpatialAudio.subprojects.qmltypes.sortPages = true
+
+qhp.QtSpatialAudio.subprojects.examples.title = Examples
+qhp.QtSpatialAudio.subprojects.examples.indexTitle = Qt Spatial Audio Examples
+qhp.QtSpatialAudio.subprojects.examples.selectors = doc:example
+qhp.QtSpatialAudio.subprojects.examples.sortPages = true
+
+exampledirs += ../../../examples/spatialaudio \
+ snippets
+
+headerdirs += .. \
+ ../../spatialaudioquick3d
+
+imagedirs += src/images \
+
+sourcedirs += .. \
+ ../../spatialaudioquick3d
+
+depends = qtcore qtdoc qtgui qtquick qtqml qtnetwork qmake qtcmake qtquickcontrols qtquick3d qtmultimedia
+
+# Ignore \since commands for versions earlier than 6.3
+ignoresince = 6.4
+
+navigation.landingpage = "Qt Spatial Audio"
+navigation.cppclassespage = "Qt Spatial Audio C++ Classes"
+navigation.qmltypespage = "Qt Spatial Audio QML Types"
diff --git a/src/spatialaudio/doc/src/qtspatialaudio-cpp.qdoc b/src/spatialaudio/doc/src/qtspatialaudio-cpp.qdoc
new file mode 100644
index 000000000..aff743e82
--- /dev/null
+++ b/src/spatialaudio/doc/src/qtspatialaudio-cpp.qdoc
@@ -0,0 +1,33 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \module QtSpatialAudio
+ \title Qt Spatial Audio Module C++ Classes
+ \ingroup modules
+ \qtvariable spatialaudio
+ \qtcmakepackage SpatialAudio
+
+ \brief The \l {Qt Spatial Audio} module provides functionality for 3D audio.
+
+ \include module-use.qdocinc using qt module
+
+ \code
+ find_package(Qt6 REQUIRED COMPONENTS SpatialAudio)
+ target_link_libraries(my_project PRIVATE Qt6::SpatialAudio)
+ \endcode
+*/
+
+/*!
+ \page qtspatialaudio-modules.html
+ \title Qt Spatial Audio C++ Classes
+ \brief Provides C++ class for spatial audio use cases.
+
+ The C++ classes provide the same functionality as the Qt Quick3D spatial audio
+ module.
+
+ \section1 Classes
+
+ \generatelist {classesbymodule QtSpatialAudio}
+
+*/
diff --git a/src/spatialaudio/doc/src/qtspatialaudio-examples.qdoc b/src/spatialaudio/doc/src/qtspatialaudio-examples.qdoc
new file mode 100644
index 000000000..b2ceb118c
--- /dev/null
+++ b/src/spatialaudio/doc/src/qtspatialaudio-examples.qdoc
@@ -0,0 +1,15 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \group spatialaudio_examples
+ \ingroup multimedia_examples
+ \title Qt Spatial Audio Examples
+ \brief Demonstrates the spatial audio functionality provided by Qt.
+
+ The \l{Qt Spatial Audio} module provides cross-platform capabilities to
+ add support for spatial audio to Qt based applications.
+
+ The examples listed below show some typical use cases where audio sources are
+ located in 3D space adding virtual rooms around the source.
+*/
diff --git a/src/spatialaudio/doc/src/qtspatialaudio-index.qdoc b/src/spatialaudio/doc/src/qtspatialaudio-index.qdoc
new file mode 100644
index 000000000..784273b54
--- /dev/null
+++ b/src/spatialaudio/doc/src/qtspatialaudio-index.qdoc
@@ -0,0 +1,104 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtspatialaudio-index.html
+ \title Qt Spatial Audio
+ \brief The Qt Spatial Audio module provides APIs for modeling sound source
+ and their surrounds in 3D space
+
+ Qt Spatial Audio is an add-on module that provides a rich set of QML types
+ and C++ classes to implement sound fields in 3D space. It contains an easy to use
+ API for positing a listener in space, adding localized sound sources around the
+ listener and emulating virtual rooms with reverb and reflections.
+
+ \section1 Getting started
+
+ If you are new to Qt Spatial Audio, the QML types can be
+ \l{qtqml import syntax}{imported} into an application using the following
+ statement in your \c {.qml} file.
+
+ \qml
+ import QtQuick3D.SpatialAudio
+ \endqml
+
+ To link against the C++ libraries, add the following to your project's
+ \c CMakeLists.txt file. Substitute \c my_project with the name of your
+ project.
+
+ \code
+ find_package(Qt6 REQUIRED COMPONENTS SpatialAudio)
+ target_link_libraries(my_project PRIVATE Qt6::SpatialAudio)
+ \endcode
+
+ \l{Spatial Audio Overview} provides a more detailed description about how
+ to use the different classes listed below.
+
+ \section1 QML Types
+
+ The following table outlines some important QML types.
+
+ \table
+ \header
+ \li Type
+ \li Description
+ \row
+ \li \l{AudioEngine}
+ \li The engine doing the processing of the audio scene
+ \row
+ \li \l {SpatialSound}
+ \li A sound source located in 3D space.
+ \row
+ \li \l {AmbientSound}
+ \li A location independent stereo sound track.
+ \row
+ \li \l {AudioRoom}
+ \li Defines a room that generates audio reverb and reflections.
+ \endtable
+
+ \section1 C++ Classes
+
+ The following table outlines some important C++ Classes
+
+ \table
+ \header
+ \li Class
+ \li Description
+ \row
+ \li \l{QAudioEngine}
+ \li The engine doing the processing of the audio scene
+ \row
+ \li \l {QSpatialSound}
+ \li A sound source located in 3D space.
+ \row
+ \li \l {QAmbientSound}
+ \li A location independent stereo sound track.
+ \row
+ \li \l {QAudioRoom}
+ \li Defines a room that generates audio reverb and reflections.
+ \endtable
+
+ \section1 Licenses and Attributions
+
+ The Qt Spatial Audio module is available under commercial licenses from
+ \l{The Qt Company}.
+ In addition, it is available under free software licenses. These free software
+ licenses are
+ \l{GNU Lesser General Public License, version 3}, or
+ the \l{GNU General Public License, version 3}.
+ See \l{Qt Licensing} for further details.
+
+ Note that Qt Spatial Audio is not available under the \l{GNU General Public License, version 2}.
+
+ Furthermore, Qt Spatial Audio in Qt \QtVersion contains third party
+ modules under the following permissive licenses:
+
+ \generatelist{groupsbymodule attributions-qtspatialaudio}
+
+ \section1 Reference and Examples
+ \list
+ \li \l{Qt Spatial Audio QML Types}{QML Types}
+ \li \l{Qt Spatial Audio C++ Classes}{C++ Classes}
+ \li \l{Qt Spatial Audio Examples}{Examples}
+ \endlist
+*/
diff --git a/src/spatialaudio/doc/src/qtspatialaudio-qml-types.qdoc b/src/spatialaudio/doc/src/qtspatialaudio-qml-types.qdoc
new file mode 100644
index 000000000..fbd1853d3
--- /dev/null
+++ b/src/spatialaudio/doc/src/qtspatialaudio-qml-types.qdoc
@@ -0,0 +1,26 @@
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\qmlmodule QtQuick3D.SpatialAudio
+\title Qt Spatial Audio QML Types
+\ingroup qmlmodules
+\brief Provides QML types for spatial audio.
+
+The QML types for \l{Qt Spatial Audio} support the full functionality of the
+C++ API.
+
+\section1 QML Types
+
+Qt Spatial Audio QML types are designed to be used together with \l{Qt Quick 3D}. They
+can be imported into your application using the following import statement in your
+.qml file:
+
+\qml
+import QtQuick3D.SpatialAudio
+\endqml
+
+\generatelist qmltypesbymodule QtQuick3D.SpatialAudio
+
+\noautolist
+*/
diff --git a/src/spatialaudio/doc/src/spatialaudiooverview.qdoc b/src/spatialaudio/doc/src/spatialaudiooverview.qdoc
new file mode 100644
index 000000000..62b95871e
--- /dev/null
+++ b/src/spatialaudio/doc/src/spatialaudiooverview.qdoc
@@ -0,0 +1,64 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page spatialaudiooverview.html
+\title Spatial Audio Overview
+\brief Support for spatial audio.
+\ingroup explanations-graphicsandmultimedia
+
+The Qt Spatial Audio API provides a number of classes that allow the creation of
+three dimensional sound scene. It is defined by objects located in 3D space
+that emit sound and surrounding geometry that can be modelled using
+one or several rooms. Finally a listener can be placed into this
+sound scene at a specified position and orientation.
+
+There are both C++ and QML APIs that can be used.
+
+\section1 Creating a sound scene
+
+To create the sound scene, one first instantiates a \l QAudioEngine. This engine
+processes input sound data and geometries to create a realistic
+representation of the sound scene as it would be experienced by a person placed
+at a specific location inside the scene.
+
+The \l QAudioEngine::OutputMode property can be used to optimize the output either
+for headphones using binaural (virtual 3D) rendering or for a stereo or surround speaker
+configuration.
+
+The output device can be selected using \l QAudioEngine::outputDevice property.
+
+Once the engine is set up, we can place various sound objects into the scene by creating
+\l QSpatialSound objects and specifying a url to a sound file using the \l
+QSpatialSound::source property.
+
+\l QAudioListener can be used to define the position and orientation of a person
+listening to the sound scene. At max one listener per engine can be used. If no listener
+is specified, the engine assumes that the listener is at the origin of the coordinate system
+facing into a positive z direction, with positive y pointing upwards.
+
+In addition to sound sources and a listener, you can define a geometry that influences how the
+sound is being experienced by the listener through a set of \l QAudioRoom objects. Rooms
+are rectangular and support a wide variety of materials for each wall giving a different experience
+with different sound reflections and reverb. Room effects will get applied if the listener is
+located inside one of the rooms. If he is inside multiple rooms, the room with the smallest
+geometrical volume will take precedence.
+
+If you need some stereo overlay that is independent of the position and orientation of
+the listener (such as background music or a voice-over), you can use
+\l QAmbientSound to create the sound overlay.
+
+For a small QWidget based example showcasing one audio source that can be moved around in a room, have
+a look at the \l {audiopanning}{Spacial Audio Panning Example}.
+
+\section1 Reference Documentation
+
+\section2 C++ Classes
+
+\annotatedlist spatialaudio
+
+\section2 QML Types
+
+\annotatedlist quick3d_spatialaudio
+
+*/
diff --git a/src/spatialaudio/qambientsound.cpp b/src/spatialaudio/qambientsound.cpp
new file mode 100644
index 000000000..cdf61b918
--- /dev/null
+++ b/src/spatialaudio/qambientsound.cpp
@@ -0,0 +1,283 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include "qambientsound.h"
+#include "qambientsound_p.h"
+#include "qaudioengine_p.h"
+#include "resonance_audio.h"
+#include <qaudiosink.h>
+#include <qurl.h>
+#include <qdebug.h>
+#include <qaudiodecoder.h>
+
+QT_BEGIN_NAMESPACE
+
+void QAmbientSoundPrivate::load()
+{
+ decoder.reset(new QAudioDecoder);
+ buffers.clear();
+ currentBuffer = 0;
+ sourceDeviceFile.reset(nullptr);
+ bufPos = 0;
+ m_playing = false;
+ m_loading = true;
+ auto *ep = QAudioEnginePrivate::get(engine);
+ QAudioFormat f;
+ f.setSampleFormat(QAudioFormat::Float);
+ f.setSampleRate(ep->sampleRate);
+ f.setChannelConfig(nchannels == 2 ? QAudioFormat::ChannelConfigStereo : QAudioFormat::ChannelConfigMono);
+ decoder->setAudioFormat(f);
+ if (url.scheme().compare(u"qrc", Qt::CaseInsensitive) == 0) {
+ auto qrcFile = std::make_unique<QFile>(u':' + url.path());
+ if (!qrcFile->open(QFile::ReadOnly))
+ return;
+ sourceDeviceFile = std::move(qrcFile);
+ decoder->setSourceDevice(sourceDeviceFile.get());
+ } else {
+ decoder->setSource(url);
+ }
+ connect(decoder.get(), &QAudioDecoder::bufferReady, this, &QAmbientSoundPrivate::bufferReady);
+ connect(decoder.get(), &QAudioDecoder::finished, this, &QAmbientSoundPrivate::finished);
+ decoder->start();
+}
+
+void QAmbientSoundPrivate::getBuffer(float *buf, int nframes, int channels)
+{
+ Q_ASSERT(channels == nchannels);
+ QMutexLocker l(&mutex);
+ if (!m_playing || currentBuffer >= buffers.size()) {
+ memset(buf, 0, channels * nframes * sizeof(float));
+ } else {
+ int frames = nframes;
+ float *ff = buf;
+ while (frames) {
+ if (currentBuffer < buffers.size()) {
+ const QAudioBuffer &b = buffers.at(currentBuffer);
+ // qDebug() << s << b.format().sampleRate() << b.format().channelCount() << b.format().sampleFormat();
+ auto *f = b.constData<float>() + bufPos*nchannels;
+ int toCopy = qMin(b.frameCount() - bufPos, frames);
+ memcpy(ff, f, toCopy*sizeof(float)*nchannels);
+ ff += toCopy*nchannels;
+ frames -= toCopy;
+ bufPos += toCopy;
+ Q_ASSERT(bufPos <= b.frameCount());
+ if (bufPos == b.frameCount()) {
+ ++currentBuffer;
+ bufPos = 0;
+ }
+ } else {
+ // no more data available
+ if (m_loading)
+ qDebug() << "underrun" << frames << "frames when loading" << url;
+ memset(ff, 0, frames * channels * sizeof(float));
+ ff += frames * channels;
+ frames = 0;
+ }
+ if (!m_loading) {
+ if (currentBuffer == buffers.size()) {
+ currentBuffer = 0;
+ ++m_currentLoop;
+ }
+ if (m_loops > 0 && m_currentLoop >= m_loops) {
+ m_playing = false;
+ m_currentLoop = 0;
+ }
+ }
+ }
+ Q_ASSERT(ff - buf == channels*nframes);
+ }
+}
+
+void QAmbientSoundPrivate::bufferReady()
+{
+ QMutexLocker l(&mutex);
+ auto b = decoder->read();
+ // qDebug() << "read buffer" << b.format() << b.startTime() << b.duration();
+ buffers.append(b);
+ if (m_autoPlay)
+ m_playing = true;
+}
+
+void QAmbientSoundPrivate::finished()
+{
+ m_loading = false;
+}
+
+/*!
+ \class QAmbientSound
+ \inmodule QtSpatialAudio
+ \ingroup spatialaudio
+ \ingroup multimedia_audio
+
+ \brief A stereo overlay sound.
+
+ QAmbientSound represents a position and orientation independent sound.
+ It's commonly used for background sounds (e.g. music) that is supposed to be independent
+ of the listeners position and orientation.
+ */
+
+/*!
+ Creates a stereo sound source for \a engine.
+ */
+QAmbientSound::QAmbientSound(QAudioEngine *engine)
+ : d(new QAmbientSoundPrivate(this))
+{
+ setEngine(engine);
+}
+
+QAmbientSound::~QAmbientSound()
+{
+ setEngine(nullptr);
+ delete d;
+}
+
+/*!
+ \property QAmbientSound::volume
+
+ Defines the volume of the sound.
+
+ Values between 0 and 1 will attenuate the sound, while values above 1
+ provide an additional gain boost.
+ */
+void QAmbientSound::setVolume(float volume)
+{
+ if (d->volume == volume)
+ return;
+ d->volume = volume;
+ auto *ep = QAudioEnginePrivate::get(d->engine);
+ if (ep)
+ ep->resonanceAudio->api->SetSourceVolume(d->sourceId, d->volume);
+ emit volumeChanged();
+}
+
+float QAmbientSound::volume() const
+{
+ return d->volume;
+}
+
+void QAmbientSound::setSource(const QUrl &url)
+{
+ if (d->url == url)
+ return;
+ d->url = url;
+
+ d->load();
+ emit sourceChanged();
+}
+
+/*!
+ \property QAmbientSound::source
+
+ The source file for the sound to be played.
+ */
+QUrl QAmbientSound::source() const
+{
+ return d->url;
+}
+/*!
+ \enum QAmbientSound::Loops
+
+ Lets you control the playback loop using the following values:
+
+ \value Infinite Loops infinitely
+ \value Once Stops playback after running once
+*/
+/*!
+ \property QAmbientSound::loops
+
+ Determines how many times the sound is played before the player stops.
+ Set to QAmbientSound::Infinite to play the current sound in
+ a loop forever.
+
+ The default value is \c 1.
+ */
+int QAmbientSound::loops() const
+{
+ return d->m_loops.loadRelaxed();
+}
+
+void QAmbientSound::setLoops(int loops)
+{
+ int oldLoops = d->m_loops.fetchAndStoreRelaxed(loops);
+ if (oldLoops != loops)
+ emit loopsChanged();
+}
+
+/*!
+ \property QAmbientSound::autoPlay
+
+ Determines whether the sound should automatically start playing when a source
+ gets specified.
+
+ The default value is \c true.
+ */
+bool QAmbientSound::autoPlay() const
+{
+ return d->m_autoPlay.loadRelaxed();
+}
+
+void QAmbientSound::setAutoPlay(bool autoPlay)
+{
+ bool old = d->m_autoPlay.fetchAndStoreRelaxed(autoPlay);
+ if (old != autoPlay)
+ emit autoPlayChanged();
+}
+
+/*!
+ Starts playing back the sound. Does nothing if the sound is already playing.
+ */
+void QAmbientSound::play()
+{
+ d->play();
+}
+
+/*!
+ Pauses sound playback. Calling play() will continue playback.
+ */
+void QAmbientSound::pause()
+{
+ d->pause();
+}
+
+/*!
+ Stops sound playback and resets the current position and current loop count to 0.
+ Calling play() will start playback at the beginning of the sound file.
+ */
+void QAmbientSound::stop()
+{
+ d->stop();
+}
+
+/*!
+ \internal
+ */
+void QAmbientSound::setEngine(QAudioEngine *engine)
+{
+ if (d->engine == engine)
+ return;
+
+ // Remove self from old engine (if necessary)
+ auto *ep = QAudioEnginePrivate::get(d->engine);
+ if (ep)
+ ep->removeStereoSound(this);
+
+ d->engine = engine;
+
+ // Add self to new engine if necessary
+ ep = QAudioEnginePrivate::get(d->engine);
+ if (ep) {
+ ep->addStereoSound(this);
+ ep->resonanceAudio->api->SetSourceVolume(d->sourceId, d->volume);
+ }
+}
+
+/*!
+ Returns the engine associated with this sound.
+ */
+QAudioEngine *QAmbientSound::engine() const
+{
+ return d->engine;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qambientsound.cpp"
diff --git a/src/spatialaudio/qambientsound.h b/src/spatialaudio/qambientsound.h
new file mode 100644
index 000000000..f10159251
--- /dev/null
+++ b/src/spatialaudio/qambientsound.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QAMBIENTSOUND_H
+#define QAMBIENTSOUND_H
+
+#include <QtSpatialAudio/qtspatialaudioglobal.h>
+#include <QtMultimedia/qtmultimediaglobal.h>
+#include <QtCore/QUrl>
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioEngine;
+class QAmbientSoundPrivate;
+
+class Q_SPATIALAUDIO_EXPORT QAmbientSound : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)
+ Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged)
+ Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged)
+
+public:
+ explicit QAmbientSound(QAudioEngine *engine);
+ ~QAmbientSound();
+
+ void setSource(const QUrl &url);
+ QUrl source() const;
+
+ enum Loops
+ {
+ Infinite = -1,
+ Once = 1
+ };
+ Q_ENUM(Loops)
+
+ int loops() const;
+ void setLoops(int loops);
+
+ bool autoPlay() const;
+ void setAutoPlay(bool autoPlay);
+
+ void setVolume(float volume);
+ float volume() const;
+
+ QAudioEngine *engine() const;
+
+Q_SIGNALS:
+ void sourceChanged();
+ void loopsChanged();
+ void autoPlayChanged();
+ void volumeChanged();
+
+public Q_SLOTS:
+ void play();
+ void pause();
+ void stop();
+
+private:
+ void setEngine(QAudioEngine *engine);
+ friend class QAmbientSoundPrivate;
+ QAmbientSoundPrivate *d = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudio/qambientsound_p.h b/src/spatialaudio/qambientsound_p.h
new file mode 100644
index 000000000..d26404f43
--- /dev/null
+++ b/src/spatialaudio/qambientsound_p.h
@@ -0,0 +1,84 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QAMBIENTSOUND_P_H
+#define QAMBIENTSOUND_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtspatialaudioglobal_p.h>
+#include <qmutex.h>
+#include <qurl.h>
+#include <qfile.h>
+#include <qaudiodecoder.h>
+#include <qaudiobuffer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioEngine;
+
+class QAmbientSoundPrivate : public QObject
+{
+public:
+ QAmbientSoundPrivate(QObject *parent, int nchannels = 2)
+ : QObject(parent)
+ , nchannels(nchannels)
+ {}
+
+ template<typename T>
+ static QAmbientSoundPrivate *get(T *soundSource) { return soundSource ? soundSource->d : nullptr; }
+
+ QUrl url;
+ float volume = 1.;
+ int nchannels = 2;
+ std::unique_ptr<QAudioDecoder> decoder;
+ std::unique_ptr<QFile> sourceDeviceFile;
+ QAudioEngine *engine = nullptr;
+
+ QMutex mutex;
+ int currentBuffer = 0;
+ int bufPos = 0;
+ int m_currentLoop = 0;
+ QList<QAudioBuffer> buffers;
+ int sourceId = -1; // kInvalidSourceId
+
+ QAtomicInteger<bool> m_autoPlay = true;
+ QAtomicInteger<bool> m_playing = false;
+ QAtomicInt m_loops = 1;
+ bool m_loading = false;
+
+ void play() {
+ m_playing = true;
+ }
+ void pause() {
+ m_playing = false;
+ }
+ void stop() {
+ QMutexLocker locker(&mutex);
+ m_playing = false;
+ currentBuffer = 0;
+ bufPos = 0;
+ m_currentLoop = 0;
+ }
+
+ void load();
+ void getBuffer(float *buf, int frames, int channels);
+
+private Q_SLOTS:
+ void bufferReady();
+ void finished();
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QAMBIENTSOUND_P_H
diff --git a/src/spatialaudio/qambisonicdecoder.cpp b/src/spatialaudio/qambisonicdecoder.cpp
new file mode 100644
index 000000000..4404b11b0
--- /dev/null
+++ b/src/spatialaudio/qambisonicdecoder.cpp
@@ -0,0 +1,314 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include "qambisonicdecoder_p.h"
+
+#include "qambisonicdecoderdata_p.h"
+#include <cmath>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+// Ambisonic decoding is described in detail in https://ambisonics.dreamhosters.com/BLaH3.pdf.
+// We're using a phase matched band splitting filter to split the ambisonic signal into a low
+// and high frequency component and apply matrix conversions to those components individually
+// as described in the document.
+//
+// We are currently not using a near field compensation filter, something that could potentially
+// improve sound quality further.
+//
+// For mono and stereo decoding, we use a simpler algorithm to avoid artificially dampening signals
+// coming from the back, as we do not have any speakers in that direction and the calculations
+// through matlab would give us audible 'holes'.
+
+struct QAmbisonicDecoderData
+{
+ QAudioFormat::ChannelConfig config;
+ const float *lf[3];
+ const float *hf[3];
+ const float *reverb;
+};
+
+const float reverb_x_0[] =
+{
+ 1.f, 0.f, // L
+ 0.f, 1.f, // R
+ .7f, .7f, // C
+ 1.f, 0.f, // Ls
+ 0.f, 1.f, // Rs
+ 1.f, 0.f, // Lb
+ 0.f, 1.f, // Rb
+};
+
+const float reverb_x_1[] =
+{
+ 1.f, 0.f, // L
+ 0.f, 1.f, // R
+ .7f, .7f, // C
+ .0f, .0f, // LFE
+ 1.f, 0.f, // Ls
+ 0.f, 1.f, // Rs
+ 1.f, 0.f, // Lb
+ 0.f, 1.f, // Rb
+};
+
+static const QAmbisonicDecoderData decoderMap[] =
+{
+ { QAudioFormat::ChannelConfigSurround5Dot0,
+ { decoderMatrix_5dot0_1_lf, decoderMatrix_5dot0_2_lf, decoderMatrix_5dot0_3_lf },
+ { decoderMatrix_5dot0_1_hf, decoderMatrix_5dot0_2_hf, decoderMatrix_5dot0_3_hf },
+ reverb_x_0
+ },
+ { QAudioFormat::ChannelConfigSurround5Dot1,
+ { decoderMatrix_5dot1_1_lf, decoderMatrix_5dot1_2_lf, decoderMatrix_5dot1_3_lf },
+ { decoderMatrix_5dot1_1_hf, decoderMatrix_5dot1_2_hf, decoderMatrix_5dot1_3_hf },
+ reverb_x_1
+ },
+ { QAudioFormat::ChannelConfigSurround7Dot0,
+ { decoderMatrix_7dot0_1_lf, decoderMatrix_7dot0_2_lf, decoderMatrix_7dot0_3_lf },
+ { decoderMatrix_7dot0_1_hf, decoderMatrix_7dot0_2_hf, decoderMatrix_7dot0_3_hf },
+ reverb_x_0
+ },
+ { QAudioFormat::ChannelConfigSurround7Dot1,
+ { decoderMatrix_7dot1_1_lf, decoderMatrix_7dot1_2_lf, decoderMatrix_7dot1_3_lf },
+ { decoderMatrix_7dot1_1_hf, decoderMatrix_7dot1_2_hf, decoderMatrix_7dot1_3_hf },
+ reverb_x_1
+ }
+};
+
+// Implements a split second order IIR filter
+// The audio data is split into a phase synced low and high frequency part
+// This allows us to apply different factors to both parts for better sound
+// localization when converting from ambisonic formats
+//
+// Details are described in https://ambisonics.dreamhosters.com/BLaH3.pdf, Appendix A.2.
+class QAmbisonicDecoderFilter
+{
+public:
+ QAmbisonicDecoderFilter() = default;
+ void configure(float sampleRate, float cutoffFrequency = 380)
+ {
+ double k = tan(M_PI*cutoffFrequency/sampleRate);
+ a1 = float(2.*(k*k - 1.)/(k*k + 2*k + 1.));
+ a2 = float((k*k - 2*k + 1.)/(k*k + 2*k + 1.));
+
+ b0_lf = float(k*k/(k*k + 2*k + 1));
+ b1_lf = 2.f*b0_lf;
+
+ b0_hf = float(1./(k*k + 2*k + 1));
+ b1_hf = -2.f*b0_hf;
+ }
+
+ struct Output
+ {
+ float lf;
+ float hf;
+ };
+
+ Output next(float x)
+ {
+ float r_lf = x*b0_lf +
+ prevX[0]*b1_lf +
+ prevX[1]*b0_lf -
+ prevR_lf[0]*a1 -
+ prevR_lf[1]*a2;
+ float r_hf = x*b0_hf +
+ prevX[0]*b1_hf +
+ prevX[1]*b0_hf -
+ prevR_hf[0]*a1 -
+ prevR_hf[1]*a2;
+ prevX[1] = prevX[0];
+ prevX[0] = x;
+ prevR_lf[1] = prevR_lf[0];
+ prevR_lf[0] = r_lf;
+ prevR_hf[1] = prevR_hf[0];
+ prevR_hf[0] = r_hf;
+ return { r_lf, r_hf };
+ }
+
+private:
+ float a1 = 0.;
+ float a2 = 0.;
+
+ float b0_hf = 0.;
+ float b1_hf = 0.;
+
+ float b0_lf = 0.;
+ float b1_lf = 0.;
+
+ float prevX[2] = {};
+ float prevR_lf[2] = {};
+ float prevR_hf[2] = {};
+};
+
+
+QAmbisonicDecoder::QAmbisonicDecoder(AmbisonicLevel ambisonicLevel, const QAudioFormat &format)
+ : level(ambisonicLevel)
+{
+ Q_ASSERT(level > 0 && level <= 3);
+ inputChannels = (level+1)*(level+1);
+ outputChannels = format.channelCount();
+
+ channelConfig = format.channelConfig();
+ if (channelConfig == QAudioFormat::ChannelConfigUnknown)
+ channelConfig = format.defaultChannelConfigForChannelCount(format.channelCount());
+
+ if (channelConfig == QAudioFormat::ChannelConfigMono ||
+ channelConfig == QAudioFormat::ChannelConfigStereo ||
+ channelConfig == QAudioFormat::ChannelConfig2Dot1 ||
+ channelConfig == QAudioFormat::ChannelConfig3Dot0 ||
+ channelConfig == QAudioFormat::ChannelConfig3Dot1) {
+ // these are non surround configs and handled manually to avoid
+ // audible holes for sounds coming from behing
+ //
+ // We use a simpler decoding process here, only taking first order
+ // ambisonics into account
+ //
+ // Left and right channels get 50% W and 50% X
+ // Center gets 50% W and 50% Y
+ // LFE gets 50% W
+ simpleDecoderFactors = new float[4*outputChannels];
+ float *r = new float[2*outputChannels]; // reverb output is in stereo
+ float *f = simpleDecoderFactors;
+ reverbFactors = r;
+ if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::FrontLeft)) {
+ f[0] = 0.5f; f[1] = 0.5f; f[2] = 0.; f[3] = 0.f;
+ f += 4;
+ r[0] = 1.; r[1] = 0.;
+ r += 2;
+ }
+ if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::FrontRight)) {
+ f[0] = 0.5f; f[1] = -0.5f; f[2] = 0.; f[3] = 0.f;
+ f += 4;
+ r[0] = 0.; r[1] = 1.;
+ r += 2;
+ }
+ if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::FrontCenter)) {
+ f[0] = 0.5f; f[1] = -0.f; f[2] = 0.; f[3] = 0.5f;
+ f += 4;
+ r[0] = .5; r[1] = .5;
+ r += 2;
+ }
+ if (channelConfig & QAudioFormat::channelConfig(QAudioFormat::LFE)) {
+ f[0] = 0.5f; f[1] = -0.f; f[2] = 0.; f[3] = 0.0f;
+ f += 4;
+ r[0] = 0.; r[1] = 0.;
+ r += 2;
+ }
+ Q_UNUSED(f);
+ Q_UNUSED(r);
+ Q_ASSERT((f - simpleDecoderFactors) == 4*outputChannels);
+ Q_ASSERT((r - reverbFactors) == 2*outputChannels);
+
+ return;
+ }
+
+ for (const auto &d : decoderMap) {
+ if (d.config == channelConfig) {
+ decoderData = &d;
+ reverbFactors = decoderData->reverb;
+ break;
+ }
+ }
+ if (!decoderData) {
+ // can't handle this,
+ outputChannels = 0;
+ return;
+ }
+
+ filters = new QAmbisonicDecoderFilter[inputChannels];
+ for (int i = 0; i < inputChannels; ++i)
+ filters[i].configure(format.sampleRate());
+}
+
+QAmbisonicDecoder::~QAmbisonicDecoder()
+{
+ if (simpleDecoderFactors) {
+ delete simpleDecoderFactors;
+ delete reverbFactors;
+ }
+}
+
+void QAmbisonicDecoder::processBuffer(const float *input[], float *output, int nSamples)
+{
+ float *o = output;
+ memset(o, 0, nSamples*outputChannels*sizeof(float));
+
+ if (simpleDecoderFactors) {
+ for (int i = 0; i < nSamples; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ for (int k = 0; k < outputChannels; ++k)
+ o[k] += simpleDecoderFactors[k*4 + j]*input[j][i];
+ }
+ o += outputChannels;
+ }
+ return;
+ }
+
+ const float *matrix_hi = decoderData->hf[level - 1];
+ const float *matrix_lo = decoderData->lf[level - 1];
+ for (int i = 0; i < nSamples; ++i) {
+ QAmbisonicDecoderFilter::Output buf[maxAmbisonicChannels];
+ for (int j = 0; j < inputChannels; ++j)
+ buf[j] = filters[j].next(input[j][i]);
+ for (int j = 0; j < inputChannels; ++j) {
+ for (int k = 0; k < outputChannels; ++k)
+ o[k] += matrix_lo[k*inputChannels + j]*buf[j].lf + matrix_hi[k*inputChannels + j]*buf[j].hf;
+ }
+ o += outputChannels;
+ }
+}
+
+void QAmbisonicDecoder::processBuffer(const float *input[], short *output, int nSamples)
+{
+ const float *reverb[] = { nullptr, nullptr };
+ return processBufferWithReverb(input, reverb, output, nSamples);
+}
+
+void QAmbisonicDecoder::processBufferWithReverb(const float *input[], const float *reverb[2], short *output, int nSamples)
+{
+ if (simpleDecoderFactors) {
+ for (int i = 0; i < nSamples; ++i) {
+ float o[4] = {};
+ for (int k = 0; k < outputChannels; ++k) {
+ for (int j = 0; j < 4; ++j)
+ o[k] += simpleDecoderFactors[k*4 + j]*input[j][i];
+ }
+ if (reverb[0]) {
+ for (int k = 0; k < outputChannels; ++k) {
+ o[k] += reverb[0][i]*reverbFactors[2*k] + reverb[1][i]*reverbFactors[2*k+1];
+ }
+ }
+
+ for (int k = 0; k < outputChannels; ++k)
+ output[k] = static_cast<short>(o[k]*32768.);
+ output += outputChannels;
+ }
+ return;
+ }
+
+ // qDebug() << "XXX" << inputChannels << outputChannels;
+ const float *matrix_hi = decoderData->hf[level - 1];
+ const float *matrix_lo = decoderData->lf[level - 1];
+ for (int i = 0; i < nSamples; ++i) {
+ QAmbisonicDecoderFilter::Output buf[maxAmbisonicChannels];
+ for (int j = 0; j < inputChannels; ++j)
+ buf[j] = filters[j].next(input[j][i]);
+ float o[32] = {}; // we can't support more than 32 channels from our API
+ for (int j = 0; j < inputChannels; ++j) {
+ for (int k = 0; k < outputChannels; ++k)
+ o[k] += matrix_lo[k*inputChannels + j]*buf[j].lf + matrix_hi[k*inputChannels + j]*buf[j].hf;
+ }
+ if (reverb[0]) {
+ for (int k = 0; k < outputChannels; ++k) {
+ o[k] += reverb[0][i]*reverbFactors[2*k] + reverb[1][i]*reverbFactors[2*k+1];
+ }
+ }
+ for (int k = 0; k < outputChannels; ++k)
+ output[k] = static_cast<short>(o[k]*32768.);
+ output += outputChannels;
+ }
+
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/spatialaudio/qambisonicdecoder_p.h b/src/spatialaudio/qambisonicdecoder_p.h
new file mode 100644
index 000000000..9ac60c2a7
--- /dev/null
+++ b/src/spatialaudio/qambisonicdecoder_p.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QAMBISONICDECODER_P_H
+#define QAMBISONICDECODER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtspatialaudioglobal_p.h>
+#include <qaudioformat.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QAmbisonicDecoderData;
+class QAmbisonicDecoderFilter;
+
+class QAmbisonicDecoder
+{
+public:
+ enum AmbisonicLevel
+ {
+ AmbisonicLevel1 = 1,
+ LowQuality = AmbisonicLevel1,
+ AmbisonicLevel2 = 2,
+ MediumQuality = AmbisonicLevel2,
+ AmbisonicLevel3 = 3,
+ HighQuality = AmbisonicLevel3
+ };
+ QAmbisonicDecoder(AmbisonicLevel ambisonicLevel, const QAudioFormat &format);
+ ~QAmbisonicDecoder();
+
+ bool hasValidConfig() const { return outputChannels > 0; }
+
+ int nInputChannels() const { return inputChannels; }
+ int nOutputChannels() const { return outputChannels; }
+
+ int outputSize(int nSamples) const { return outputChannels * nSamples; }
+
+ // input is planar, output interleaved
+ void processBuffer(const float *input[], float *output, int nSamples);
+ void processBuffer(const float *input[], short *output, int nSamples);
+
+ void processBufferWithReverb(const float *input[], const float *reverb[2], short *output, int nSamples);
+
+ static constexpr int maxAmbisonicChannels = 16;
+ static constexpr int maxAmbisonicLevel = 3;
+private:
+ QAudioFormat::ChannelConfig channelConfig;
+ AmbisonicLevel level = AmbisonicLevel1;
+ int inputChannels = 0;
+ int outputChannels = 0;
+ const QAmbisonicDecoderData *decoderData = nullptr;
+ QAmbisonicDecoderFilter *filters = nullptr;
+ float *simpleDecoderFactors = nullptr;
+ const float *reverbFactors = nullptr;
+};
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudio/qambisonicdecoderdata_p.h b/src/spatialaudio/qambisonicdecoderdata_p.h
new file mode 100644
index 000000000..dfee9213a
--- /dev/null
+++ b/src/spatialaudio/qambisonicdecoderdata_p.h
@@ -0,0 +1,279 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QAMBISONICDECODERDATA_P_H
+#define QAMBISONICDECODERDATA_P_H
+
+#include <qtspatialaudioglobal_p.h>
+
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// This file is generated by the matlab/octave file adt_generate_qt.m
+// using the Ambisonic Decoder Toolbox (https://bitbucket.org/ambidecodertoolbox/adt/src/master/)
+
+
+QT_BEGIN_NAMESPACE
+
+// Decoder matrix for 5dot0, ambisonic level 1
+static constexpr float decoderMatrix_5dot0_1_lf[5*4] = {
+0.255580f, 0.430877f, 0.000000f, 0.386458f, // L
+0.255573f, -0.430877f, 0.000000f, 0.386450f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, // C
+0.552170f, 0.623932f, 0.000000f, -0.628578f, // Ls
+0.552175f, -0.623939f, 0.000000f, -0.628571f, // Rs
+};
+
+// Decoder matrix for 5dot0, ambisonic level 1
+static constexpr float decoderMatrix_5dot0_1_hf[5*4] = {
+0.361445f, 0.351810f, 0.000000f, 0.315542f, // L
+0.361435f, -0.351809f, 0.000000f, 0.315535f, // R
+0.191780f, 0.000000f, 0.000000f, 0.268870f, // C
+0.780886f, 0.509439f, 0.000000f, -0.513232f, // Ls
+0.780893f, -0.509444f, 0.000000f, -0.513226f, // Rs
+};
+
+// Decoder matrix for 5dot0, ambisonic level 2
+static constexpr float decoderMatrix_5dot0_2_lf[5*9] = {
+0.255580f, 0.430877f, 0.000000f, 0.386458f, 0.157509f, 0.000000f, -0.095304f, 0.000000f, -0.013008f, // L
+0.255573f, -0.430877f, 0.000000f, 0.386450f, -0.157507f, 0.000000f, -0.095343f, 0.000000f, -0.013009f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, 0.000000f, 0.000000f, -0.056105f, 0.000000f, 0.111010f, // C
+0.552170f, 0.623932f, 0.000000f, -0.628578f, -0.041978f, 0.000000f, -0.139072f, 0.000000f, -0.030798f, // Ls
+0.552175f, -0.623939f, 0.000000f, -0.628571f, 0.041979f, 0.000000f, -0.139039f, 0.000000f, -0.030796f, // Rs
+};
+
+// Decoder matrix for 5dot0, ambisonic level 2
+static constexpr float decoderMatrix_5dot0_2_hf[5*9] = {
+0.404108f, 0.527714f, 0.000000f, 0.473313f, 0.099617f, 0.000000f, -0.060276f, 0.000000f, -0.008227f, // L
+0.404097f, -0.527714f, 0.000000f, 0.473303f, -0.099616f, 0.000000f, -0.060300f, 0.000000f, -0.008228f, // R
+0.214417f, 0.000000f, 0.000000f, 0.403305f, 0.000000f, 0.000000f, -0.035484f, 0.000000f, 0.070209f, // C
+0.873057f, 0.764158f, 0.000000f, -0.769848f, -0.026549f, 0.000000f, -0.087957f, 0.000000f, -0.019478f, // Ls
+0.873065f, -0.764166f, 0.000000f, -0.769839f, 0.026550f, 0.000000f, -0.087936f, 0.000000f, -0.019477f, // Rs
+};
+
+// Decoder matrix for 5dot0, ambisonic level 3
+static constexpr float decoderMatrix_5dot0_3_lf[5*16] = {
+0.255580f, 0.430877f, 0.000000f, 0.386458f, 0.157509f, 0.000000f, -0.095304f, 0.000000f, -0.013008f, 0.013422f, 0.000000f, 0.030238f, 0.000000f, 0.025660f, 0.000000f, -0.014215f, // L
+0.255573f, -0.430877f, 0.000000f, 0.386450f, -0.157507f, 0.000000f, -0.095343f, 0.000000f, -0.013009f, -0.013422f, 0.000000f, -0.030227f, 0.000000f, 0.025649f, 0.000000f, -0.014214f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, 0.000000f, 0.000000f, -0.056105f, 0.000000f, 0.111010f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.018999f, 0.000000f, 0.020478f, // C
+0.552170f, 0.623932f, 0.000000f, -0.628578f, -0.041978f, 0.000000f, -0.139072f, 0.000000f, -0.030798f, -0.012642f, 0.000000f, 0.049794f, 0.000000f, -0.073727f, 0.000000f, -0.001768f, // Ls
+0.552175f, -0.623939f, 0.000000f, -0.628571f, 0.041979f, 0.000000f, -0.139039f, 0.000000f, -0.030796f, 0.012642f, 0.000000f, -0.049793f, 0.000000f, -0.073721f, 0.000000f, -0.001769f, // Rs
+};
+
+// Decoder matrix for 5dot0, ambisonic level 3
+static constexpr float decoderMatrix_5dot0_3_hf[5*16] = {
+0.426355f, 0.618969f, 0.000000f, 0.555160f, 0.160893f, 0.000000f, -0.097352f, 0.000000f, -0.013287f, 0.006823f, 0.000000f, 0.015372f, 0.000000f, 0.013045f, 0.000000f, -0.007226f, // L
+0.426343f, -0.618969f, 0.000000f, 0.555149f, -0.160891f, 0.000000f, -0.097392f, 0.000000f, -0.013289f, -0.006823f, 0.000000f, -0.015367f, 0.000000f, 0.013039f, 0.000000f, -0.007226f, // R
+0.226221f, 0.000000f, 0.000000f, 0.473046f, 0.000000f, 0.000000f, -0.057310f, 0.000000f, 0.113395f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.009658f, 0.000000f, 0.010410f, // C
+0.921121f, 0.896300f, 0.000000f, -0.902974f, -0.042880f, 0.000000f, -0.142060f, 0.000000f, -0.031459f, -0.006427f, 0.000000f, 0.025314f, 0.000000f, -0.037481f, 0.000000f, -0.000899f, // Ls
+0.921130f, -0.896310f, 0.000000f, -0.902963f, 0.042881f, 0.000000f, -0.142027f, 0.000000f, -0.031457f, 0.006427f, 0.000000f, -0.025313f, 0.000000f, -0.037478f, 0.000000f, -0.000899f, // Rs
+};
+
+// Decoder matrix for 5dot1, ambisonic level 1
+static constexpr float decoderMatrix_5dot1_1_lf[6*4] = {
+0.255580f, 0.430877f, 0.000000f, 0.386458f, // L
+0.255573f, -0.430877f, 0.000000f, 0.386450f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, // C
+0.5f, 0.0f, 0.0f, 0.0f, // LFE
+0.552170f, 0.623932f, 0.000000f, -0.628578f, // Ls
+0.552175f, -0.623939f, 0.000000f, -0.628571f, // Rs
+};
+
+// Decoder matrix for 5dot1, ambisonic level 1
+static constexpr float decoderMatrix_5dot1_1_hf[6*4] = {
+0.361445f, 0.351810f, 0.000000f, 0.315542f, // L
+0.361435f, -0.351809f, 0.000000f, 0.315535f, // R
+0.191780f, 0.000000f, 0.000000f, 0.268870f, // C
+0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.780886f, 0.509439f, 0.000000f, -0.513232f, // Ls
+0.780893f, -0.509444f, 0.000000f, -0.513226f, // Rs
+};
+
+// Decoder matrix for 5dot1, ambisonic level 2
+static constexpr float decoderMatrix_5dot1_2_lf[6*9] = {
+0.255580f, 0.430877f, 0.000000f, 0.386458f, 0.157509f, 0.000000f, -0.095304f, 0.000000f, -0.013008f, // L
+0.255573f, -0.430877f, 0.000000f, 0.386450f, -0.157507f, 0.000000f, -0.095343f, 0.000000f, -0.013009f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, 0.000000f, 0.000000f, -0.056105f, 0.000000f, 0.111010f, // C
+0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.552170f, 0.623932f, 0.000000f, -0.628578f, -0.041978f, 0.000000f, -0.139072f, 0.000000f, -0.030798f, // Ls
+0.552175f, -0.623939f, 0.000000f, -0.628571f, 0.041979f, 0.000000f, -0.139039f, 0.000000f, -0.030796f, // Rs
+};
+
+// Decoder matrix for 5dot1, ambisonic level 2
+static constexpr float decoderMatrix_5dot1_2_hf[6*9] = {
+0.404108f, 0.527714f, 0.000000f, 0.473313f, 0.099617f, 0.000000f, -0.060276f, 0.000000f, -0.008227f, // L
+0.404097f, -0.527714f, 0.000000f, 0.473303f, -0.099616f, 0.000000f, -0.060300f, 0.000000f, -0.008228f, // R
+0.214417f, 0.000000f, 0.000000f, 0.403305f, 0.000000f, 0.000000f, -0.035484f, 0.000000f, 0.070209f, // C
+0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.873057f, 0.764158f, 0.000000f, -0.769848f, -0.026549f, 0.000000f, -0.087957f, 0.000000f, -0.019478f, // Ls
+0.873065f, -0.764166f, 0.000000f, -0.769839f, 0.026550f, 0.000000f, -0.087936f, 0.000000f, -0.019477f, // Rs
+};
+
+// Decoder matrix for 5dot1, ambisonic level 3
+static constexpr float decoderMatrix_5dot1_3_lf[6*16] = {
+0.255580f, 0.430877f, 0.000000f, 0.386458f, 0.157509f, 0.000000f, -0.095304f, 0.000000f, -0.013008f, 0.013422f, 0.000000f, 0.030238f, 0.000000f, 0.025660f, 0.000000f, -0.014215f, // L
+0.255573f, -0.430877f, 0.000000f, 0.386450f, -0.157507f, 0.000000f, -0.095343f, 0.000000f, -0.013009f, -0.013422f, 0.000000f, -0.030227f, 0.000000f, 0.025649f, 0.000000f, -0.014214f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, 0.000000f, 0.000000f, -0.056105f, 0.000000f, 0.111010f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.018999f, 0.000000f, 0.020478f, // C
+0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.552170f, 0.623932f, 0.000000f, -0.628578f, -0.041978f, 0.000000f, -0.139072f, 0.000000f, -0.030798f, -0.012642f, 0.000000f, 0.049794f, 0.000000f, -0.073727f, 0.000000f, -0.001768f, // Ls
+0.552175f, -0.623939f, 0.000000f, -0.628571f, 0.041979f, 0.000000f, -0.139039f, 0.000000f, -0.030796f, 0.012642f, 0.000000f, -0.049793f, 0.000000f, -0.073721f, 0.000000f, -0.001769f, // Rs
+};
+
+// Decoder matrix for 5dot1, ambisonic level 3
+static constexpr float decoderMatrix_5dot1_3_hf[6*16] = {
+0.426355f, 0.618969f, 0.000000f, 0.555160f, 0.160893f, 0.000000f, -0.097352f, 0.000000f, -0.013287f, 0.006823f, 0.000000f, 0.015372f, 0.000000f, 0.013045f, 0.000000f, -0.007226f, // L
+0.426343f, -0.618969f, 0.000000f, 0.555149f, -0.160891f, 0.000000f, -0.097392f, 0.000000f, -0.013289f, -0.006823f, 0.000000f, -0.015367f, 0.000000f, 0.013039f, 0.000000f, -0.007226f, // R
+0.226221f, 0.000000f, 0.000000f, 0.473046f, 0.000000f, 0.000000f, -0.057310f, 0.000000f, 0.113395f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.009658f, 0.000000f, 0.010410f, // C
+0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.921121f, 0.896300f, 0.000000f, -0.902974f, -0.042880f, 0.000000f, -0.142060f, 0.000000f, -0.031459f, -0.006427f, 0.000000f, 0.025314f, 0.000000f, -0.037481f, 0.000000f, -0.000899f, // Ls
+0.921130f, -0.896310f, 0.000000f, -0.902963f, 0.042881f, 0.000000f, -0.142027f, 0.000000f, -0.031457f, 0.006427f, 0.000000f, -0.025313f, 0.000000f, -0.037478f, 0.000000f, -0.000899f, // Rs
+};
+
+// Decoder matrix for 7dot0, ambisonic level 1
+static constexpr float decoderMatrix_7dot0_1_lf[7*4] = {
+0.205900f, 0.314866f, 0.000000f, 0.366133f, // L
+0.205883f, -0.314871f, 0.000000f, 0.366126f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, // C
+0.276200f, 0.619758f, 0.000000f, 0.000000f, // Ls
+0.276228f, -0.619768f, 0.000000f, 0.000000f, // Rs
+0.276222f, 0.309884f, 0.000000f, -0.536733f, // Lb
+0.276196f, -0.309893f, 0.000000f, -0.536723f, // Rb
+};
+
+// Decoder matrix for 7dot0, ambisonic level 1
+static constexpr float decoderMatrix_7dot0_1_hf[7*4] = {
+0.291186f, 0.257087f, 0.000000f, 0.298946f, // L
+0.291162f, -0.257091f, 0.000000f, 0.298940f, // R
+0.191780f, 0.000000f, 0.000000f, 0.268870f, // C
+0.390605f, 0.506030f, 0.000000f, 0.000000f, // Ls
+0.390645f, -0.506039f, 0.000000f, 0.000000f, // Rs
+0.390637f, 0.253019f, 0.000000f, -0.438240f, // Lb
+0.390600f, -0.253026f, 0.000000f, -0.438232f, // Rb
+};
+
+// Decoder matrix for 7dot0, ambisonic level 2
+static constexpr float decoderMatrix_7dot0_2_lf[7*9] = {
+0.205900f, 0.314866f, 0.000000f, 0.366133f, 0.144868f, 0.000000f, -0.081538f, 0.000000f, 0.023353f, // L
+0.205883f, -0.314871f, 0.000000f, 0.366126f, -0.144867f, 0.000000f, -0.081652f, 0.000000f, 0.023347f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, 0.000000f, 0.000000f, -0.056105f, 0.000000f, 0.111010f, // C
+0.276200f, 0.619758f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.106989f, 0.000000f, -0.163267f, // Ls
+0.276228f, -0.619768f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.106856f, 0.000000f, -0.163267f, // Rs
+0.276222f, 0.309884f, 0.000000f, -0.536733f, -0.141395f, 0.000000f, -0.106873f, 0.000000f, 0.081634f, // Lb
+0.276196f, -0.309893f, 0.000000f, -0.536723f, 0.141394f, 0.000000f, -0.107038f, 0.000000f, 0.081628f, // Rb
+};
+
+// Decoder matrix for 7dot0, ambisonic level 2
+static constexpr float decoderMatrix_7dot0_2_hf[7*9] = {
+0.325556f, 0.385630f, 0.000000f, 0.448419f, 0.091623f, 0.000000f, -0.051569f, 0.000000f, 0.014770f, // L
+0.325529f, -0.385637f, 0.000000f, 0.448410f, -0.091622f, 0.000000f, -0.051641f, 0.000000f, 0.014766f, // R
+0.214417f, 0.000000f, 0.000000f, 0.403305f, 0.000000f, 0.000000f, -0.035484f, 0.000000f, 0.070209f, // C
+0.436710f, 0.759045f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.067666f, 0.000000f, -0.103259f, // Ls
+0.436755f, -0.759058f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.067582f, 0.000000f, -0.103259f, // Rs
+0.436746f, 0.379529f, 0.000000f, -0.657361f, -0.089426f, 0.000000f, -0.067593f, 0.000000f, 0.051630f, // Lb
+0.436704f, -0.379539f, 0.000000f, -0.657348f, 0.089426f, 0.000000f, -0.067697f, 0.000000f, 0.051626f, // Rb
+};
+
+// Decoder matrix for 7dot0, ambisonic level 3
+static constexpr float decoderMatrix_7dot0_3_lf[7*16] = {
+0.205900f, 0.314866f, 0.000000f, 0.366133f, 0.144868f, 0.000000f, -0.081538f, 0.000000f, 0.023353f, 0.019489f, 0.000000f, 0.019857f, 0.000000f, 0.022683f, 0.000000f, -0.011066f, // L
+0.205883f, -0.314871f, 0.000000f, 0.366126f, -0.144867f, 0.000000f, -0.081652f, 0.000000f, 0.023347f, -0.019488f, 0.000000f, -0.019830f, 0.000000f, 0.022669f, 0.000000f, -0.011066f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, 0.000000f, 0.000000f, -0.056105f, 0.000000f, 0.111010f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.018999f, 0.000000f, 0.020478f, // C
+0.276200f, 0.619758f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.106989f, 0.000000f, -0.163267f, -0.018501f, 0.000000f, 0.040168f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, // Ls
+0.276228f, -0.619768f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.106856f, 0.000000f, -0.163267f, 0.018501f, 0.000000f, -0.040194f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, // Rs
+0.276222f, 0.309884f, 0.000000f, -0.536733f, -0.141395f, 0.000000f, -0.106873f, 0.000000f, 0.081634f, 0.018501f, 0.000000f, 0.020094f, 0.000000f, -0.034809f, 0.000000f, 0.000000f, // Lb
+0.276196f, -0.309893f, 0.000000f, -0.536723f, 0.141394f, 0.000000f, -0.107038f, 0.000000f, 0.081628f, -0.018500f, 0.000000f, -0.020079f, 0.000000f, -0.034779f, 0.000000f, 0.000000f, // Rb
+};
+
+// Decoder matrix for 7dot0, ambisonic level 3
+static constexpr float decoderMatrix_7dot0_3_hf[7*16] = {
+0.343479f, 0.452315f, 0.000000f, 0.525962f, 0.147981f, 0.000000f, -0.083290f, 0.000000f, 0.023855f, 0.009908f, 0.000000f, 0.010095f, 0.000000f, 0.011532f, 0.000000f, -0.005626f, // L
+0.343450f, -0.452323f, 0.000000f, 0.525952f, -0.147980f, 0.000000f, -0.083406f, 0.000000f, 0.023849f, -0.009907f, 0.000000f, -0.010081f, 0.000000f, 0.011524f, 0.000000f, -0.005626f, // R
+0.226221f, 0.000000f, 0.000000f, 0.473046f, 0.000000f, 0.000000f, -0.057310f, 0.000000f, 0.113395f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.009658f, 0.000000f, 0.010410f, // C
+0.460752f, 0.890303f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.109288f, 0.000000f, -0.166775f, -0.009405f, 0.000000f, 0.020420f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, // Ls
+0.460799f, -0.890318f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.109152f, 0.000000f, -0.166775f, 0.009405f, 0.000000f, -0.020434f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, // Rs
+0.460790f, 0.445159f, 0.000000f, -0.771035f, -0.144433f, 0.000000f, -0.109170f, 0.000000f, 0.083387f, 0.009406f, 0.000000f, 0.010215f, 0.000000f, -0.017696f, 0.000000f, 0.000000f, // Lb
+0.460745f, -0.445171f, 0.000000f, -0.771020f, 0.144432f, 0.000000f, -0.109338f, 0.000000f, 0.083382f, -0.009405f, 0.000000f, -0.010207f, 0.000000f, -0.017681f, 0.000000f, 0.000000f, // Rb
+};
+
+// Decoder matrix for 7dot1, ambisonic level 1
+static constexpr float decoderMatrix_7dot1_1_lf[8*4] = {
+0.205900f, 0.314866f, 0.000000f, 0.366133f, // L
+0.205883f, -0.314871f, 0.000000f, 0.366126f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, // C
+0.5f, 0.0f, 0.0f, 0.0f, // LFE
+0.276200f, 0.619758f, 0.000000f, 0.000000f, // Ls
+0.276228f, -0.619768f, 0.000000f, 0.000000f, // Rs
+0.276222f, 0.309884f, 0.000000f, -0.536733f, // Lb
+0.276196f, -0.309893f, 0.000000f, -0.536723f, // Rb
+};
+
+// Decoder matrix for 7dot1, ambisonic level 1
+static constexpr float decoderMatrix_7dot1_1_hf[8*4] = {
+0.291186f, 0.257087f, 0.000000f, 0.298946f, // L
+0.291162f, -0.257091f, 0.000000f, 0.298940f, // R
+0.191780f, 0.000000f, 0.000000f, 0.268870f, // C
+0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.390605f, 0.506030f, 0.000000f, 0.000000f, // Ls
+0.390645f, -0.506039f, 0.000000f, 0.000000f, // Rs
+0.390637f, 0.253019f, 0.000000f, -0.438240f, // Lb
+0.390600f, -0.253026f, 0.000000f, -0.438232f, // Rb
+};
+
+// Decoder matrix for 7dot1, ambisonic level 2
+static constexpr float decoderMatrix_7dot1_2_lf[8*9] = {
+0.205900f, 0.314866f, 0.000000f, 0.366133f, 0.144868f, 0.000000f, -0.081538f, 0.000000f, 0.023353f, // L
+0.205883f, -0.314871f, 0.000000f, 0.366126f, -0.144867f, 0.000000f, -0.081652f, 0.000000f, 0.023347f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, 0.000000f, 0.000000f, -0.056105f, 0.000000f, 0.111010f, // C
+0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.276200f, 0.619758f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.106989f, 0.000000f, -0.163267f, // Ls
+0.276228f, -0.619768f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.106856f, 0.000000f, -0.163267f, // Rs
+0.276222f, 0.309884f, 0.000000f, -0.536733f, -0.141395f, 0.000000f, -0.106873f, 0.000000f, 0.081634f, // Lb
+0.276196f, -0.309893f, 0.000000f, -0.536723f, 0.141394f, 0.000000f, -0.107038f, 0.000000f, 0.081628f, // Rb
+};
+
+// Decoder matrix for 7dot1, ambisonic level 2
+static constexpr float decoderMatrix_7dot1_2_hf[8*9] = {
+0.325556f, 0.385630f, 0.000000f, 0.448419f, 0.091623f, 0.000000f, -0.051569f, 0.000000f, 0.014770f, // L
+0.325529f, -0.385637f, 0.000000f, 0.448410f, -0.091622f, 0.000000f, -0.051641f, 0.000000f, 0.014766f, // R
+0.214417f, 0.000000f, 0.000000f, 0.403305f, 0.000000f, 0.000000f, -0.035484f, 0.000000f, 0.070209f, // C
+0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.436710f, 0.759045f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.067666f, 0.000000f, -0.103259f, // Ls
+0.436755f, -0.759058f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.067582f, 0.000000f, -0.103259f, // Rs
+0.436746f, 0.379529f, 0.000000f, -0.657361f, -0.089426f, 0.000000f, -0.067593f, 0.000000f, 0.051630f, // Lb
+0.436704f, -0.379539f, 0.000000f, -0.657348f, 0.089426f, 0.000000f, -0.067697f, 0.000000f, 0.051626f, // Rb
+};
+
+// Decoder matrix for 7dot1, ambisonic level 3
+static constexpr float decoderMatrix_7dot1_3_lf[8*16] = {
+0.205900f, 0.314866f, 0.000000f, 0.366133f, 0.144868f, 0.000000f, -0.081538f, 0.000000f, 0.023353f, 0.019489f, 0.000000f, 0.019857f, 0.000000f, 0.022683f, 0.000000f, -0.011066f, // L
+0.205883f, -0.314871f, 0.000000f, 0.366126f, -0.144867f, 0.000000f, -0.081652f, 0.000000f, 0.023347f, -0.019488f, 0.000000f, -0.019830f, 0.000000f, 0.022669f, 0.000000f, -0.011066f, // R
+0.135609f, 0.000000f, 0.000000f, 0.329297f, 0.000000f, 0.000000f, -0.056105f, 0.000000f, 0.111010f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.018999f, 0.000000f, 0.020478f, // C
+0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.276200f, 0.619758f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.106989f, 0.000000f, -0.163267f, -0.018501f, 0.000000f, 0.040168f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, // Ls
+0.276228f, -0.619768f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.106856f, 0.000000f, -0.163267f, 0.018501f, 0.000000f, -0.040194f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, // Rs
+0.276222f, 0.309884f, 0.000000f, -0.536733f, -0.141395f, 0.000000f, -0.106873f, 0.000000f, 0.081634f, 0.018501f, 0.000000f, 0.020094f, 0.000000f, -0.034809f, 0.000000f, 0.000000f, // Lb
+0.276196f, -0.309893f, 0.000000f, -0.536723f, 0.141394f, 0.000000f, -0.107038f, 0.000000f, 0.081628f, -0.018500f, 0.000000f, -0.020079f, 0.000000f, -0.034779f, 0.000000f, 0.000000f, // Rb
+};
+
+// Decoder matrix for 7dot1, ambisonic level 3
+static constexpr float decoderMatrix_7dot1_3_hf[8*16] = {
+0.343479f, 0.452315f, 0.000000f, 0.525962f, 0.147981f, 0.000000f, -0.083290f, 0.000000f, 0.023855f, 0.009908f, 0.000000f, 0.010095f, 0.000000f, 0.011532f, 0.000000f, -0.005626f, // L
+0.343450f, -0.452323f, 0.000000f, 0.525952f, -0.147980f, 0.000000f, -0.083406f, 0.000000f, 0.023849f, -0.009907f, 0.000000f, -0.010081f, 0.000000f, 0.011524f, 0.000000f, -0.005626f, // R
+0.226221f, 0.000000f, 0.000000f, 0.473046f, 0.000000f, 0.000000f, -0.057310f, 0.000000f, 0.113395f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.009658f, 0.000000f, 0.010410f, // C
+0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
+0.460752f, 0.890303f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.109288f, 0.000000f, -0.166775f, -0.009405f, 0.000000f, 0.020420f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, // Ls
+0.460799f, -0.890318f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.109152f, 0.000000f, -0.166775f, 0.009405f, 0.000000f, -0.020434f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, // Rs
+0.460790f, 0.445159f, 0.000000f, -0.771035f, -0.144433f, 0.000000f, -0.109170f, 0.000000f, 0.083387f, 0.009406f, 0.000000f, 0.010215f, 0.000000f, -0.017696f, 0.000000f, 0.000000f, // Lb
+0.460745f, -0.445171f, 0.000000f, -0.771020f, 0.144432f, 0.000000f, -0.109338f, 0.000000f, 0.083382f, -0.009405f, 0.000000f, -0.010207f, 0.000000f, -0.017681f, 0.000000f, 0.000000f, // Rb
+};
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/spatialaudio/qaudioengine.cpp b/src/spatialaudio/qaudioengine.cpp
new file mode 100644
index 000000000..82228f72f
--- /dev/null
+++ b/src/spatialaudio/qaudioengine.cpp
@@ -0,0 +1,602 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include <qaudioengine_p.h>
+#include <qambientsound_p.h>
+#include <qspatialsound_p.h>
+#include <qambientsound.h>
+#include <qaudioroom_p.h>
+#include <qaudiolistener.h>
+#include <resonance_audio.h>
+#include <qambisonicdecoder_p.h>
+#include <qaudiodecoder.h>
+#include <qmediadevices.h>
+#include <qiodevice.h>
+#include <qaudiosink.h>
+#include <qdebug.h>
+#include <qelapsedtimer.h>
+
+#include <QFile>
+
+QT_BEGIN_NAMESPACE
+
+// We'd like to have short buffer times, so the sound adjusts itself to changes
+// quickly, but times below 100ms seem to give stuttering on macOS.
+// It might be possible to set this value lower on other OSes.
+const int bufferTimeMs = 100;
+
+// This class lives in the audioThread, but pulls data from QAudioEnginePrivate
+// which lives in the mainThread.
+class QAudioOutputStream : public QIODevice
+{
+ Q_OBJECT
+public:
+ explicit QAudioOutputStream(QAudioEnginePrivate *d)
+ : d(d)
+ {
+ open(QIODevice::ReadOnly);
+ }
+ ~QAudioOutputStream();
+
+ qint64 readData(char *data, qint64 len) override;
+
+ qint64 writeData(const char *, qint64) override;
+
+ qint64 size() const override { return 0; }
+ qint64 bytesAvailable() const override {
+ return std::numeric_limits<qint64>::max();
+ }
+ bool isSequential() const override {
+ return true;
+ }
+ bool atEnd() const override {
+ return false;
+ }
+ qint64 pos() const override {
+ return m_pos;
+ }
+
+ Q_INVOKABLE void startOutput() {
+ d->mutex.lock();
+ Q_ASSERT(!sink);
+ QAudioFormat format;
+ auto channelConfig = d->outputMode == QAudioEngine::Surround ?
+ d->device.channelConfiguration() : QAudioFormat::ChannelConfigStereo;
+ if (channelConfig != QAudioFormat::ChannelConfigUnknown)
+ format.setChannelConfig(channelConfig);
+ else
+ format.setChannelCount(d->device.preferredFormat().channelCount());
+ format.setSampleRate(d->sampleRate);
+ format.setSampleFormat(QAudioFormat::Int16);
+ ambisonicDecoder.reset(new QAmbisonicDecoder(QAmbisonicDecoder::HighQuality, format));
+ sink.reset(new QAudioSink(d->device, format));
+ const qsizetype bufferSize = format.bytesForDuration(bufferTimeMs * 1000);
+ sink->setBufferSize(bufferSize);
+ d->mutex.unlock();
+ // It is important to unlock the mutex before starting the sink, as the sink will
+ // call readData() in the audio thread, which will try to lock the mutex (again)
+ sink->start(this);
+ }
+
+ Q_INVOKABLE void stopOutput() {
+ sink->stop();
+ sink.reset();
+ ambisonicDecoder.reset();
+ }
+
+ Q_INVOKABLE void restartOutput() {
+ stopOutput();
+ startOutput();
+ }
+
+ void setPaused(bool paused) {
+ if (paused)
+ sink->suspend();
+ else
+ sink->resume();
+ }
+
+private:
+ qint64 m_pos = 0;
+ QAudioEnginePrivate *d = nullptr;
+ std::unique_ptr<QAudioSink> sink;
+ std::unique_ptr<QAmbisonicDecoder> ambisonicDecoder;
+};
+
+
+QAudioOutputStream::~QAudioOutputStream()
+{
+}
+
+qint64 QAudioOutputStream::writeData(const char *, qint64)
+{
+ return 0;
+}
+
+qint64 QAudioOutputStream::readData(char *data, qint64 len)
+{
+ if (d->paused.loadRelaxed())
+ return 0;
+
+ QMutexLocker l(&d->mutex);
+ d->updateRooms();
+
+ int nChannels = ambisonicDecoder ? ambisonicDecoder->nOutputChannels() : 2;
+ if (len < nChannels*int(sizeof(float))*QAudioEnginePrivate::bufferSize)
+ return 0;
+
+ short *fd = (short *)data;
+ qint64 frames = len / nChannels / sizeof(short);
+ bool ok = true;
+ while (frames >= qint64(QAudioEnginePrivate::bufferSize)) {
+ // Fill input buffers
+ for (auto *source : std::as_const(d->sources)) {
+ auto *sp = QSpatialSoundPrivate::get(source);
+ if (!sp)
+ continue;
+ float buf[QAudioEnginePrivate::bufferSize];
+ sp->getBuffer(buf, QAudioEnginePrivate::bufferSize, 1);
+ d->resonanceAudio->api->SetInterleavedBuffer(sp->sourceId, buf, 1, QAudioEnginePrivate::bufferSize);
+ }
+ for (auto *source : std::as_const(d->stereoSources)) {
+ auto *sp = QAmbientSoundPrivate::get(source);
+ if (!sp)
+ continue;
+ float buf[2*QAudioEnginePrivate::bufferSize];
+ sp->getBuffer(buf, QAudioEnginePrivate::bufferSize, 2);
+ d->resonanceAudio->api->SetInterleavedBuffer(sp->sourceId, buf, 2, QAudioEnginePrivate::bufferSize);
+ }
+
+ if (ambisonicDecoder && d->outputMode == QAudioEngine::Surround) {
+ const float *channels[QAmbisonicDecoder::maxAmbisonicChannels];
+ const float *reverbBuffers[2];
+ int nSamples = d->resonanceAudio->getAmbisonicOutput(channels, reverbBuffers, ambisonicDecoder->nInputChannels());
+ Q_ASSERT(ambisonicDecoder->nOutputChannels() <= 8);
+ ambisonicDecoder->processBufferWithReverb(channels, reverbBuffers, fd, nSamples);
+ } else {
+ ok = d->resonanceAudio->api->FillInterleavedOutputBuffer(2, QAudioEnginePrivate::bufferSize, fd);
+ if (!ok) {
+ // If we get here, it means that resonanceAudio did not actually fill the buffer.
+ // Sometimes this is expected, for example if resonanceAudio does not have any sources.
+ // In this case we just fill the buffer with silence.
+ if (d->sources.isEmpty() && d->stereoSources.isEmpty()) {
+ memset(fd, 0, nChannels * QAudioEnginePrivate::bufferSize * sizeof(short));
+ } else {
+ // If we get here, it means that something unexpected happened, so bail.
+ qWarning() << " Reading failed!";
+ break;
+ }
+ }
+ }
+ fd += nChannels*QAudioEnginePrivate::bufferSize;
+ frames -= QAudioEnginePrivate::bufferSize;
+ }
+ const int bytesProcessed = ((char *)fd - data);
+ m_pos += bytesProcessed;
+ return bytesProcessed;
+}
+
+
+QAudioEnginePrivate::QAudioEnginePrivate()
+{
+ device = QMediaDevices::defaultAudioOutput();
+}
+
+QAudioEnginePrivate::~QAudioEnginePrivate()
+{
+ delete resonanceAudio;
+}
+
+void QAudioEnginePrivate::addSpatialSound(QSpatialSound *sound)
+{
+ QMutexLocker l(&mutex);
+ QAmbientSoundPrivate *sd = QAmbientSoundPrivate::get(sound);
+
+ sd->sourceId = resonanceAudio->api->CreateSoundObjectSource(vraudio::kBinauralHighQuality);
+ sources.append(sound);
+}
+
+void QAudioEnginePrivate::removeSpatialSound(QSpatialSound *sound)
+{
+ QMutexLocker l(&mutex);
+ QAmbientSoundPrivate *sd = QAmbientSoundPrivate::get(sound);
+
+ resonanceAudio->api->DestroySource(sd->sourceId);
+ sd->sourceId = vraudio::ResonanceAudioApi::kInvalidSourceId;
+ sources.removeOne(sound);
+}
+
+void QAudioEnginePrivate::addStereoSound(QAmbientSound *sound)
+{
+ QMutexLocker l(&mutex);
+ QAmbientSoundPrivate *sd = QAmbientSoundPrivate::get(sound);
+
+ sd->sourceId = resonanceAudio->api->CreateStereoSource(2);
+ stereoSources.append(sound);
+}
+
+void QAudioEnginePrivate::removeStereoSound(QAmbientSound *sound)
+{
+ QMutexLocker l(&mutex);
+ QAmbientSoundPrivate *sd = QAmbientSoundPrivate::get(sound);
+
+ resonanceAudio->api->DestroySource(sd->sourceId);
+ sd->sourceId = vraudio::ResonanceAudioApi::kInvalidSourceId;
+ stereoSources.removeOne(sound);
+}
+
+void QAudioEnginePrivate::addRoom(QAudioRoom *room)
+{
+ QMutexLocker l(&mutex);
+ rooms.append(room);
+}
+
+void QAudioEnginePrivate::removeRoom(QAudioRoom *room)
+{
+ QMutexLocker l(&mutex);
+ rooms.removeOne(room);
+}
+
+// This method is called from the audio thread
+void QAudioEnginePrivate::updateRooms()
+{
+ if (!roomEffectsEnabled)
+ return;
+
+ bool needUpdate = listenerPositionDirty;
+ listenerPositionDirty = false;
+
+ bool roomDirty = false;
+ for (const auto &room : rooms) {
+ auto *rd = QAudioRoomPrivate::get(room);
+ if (rd->dirty) {
+ roomDirty = true;
+ rd->update();
+ needUpdate = true;
+ }
+ }
+
+ if (!needUpdate)
+ return;
+
+ QVector3D listenerPos = listenerPosition();
+ float roomVolume = float(qInf());
+ QAudioRoom *room = nullptr;
+ // Find the smallest room that contains the listener and apply its room effects
+ for (auto *r : std::as_const(rooms)) {
+ QVector3D dim2 = r->dimensions()/2.;
+ float vol = dim2.x()*dim2.y()*dim2.z();
+ if (vol > roomVolume)
+ continue;
+ QVector3D dist = r->position() - listenerPos;
+ // transform into room coordinates
+ dist = r->rotation().rotatedVector(dist);
+ if (qAbs(dist.x()) <= dim2.x() &&
+ qAbs(dist.y()) <= dim2.y() &&
+ qAbs(dist.z()) <= dim2.z()) {
+ room = r;
+ roomVolume = vol;
+ }
+ }
+ if (room != currentRoom)
+ roomDirty = true;
+ const bool previousRoom = currentRoom;
+ currentRoom = room;
+
+ if (!roomDirty)
+ return;
+
+ // apply room to engine
+ if (!currentRoom) {
+ resonanceAudio->api->EnableRoomEffects(false);
+ return;
+ }
+ if (!previousRoom)
+ resonanceAudio->api->EnableRoomEffects(true);
+
+ QAudioRoomPrivate *rp = QAudioRoomPrivate::get(room);
+ resonanceAudio->api->SetReflectionProperties(rp->reflections);
+ resonanceAudio->api->SetReverbProperties(rp->reverb);
+
+ // update room effects for all sound sources
+ for (auto *s : std::as_const(sources)) {
+ auto *sp = QSpatialSoundPrivate::get(s);
+ if (!sp)
+ continue;
+ sp->updateRoomEffects();
+ }
+}
+
+QVector3D QAudioEnginePrivate::listenerPosition() const
+{
+ return listener ? listener->position() : QVector3D();
+}
+
+
+/*!
+ \class QAudioEngine
+ \inmodule QtSpatialAudio
+ \ingroup spatialaudio
+ \ingroup multimedia_audio
+
+ \brief QAudioEngine manages a three dimensional sound field.
+
+ You can use an instance of QAudioEngine to manage a sound field in
+ three dimensions. A sound field is defined by several QSpatialSound
+ objects that define a sound at a specified location in 3D space. You can also
+ add stereo overlays using QAmbientSound.
+
+ You can use QAudioListener to define the position of the person listening
+ to the sound field relative to the sound sources. Sound sources will be less audible
+ if the listener is further away from source. They will also get mapped to the corresponding
+ loudspeakers depending on the direction between listener and source.
+
+ QAudioEngine offers two output modes. The first mode renders the sound field to a set of
+ speakers, either a stereo speaker pair or a surround configuration. The second mode provides
+ an immersive 3D sound experience when using headphones.
+
+ Perception of sound localization is driven mainly by two factors. The first factor is timing
+ differences of the sound waves between left and right ear. The second factor comes from various
+ ways how sounds coming from different direcations create different types of reflections from our
+ ears and heads. See https://en.wikipedia.org/wiki/Sound_localization for more details.
+
+ The spatial audio engine emulates those timing differences and reflections through
+ Head related transfer functions (HRTF, see
+ https://en.wikipedia.org/wiki/Head-related_transfer_function). The functions used emulates those
+ effects for an average persons ears and head. It provides a good and immersive 3D sound localization
+ experience for most persons when using headphones.
+
+ The engine is rather versatile allowing you to define room properties and reverb settings to emulate
+ different types of rooms.
+
+ Sound sources can also be occluded dampening the sound coming from those sources.
+
+ The audio engine uses a coordinate system that is in centimeters by default. The axes are aligned with the
+ typical coordinate system used in 3D. Positive x points to the right, positive y points up and positive z points
+ backwards.
+
+*/
+
+/*!
+ \fn QAudioEngine::QAudioEngine()
+ \fn QAudioEngine::QAudioEngine(QObject *parent)
+ \fn QAudioEngine::QAudioEngine(int sampleRate, QObject *parent = nullptr)
+
+ Constructs a spatial audio engine with \a parent, if any.
+
+ The engine will operate with a sample rate given by \a sampleRate. The
+ default sample rate, if none is provided, is 44100 (44.1kHz).
+
+ Sound content that is not provided at that sample rate will automatically
+ get resampled to \a sampleRate when being processed by the engine. The
+ default sample rate is fine in most cases, but you can define a different
+ rate if most of your sound files are sampled with a different rate, and
+ avoid some CPU overhead for resampling.
+ */
+QAudioEngine::QAudioEngine(int sampleRate, QObject *parent)
+ : QObject(parent)
+ , d(new QAudioEnginePrivate)
+{
+ d->sampleRate = sampleRate;
+ d->resonanceAudio = new vraudio::ResonanceAudio(2, QAudioEnginePrivate::bufferSize, d->sampleRate);
+}
+
+/*!
+ Destroys the spatial audio engine.
+ */
+QAudioEngine::~QAudioEngine()
+{
+ stop();
+ delete d;
+}
+
+/*! \enum QAudioEngine::OutputMode
+ \value Surround Map the sounds to the loudspeaker configuration of the output device.
+ This is normally a stereo or surround speaker setup.
+ \value Stereo Map the sounds to the stereo loudspeaker configuration of the output device.
+ This will ignore any additional speakers and only use the left and right channels
+ to create a stero rendering of the sound field.
+ \value Headphone Use Headphone spatialization to create a 3D audio effect when listening
+ to the sound field through headphones
+*/
+
+/*!
+ \property QAudioEngine::outputMode
+
+ Sets or retrieves the current output mode of the engine.
+
+ \sa QAudioEngine::OutputMode
+ */
+void QAudioEngine::setOutputMode(OutputMode mode)
+{
+ if (d->outputMode == mode)
+ return;
+ d->outputMode = mode;
+ if (d->resonanceAudio->api)
+ d->resonanceAudio->api->SetStereoSpeakerMode(mode != Headphone);
+
+ QMetaObject::invokeMethod(d->outputStream.get(), "restartOutput", Qt::BlockingQueuedConnection);
+
+ emit outputModeChanged();
+}
+
+QAudioEngine::OutputMode QAudioEngine::outputMode() const
+{
+ return d->outputMode;
+}
+
+/*!
+ Returns the sample rate the engine has been configured with.
+ */
+int QAudioEngine::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+/*!
+ \property QAudioEngine::outputDevice
+
+ Sets or returns the device that is being used for playing the sound field.
+ */
+void QAudioEngine::setOutputDevice(const QAudioDevice &device)
+{
+ if (d->device == device)
+ return;
+ if (d->resonanceAudio->api) {
+ qWarning() << "Changing device on a running engine not implemented";
+ return;
+ }
+ d->device = device;
+ emit outputDeviceChanged();
+}
+
+QAudioDevice QAudioEngine::outputDevice() const
+{
+ return d->device;
+}
+
+/*!
+ \property QAudioEngine::masterVolume
+
+ Sets or returns volume being used to render the sound field.
+ */
+void QAudioEngine::setMasterVolume(float volume)
+{
+ if (d->masterVolume == volume)
+ return;
+ d->masterVolume = volume;
+ d->resonanceAudio->api->SetMasterVolume(volume);
+ emit masterVolumeChanged();
+}
+
+float QAudioEngine::masterVolume() const
+{
+ return d->masterVolume;
+}
+
+/*!
+ Starts the engine.
+ */
+void QAudioEngine::start()
+{
+ if (d->outputStream)
+ // already started
+ return;
+
+ d->resonanceAudio->api->SetStereoSpeakerMode(d->outputMode != Headphone);
+ d->resonanceAudio->api->SetMasterVolume(d->masterVolume);
+
+ d->outputStream.reset(new QAudioOutputStream(d));
+ d->outputStream->moveToThread(&d->audioThread);
+ d->audioThread.start(QThread::TimeCriticalPriority);
+
+ QMetaObject::invokeMethod(d->outputStream.get(), "startOutput");
+}
+
+/*!
+ Stops the engine.
+ */
+void QAudioEngine::stop()
+{
+ QMetaObject::invokeMethod(d->outputStream.get(), "stopOutput", Qt::BlockingQueuedConnection);
+ d->outputStream.reset();
+ d->audioThread.exit(0);
+ d->audioThread.wait();
+ delete d->resonanceAudio->api;
+ d->resonanceAudio->api = nullptr;
+}
+
+/*!
+ \property QAudioEngine::paused
+
+ Pauses the spatial audio engine.
+ */
+void QAudioEngine::setPaused(bool paused)
+{
+ bool old = d->paused.fetchAndStoreRelaxed(paused);
+ if (old != paused) {
+ if (d->outputStream)
+ d->outputStream->setPaused(paused);
+ emit pausedChanged();
+ }
+}
+
+bool QAudioEngine::paused() const
+{
+ return d->paused.loadRelaxed();
+}
+
+/*!
+ Enables room effects such as echos and reverb.
+
+ Enables room effects if \a enabled is true.
+ Room effects will only apply if you create one or more \l QAudioRoom objects
+ and the listener is inside at least one of the rooms. If the listener is inside
+ multiple rooms, the room with the smallest volume will be used.
+ */
+void QAudioEngine::setRoomEffectsEnabled(bool enabled)
+{
+ if (d->roomEffectsEnabled == enabled)
+ return;
+ d->roomEffectsEnabled = enabled;
+ d->resonanceAudio->roomEffectsEnabled = enabled;
+}
+
+/*!
+ Returns true if room effects are enabled.
+ */
+bool QAudioEngine::roomEffectsEnabled() const
+{
+ return d->roomEffectsEnabled;
+}
+
+/*!
+ \property QAudioEngine::distanceScale
+
+ Defines the scale of the coordinate system being used by the spatial audio engine.
+ By default, all units are in centimeters, in line with the default units being
+ used by Qt Quick 3D.
+
+ Set the distance scale to QAudioEngine::DistanceScaleMeter to get units in meters.
+*/
+void QAudioEngine::setDistanceScale(float scale)
+{
+ // multiply with 100, to get the conversion to meters that resonance audio uses
+ scale /= 100.f;
+ if (scale <= 0.0f) {
+ qWarning() << "QAudioEngine: Invalid distance scale.";
+ return;
+ }
+ if (scale == d->distanceScale)
+ return;
+ d->distanceScale = scale;
+ emit distanceScaleChanged();
+}
+
+float QAudioEngine::distanceScale() const
+{
+ return d->distanceScale*100.f;
+}
+
+/*!
+ \fn void QAudioEngine::pause()
+
+ Pauses playback.
+*/
+/*!
+ \fn void QAudioEngine::resume()
+
+ Resumes playback.
+*/
+/*!
+ \variable QAudioEngine::DistanceScaleCentimeter
+ \internal
+*/
+/*!
+ \variable QAudioEngine::DistanceScaleMeter
+ \internal
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qaudioengine.cpp"
+#include "qaudioengine.moc"
diff --git a/src/spatialaudio/qaudioengine.h b/src/spatialaudio/qaudioengine.h
new file mode 100644
index 000000000..13de2638a
--- /dev/null
+++ b/src/spatialaudio/qaudioengine.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOENGINE_H
+#define QAUDIOENGINE_H
+
+#include <QtSpatialAudio/qtspatialaudioglobal.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioEnginePrivate;
+class QAudioDevice;
+
+class Q_SPATIALAUDIO_EXPORT QAudioEngine : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(OutputMode outputMode READ outputMode WRITE setOutputMode NOTIFY outputModeChanged)
+ Q_PROPERTY(QAudioDevice outputDevice READ outputDevice WRITE setOutputDevice NOTIFY outputDeviceChanged)
+ Q_PROPERTY(float masterVolume READ masterVolume WRITE setMasterVolume NOTIFY masterVolumeChanged)
+ Q_PROPERTY(bool paused READ paused WRITE setPaused NOTIFY pausedChanged)
+ Q_PROPERTY(float distanceScale READ distanceScale WRITE setDistanceScale NOTIFY distanceScaleChanged)
+public:
+ QAudioEngine() : QAudioEngine(nullptr) {};
+ explicit QAudioEngine(QObject *parent) : QAudioEngine(44100, parent) {}
+ explicit QAudioEngine(int sampleRate, QObject *parent = nullptr);
+ ~QAudioEngine();
+
+ enum OutputMode {
+ Surround,
+ Stereo,
+ Headphone
+ };
+ Q_ENUM(OutputMode)
+
+ void setOutputMode(OutputMode mode);
+ OutputMode outputMode() const;
+
+ int sampleRate() const;
+
+ void setOutputDevice(const QAudioDevice &device);
+ QAudioDevice outputDevice() const;
+
+ void setMasterVolume(float volume);
+ float masterVolume() const;
+
+ void setPaused(bool paused);
+ bool paused() const;
+
+ void setRoomEffectsEnabled(bool enabled);
+ bool roomEffectsEnabled() const;
+
+ static constexpr float DistanceScaleCentimeter = 1.f;
+ static constexpr float DistanceScaleMeter = 100.f;
+
+ void setDistanceScale(float scale);
+ float distanceScale() const;
+
+Q_SIGNALS:
+ void outputModeChanged();
+ void outputDeviceChanged();
+ void masterVolumeChanged();
+ void pausedChanged();
+ void distanceScaleChanged();
+
+public Q_SLOTS:
+ void start();
+ void stop();
+
+ void pause() { setPaused(true); }
+ void resume() { setPaused(false); }
+
+private:
+ friend class QAudioEnginePrivate;
+ QAudioEnginePrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudio/qaudioengine_p.h b/src/spatialaudio/qaudioengine_p.h
new file mode 100644
index 000000000..59eaa4540
--- /dev/null
+++ b/src/spatialaudio/qaudioengine_p.h
@@ -0,0 +1,92 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOENGINE_P_H
+#define QAUDIOENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtspatialaudioglobal_p.h>
+#include <qaudioengine.h>
+#include <qaudiodevice.h>
+#include <qaudiodecoder.h>
+#include <qthread.h>
+#include <qmutex.h>
+#include <qurl.h>
+#include <qaudiobuffer.h>
+#include <qvector3d.h>
+#include <qfile.h>
+
+namespace vraudio {
+class ResonanceAudio;
+}
+
+QT_BEGIN_NAMESPACE
+
+class QSpatialSound;
+class QAmbientSound;
+class QAudioSink;
+class QAudioOutputStream;
+class QAmbisonicDecoder;
+class QAudioDecoder;
+class QAudioRoom;
+class QAudioListener;
+
+class QAudioEnginePrivate
+{
+public:
+ static QAudioEnginePrivate *get(QAudioEngine *engine) { return engine ? engine->d : nullptr; }
+
+ static constexpr int bufferSize = 128;
+
+ QAudioEnginePrivate();
+ ~QAudioEnginePrivate();
+ vraudio::ResonanceAudio *resonanceAudio = nullptr;
+ int sampleRate = 44100;
+ float masterVolume = 1.;
+ QAudioEngine::OutputMode outputMode = QAudioEngine::Surround;
+ bool roomEffectsEnabled = true;
+
+ // Resonance Audio uses meters internally, while Qt Quick 3D and our API uses cm by default.
+ // To make things independent from the scale setting, we store all distances in meters internally
+ // and convert in the setters and getters.
+ float distanceScale = 0.01f;
+
+ QMutex mutex;
+ QAudioDevice device;
+ QAtomicInteger<bool> paused = false;
+
+ QThread audioThread;
+ std::unique_ptr<QAudioOutputStream> outputStream;
+
+ QAudioListener *listener = nullptr;
+ QList<QSpatialSound *> sources;
+ QList<QAmbientSound *> stereoSources;
+ QList<QAudioRoom *> rooms;
+ mutable bool listenerPositionDirty = true;
+ QAudioRoom *currentRoom = nullptr;
+
+ void addSpatialSound(QSpatialSound *sound);
+ void removeSpatialSound(QSpatialSound *sound);
+ void addStereoSound(QAmbientSound *sound);
+ void removeStereoSound(QAmbientSound *sound);
+
+ void addRoom(QAudioRoom *room);
+ void removeRoom(QAudioRoom *room);
+ void updateRooms();
+
+ QVector3D listenerPosition() const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudio/qaudiolistener.cpp b/src/spatialaudio/qaudiolistener.cpp
new file mode 100644
index 000000000..ed4dbd58e
--- /dev/null
+++ b/src/spatialaudio/qaudiolistener.cpp
@@ -0,0 +1,134 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include "qaudiolistener.h"
+#include "qaudioengine_p.h"
+#include "resonance_audio.h"
+#include <qaudiosink.h>
+#include <qurl.h>
+#include <qdebug.h>
+#include <qaudiodecoder.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioListenerPrivate
+{
+public:
+ QAudioEngine *engine = nullptr;
+ QVector3D pos;
+ QQuaternion rotation;
+};
+
+/*!
+ \class QAudioListener
+ \inmodule QtSpatialAudio
+ \ingroup spatialaudio
+ \ingroup multimedia_audio
+
+ \brief Defines the position and orientation of the person listening to a sound field
+ defined by QAudioEngine.
+
+ A QAudioEngine can have exactly one listener that defines the position and orientation
+ of the person listening to the sound field.
+ */
+
+/*!
+ Creates a listener for the spatial audio engine for \a engine.
+ */
+QAudioListener::QAudioListener(QAudioEngine *engine)
+ : d(new QAudioListenerPrivate)
+{
+ setEngine(engine);
+}
+
+/*!
+ Destroys the listener.
+ */
+QAudioListener::~QAudioListener()
+{
+ // Unregister this listener from the engine
+ setEngine(nullptr);
+ delete d;
+}
+
+/*!
+ Sets the listener's position in 3D space to \a pos. Units are in centimeters
+ by default.
+
+ \sa QAudioEngine::distanceScale
+ */
+void QAudioListener::setPosition(QVector3D pos)
+{
+ auto *ep = QAudioEnginePrivate::get(d->engine);
+ if (!ep)
+ return;
+ pos *= ep->distanceScale;
+ if (d->pos == pos)
+ return;
+
+ d->pos = pos;
+ if (ep && ep->resonanceAudio->api) {
+ ep->resonanceAudio->api->SetHeadPosition(pos.x(), pos.y(), pos.z());
+ ep->listenerPositionDirty = true;
+ }
+}
+
+/*!
+ Returns the current position of the listener.
+ */
+QVector3D QAudioListener::position() const
+{
+ auto *ep = QAudioEnginePrivate::get(d->engine);
+ if (!ep)
+ return QVector3D();
+ return d->pos/ep->distanceScale;
+}
+
+/*!
+ Sets the listener's orientation in 3D space to \a q.
+ */
+void QAudioListener::setRotation(const QQuaternion &q)
+{
+ d->rotation = q;
+ auto *ep = QAudioEnginePrivate::get(d->engine);
+ if (ep && ep->resonanceAudio->api)
+ ep->resonanceAudio->api->SetHeadRotation(d->rotation.x(), d->rotation.y(), d->rotation.z(), d->rotation.scalar());
+}
+
+/*!
+ Returns the listener's orientation in 3D space.
+ */
+QQuaternion QAudioListener::rotation() const
+{
+ return d->rotation;
+}
+
+/*!
+ \internal
+ */
+void QAudioListener::setEngine(QAudioEngine *engine)
+{
+ if (d->engine) {
+ auto *ed = QAudioEnginePrivate::get(d->engine);
+ ed->listener = nullptr;
+ }
+ d->engine = engine;
+ if (d->engine) {
+ auto *ed = QAudioEnginePrivate::get(d->engine);
+ if (ed->listener) {
+ qWarning() << "Ignoring attempt to add a second listener to the spatial audio engine.";
+ d->engine = nullptr;
+ return;
+ }
+ ed->listener = this;
+ }
+}
+
+/*!
+ Returns the engine associated with this listener.
+ */
+QAudioEngine *QAudioListener::engine() const
+{
+ return d->engine;
+}
+
+QT_END_NAMESPACE
diff --git a/src/spatialaudio/qaudiolistener.h b/src/spatialaudio/qaudiolistener.h
new file mode 100644
index 000000000..daa088b6d
--- /dev/null
+++ b/src/spatialaudio/qaudiolistener.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QLISTENER_H
+#define QLISTENER_H
+
+#include <QtSpatialAudio/qtspatialaudioglobal.h>
+#include <QtCore/QObject>
+#include <QtMultimedia/qaudioformat.h>
+#include <QtGui/qvector3d.h>
+#include <QtGui/qquaternion.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioEngine;
+
+class QAudioListenerPrivate;
+class Q_SPATIALAUDIO_EXPORT QAudioListener : public QObject
+{
+public:
+ explicit QAudioListener(QAudioEngine *engine);
+ ~QAudioListener();
+
+ void setPosition(QVector3D pos);
+ QVector3D position() const;
+ void setRotation(const QQuaternion &q);
+ QQuaternion rotation() const;
+
+ QAudioEngine *engine() const;
+
+private:
+ void setEngine(QAudioEngine *engine);
+ QAudioListenerPrivate *d = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/spatial/qspatialaudioroom.cpp b/src/spatialaudio/qaudioroom.cpp
index 9e091c43f..3187abd10 100644
--- a/src/multimedia/spatial/qspatialaudioroom.cpp
+++ b/src/spatialaudio/qaudioroom.cpp
@@ -1,40 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qspatialaudioroom_p.h>
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include <qaudioroom_p.h>
QT_BEGIN_NAMESPACE
@@ -103,17 +69,26 @@ struct {
}
-float QSpatialAudioRoomPrivate::wallOcclusion(QSpatialAudioRoom::Wall wall) const
+// make sure the wall definitions agree with resonance audio
+
+static_assert(QAudioRoom::LeftWall == 0);
+static_assert(QAudioRoom::RightWall == 1);
+static_assert(QAudioRoom::Floor == 2);
+static_assert(QAudioRoom::Ceiling == 3);
+static_assert(QAudioRoom::FrontWall == 4);
+static_assert(QAudioRoom::BackWall == 5);
+
+float QAudioRoomPrivate::wallOcclusion(QAudioRoom::Wall wall) const
{
return m_wallOcclusion[wall] < 0 ? occlusionAndDampening[roomProperties.material_names[wall]].occlusion : m_wallOcclusion[wall];
}
-float QSpatialAudioRoomPrivate::wallDampening(QSpatialAudioRoom::Wall wall) const
+float QAudioRoomPrivate::wallDampening(QAudioRoom::Wall wall) const
{
return m_wallDampening[wall] < 0 ? occlusionAndDampening[roomProperties.material_names[wall]].dampening : m_wallDampening[wall];
}
-void QSpatialAudioRoomPrivate::update()
+void QAudioRoomPrivate::update()
{
if (!dirty)
return;
@@ -124,16 +99,17 @@ void QSpatialAudioRoomPrivate::update()
/*!
- \class QSpatialAudioRoom
- \inmodule QtMultimedia
- \ingroup multimedia_spatialaudio
+ \class QAudioRoom
+ \inmodule QtSpatialAudio
+ \ingroup spatialaudio
+ \ingroup multimedia_audio
Defines a room for the spatial audio engine.
If the listener is inside a room, first order sound reflections and reverb
matching the rooms properties will get applied to the sound field.
- A room is always square and defined by it's center position, it's orientation and dimensions.
+ A room is always square and defined by its center position, its orientation and dimensions.
Each of the 6 walls of the room can be made of different materials that will contribute
to the computed reflections and reverb that the listener will experience while being inside
the room.
@@ -143,30 +119,30 @@ void QSpatialAudioRoomPrivate::update()
*/
/*!
- Constructs a QSpatialAudioRoom for \a engine.
+ Constructs a QAudioRoom for \a engine.
*/
-QSpatialAudioRoom::QSpatialAudioRoom(QSpatialAudioEngine *engine)
- : d(new QSpatialAudioRoomPrivate)
+QAudioRoom::QAudioRoom(QAudioEngine *engine)
+ : d(new QAudioRoomPrivate)
{
Q_ASSERT(engine);
d->engine = engine;
- auto *ep = QSpatialAudioEnginePrivate::get(engine);
+ auto *ep = QAudioEnginePrivate::get(engine);
ep->addRoom(this);
}
/*!
Destroys the room.
*/
-QSpatialAudioRoom::~QSpatialAudioRoom()
+QAudioRoom::~QAudioRoom()
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
ep->removeRoom(this);
delete d;
}
/*!
- \enum QSpatialAudioRoom::Material
+ \enum QAudioRoom::Material
Defines different materials that can be applied to the different walls of the room.
@@ -197,30 +173,30 @@ QSpatialAudioRoom::~QSpatialAudioRoom()
*/
/*!
- \enum QSpatialAudioRoom::Wall
+ \enum QAudioRoom::Wall
An enum defining the 6 walls of the room
\value LeftWall Left wall (negative x)
\value RightWall Right wall (positive x)
- \value BackWall Back wall (negative y)
- \value FrontWall Front wall (positive y)
- \value Floor Bottom wall (negative z)
- \value Ceiling Top wall (positive z)
+ \value Floor Bottom wall (negative y)
+ \value Ceiling Top wall (positive y)
+ \value FrontWall Front wall (negative z)
+ \value BackWall Back wall (positive z)
*/
/*!
- \property QSpatialAudioRoom::position
+ \property QAudioRoom::position
Defines the position of the center of the room in 3D space. Units are in centimeters
by default.
- \sa dimensions, QSpatialAudioEngine::distanceScale
+ \sa dimensions, QAudioEngine::distanceScale
*/
-void QSpatialAudioRoom::setPosition(QVector3D pos)
+void QAudioRoom::setPosition(QVector3D pos)
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
pos *= ep->distanceScale;
if (toVector(d->roomProperties.position) == pos)
return;
@@ -229,25 +205,25 @@ void QSpatialAudioRoom::setPosition(QVector3D pos)
emit positionChanged();
}
-QVector3D QSpatialAudioRoom::position() const
+QVector3D QAudioRoom::position() const
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
auto pos = toVector(d->roomProperties.position);
pos /= ep->distanceScale;
return pos;
}
/*!
- \property QSpatialAudioRoom::dimensions
+ \property QAudioRoom::dimensions
Defines the dimensions of the room in 3D space. Units are in centimeters
by default.
- \sa position, QSpatialAudioEngine::distanceScale
+ \sa position, QAudioEngine::distanceScale
*/
-void QSpatialAudioRoom::setDimensions(QVector3D dim)
+void QAudioRoom::setDimensions(QVector3D dim)
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
dim *= ep->distanceScale;
if (toVector(d->roomProperties.dimensions) == dim)
return;
@@ -256,20 +232,20 @@ void QSpatialAudioRoom::setDimensions(QVector3D dim)
emit dimensionsChanged();
}
-QVector3D QSpatialAudioRoom::dimensions() const
+QVector3D QAudioRoom::dimensions() const
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
auto dim = toVector(d->roomProperties.dimensions);
dim /= ep->distanceScale;
return dim;
}
/*!
- \property QSpatialAudioRoom::rotation
+ \property QAudioRoom::rotation
Defines the orientation of the room in 3D space.
*/
-void QSpatialAudioRoom::setRotation(const QQuaternion &q)
+void QAudioRoom::setRotation(const QQuaternion &q)
{
if (toQuaternion(d->roomProperties.rotation) == q)
return;
@@ -278,20 +254,25 @@ void QSpatialAudioRoom::setRotation(const QQuaternion &q)
emit rotationChanged();
}
-QQuaternion QSpatialAudioRoom::rotation() const
+QQuaternion QAudioRoom::rotation() const
{
return toQuaternion(d->roomProperties.rotation);
}
/*!
+ \fn void QAudioRoom::wallsChanged()
+
+ Signals when the wall material changes.
+*/
+/*!
Sets \a wall to \a material.
Different wall materials have different reflection and reverb properties
that influence the sound of the room.
- \sa wallMaterial(), Material, QSpatialAudioRoom::Wall
+ \sa wallMaterial(), Material, QAudioRoom::Wall
*/
-void QSpatialAudioRoom::setWallMaterial(Wall wall, Material material)
+void QAudioRoom::setWallMaterial(Wall wall, Material material)
{
static_assert(vraudio::kUniform == int(UniformMaterial));
static_assert(vraudio::kTransparent == int(Transparent));
@@ -306,15 +287,15 @@ void QSpatialAudioRoom::setWallMaterial(Wall wall, Material material)
/*!
returns the material being used for \a wall.
- \sa setWallMaterial(), Material, QSpatialAudioRoom::Wall
+ \sa setWallMaterial(), Material, QAudioRoom::Wall
*/
-QSpatialAudioRoom::Material QSpatialAudioRoom::wallMaterial(Wall wall) const
+QAudioRoom::Material QAudioRoom::wallMaterial(Wall wall) const
{
return Material(d->roomProperties.material_names[int(wall)]);
}
/*!
- \property QSpatialAudioRoom::reflectionGain
+ \property QAudioRoom::reflectionGain
A gain factor for reflections generated in this room. A value
from 0 to 1 will dampen reflections, while a value larger than 1
@@ -323,7 +304,7 @@ QSpatialAudioRoom::Material QSpatialAudioRoom::wallMaterial(Wall wall) const
The default is 1, a factor of 0 disables reflections. Negative
values are mapped to 0.
*/
-void QSpatialAudioRoom::setReflectionGain(float factor)
+void QAudioRoom::setReflectionGain(float factor)
{
if (factor < 0.)
factor = 0.;
@@ -331,16 +312,16 @@ void QSpatialAudioRoom::setReflectionGain(float factor)
return;
d->roomProperties.reflection_scalar = factor;
d->dirty = true;
- reflectionGainChanged();
+ emit reflectionGainChanged();
}
-float QSpatialAudioRoom::reflectionGain() const
+float QAudioRoom::reflectionGain() const
{
return d->roomProperties.reflection_scalar;
}
/*!
- \property QSpatialAudioRoom::reverbGain
+ \property QAudioRoom::reverbGain
A gain factor for reverb generated in this room. A value
from 0 to 1 will dampen reverb, while a value larger than 1
@@ -349,7 +330,7 @@ float QSpatialAudioRoom::reflectionGain() const
The default is 1, a factor of 0 disables reverb. Negative
values are mapped to 0.
*/
-void QSpatialAudioRoom::setReverbGain(float factor)
+void QAudioRoom::setReverbGain(float factor)
{
if (factor < 0)
factor = 0;
@@ -357,16 +338,16 @@ void QSpatialAudioRoom::setReverbGain(float factor)
return;
d->roomProperties.reverb_gain = factor;
d->dirty = true;
- reverbGainChanged();
+ emit reverbGainChanged();
}
-float QSpatialAudioRoom::reverbGain() const
+float QAudioRoom::reverbGain() const
{
return d->roomProperties.reverb_gain;
}
/*!
- \property QSpatialAudioRoom::reverbTime
+ \property QAudioRoom::reverbTime
A factor to be applies to all reverb timings generated for this room.
Larger values will lead to longer reverb timings, making the room sound
@@ -374,7 +355,7 @@ float QSpatialAudioRoom::reverbGain() const
The default is 1. Negative values are mapped to 0.
*/
-void QSpatialAudioRoom::setReverbTime(float factor)
+void QAudioRoom::setReverbTime(float factor)
{
if (factor < 0)
factor = 0;
@@ -382,16 +363,16 @@ void QSpatialAudioRoom::setReverbTime(float factor)
return;
d->roomProperties.reverb_time = factor;
d->dirty = true;
- reverbTimeChanged();
+ emit reverbTimeChanged();
}
-float QSpatialAudioRoom::reverbTime() const
+float QAudioRoom::reverbTime() const
{
return d->roomProperties.reverb_time;
}
/*!
- \property QSpatialAudioRoom::reverbBrightness
+ \property QAudioRoom::reverbBrightness
A brightness factor to be applied to the generated reverb.
A positive value will increase reverb for higher frequencies and
@@ -399,20 +380,20 @@ float QSpatialAudioRoom::reverbTime() const
The default is 0.
*/
-void QSpatialAudioRoom::setReverbBrightness(float factor)
+void QAudioRoom::setReverbBrightness(float factor)
{
if (d->roomProperties.reverb_brightness == factor)
return;
d->roomProperties.reverb_brightness = factor;
d->dirty = true;
- reverbBrightnessChanged();
+ emit reverbBrightnessChanged();
}
-float QSpatialAudioRoom::reverbBrightness() const
+float QAudioRoom::reverbBrightness() const
{
return d->roomProperties.reverb_brightness;
}
QT_END_NAMESPACE
-#include "moc_qspatialaudioroom.cpp"
+#include "moc_qaudioroom.cpp"
diff --git a/src/multimedia/spatial/qspatialaudioroom.h b/src/spatialaudio/qaudioroom.h
index a3bba8930..eb2b88cbc 100644
--- a/src/multimedia/spatial/qspatialaudioroom.h
+++ b/src/spatialaudio/qaudioroom.h
@@ -1,53 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSPATIALAUDIOROOM_H
-#define QSPATIALAUDIOROOM_H
-
-#include <QtMultimedia/qtmultimediaglobal.h>
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QAUDIOROOM_H
+#define QAUDIOROOM_H
+
+#include <QtSpatialAudio/qtspatialaudioglobal.h>
#include <QtCore/qobject.h>
#include <QtGui/qvector3d.h>
QT_BEGIN_NAMESPACE
-class QSpatialAudioEngine;
-class QSpatialAudioRoomPrivate;
+class QAudioEngine;
+class QAudioRoomPrivate;
-class Q_MULTIMEDIA_EXPORT QSpatialAudioRoom : public QObject
+class Q_SPATIALAUDIO_EXPORT QAudioRoom : public QObject
{
Q_OBJECT
Q_PROPERTY(QVector3D position READ position WRITE setPosition NOTIFY positionChanged)
@@ -58,8 +24,8 @@ class Q_MULTIMEDIA_EXPORT QSpatialAudioRoom : public QObject
Q_PROPERTY(float reverbTime READ reverbTime WRITE setReverbTime NOTIFY reverbTimeChanged)
Q_PROPERTY(float reverbBrightness READ reverbBrightness WRITE setReverbBrightness NOTIFY reverbBrightnessChanged)
public:
- QSpatialAudioRoom(QSpatialAudioEngine *engine);
- ~QSpatialAudioRoom();
+ explicit QAudioRoom(QAudioEngine *engine);
+ ~QAudioRoom();
enum Material {
Transparent,
@@ -100,7 +66,7 @@ public:
void setPosition(QVector3D pos);
QVector3D position() const;
- void setDimensions(QVector3D pos);
+ void setDimensions(QVector3D dim);
QVector3D dimensions() const;
void setRotation(const QQuaternion &q);
@@ -132,8 +98,8 @@ Q_SIGNALS:
void reverbBrightnessChanged();
private:
- friend class QSpatialAudioRoomPrivate;
- QSpatialAudioRoomPrivate *d;
+ friend class QAudioRoomPrivate;
+ QAudioRoomPrivate *d;
};
QT_END_NAMESPACE
diff --git a/src/spatialaudio/qaudioroom_p.h b/src/spatialaudio/qaudioroom_p.h
new file mode 100644
index 000000000..b320dc9fc
--- /dev/null
+++ b/src/spatialaudio/qaudioroom_p.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QAUDIOROOM_P_H
+#define QAUDIOROOM_P_H
+
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qtspatialaudioglobal_p.h>
+#include <qaudioroom.h>
+#include <qaudioengine_p.h>
+#include <QtGui/qquaternion.h>
+
+#include <resonance_audio.h>
+#include "platforms/common/room_effects_utils.h"
+#include "platforms/common/room_properties.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAudioRoomPrivate
+{
+public:
+ static QAudioRoomPrivate *get(const QAudioRoom *r) { return r->d; }
+
+ QAudioEngine *engine = nullptr;
+ vraudio::RoomProperties roomProperties;
+ bool dirty = true;
+
+ vraudio::ReverbProperties reverb;
+ vraudio::ReflectionProperties reflections;
+
+ float m_wallOcclusion[6] = { -1.f, -1.f, -1.f, -1.f, -1.f, -1.f };
+ float m_wallDampening[6] = { -1.f, -1.f, -1.f, -1.f, -1.f, -1.f };
+
+ float wallOcclusion(QAudioRoom::Wall wall) const;
+ float wallDampening(QAudioRoom::Wall wall) const;
+
+ void update();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/multimedia/spatial/qspatialaudiosoundsource.cpp b/src/spatialaudio/qspatialsound.cpp
index c86e6351b..84512556c 100644
--- a/src/multimedia/spatial/qspatialaudiosoundsource.cpp
+++ b/src/spatialaudio/qspatialsound.cpp
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "qspatialaudioroom_p.h"
-#include "qspatialaudiosoundsource_p.h"
-#include "qspatialaudiolistener.h"
-#include "qspatialaudioengine_p.h"
-#include "qspatialaudioroom.h"
-#include "api/resonance_audio_api.h"
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include "qaudioroom_p.h"
+#include "qspatialsound_p.h"
+#include "qaudiolistener.h"
+#include "qaudioengine_p.h"
+#include "resonance_audio.h"
#include <qaudiosink.h>
#include <qurl.h>
#include <qdebug.h>
@@ -48,14 +13,15 @@
QT_BEGIN_NAMESPACE
/*!
- \class QSpatialAudioSoundSource
- \inmodule QtMultimedia
- \ingroup multimedia_spatialaudio
+ \class QSpatialSound
+ \inmodule QtSpatialAudio
+ \ingroup spatialaudio
+ \ingroup multimedia_audio
\brief A sound object in 3D space.
- QSpatialAudioSoundSource represents an audible object in 3D space. You can define
- it's position and orientation in space, set the sound it is playing and define a
+ QSpatialSound represents an audible object in 3D space. You can define
+ its position and orientation in space, set the sound it is playing and define a
volume for the object.
The object can have different attenuation behavior, emit sound mainly in one direction
@@ -66,8 +32,8 @@ QT_BEGIN_NAMESPACE
Creates a spatial sound source for \a engine. The object can be placed in
3D space and will be louder the closer to the listener it is.
*/
-QSpatialAudioSoundSource::QSpatialAudioSoundSource(QSpatialAudioEngine *engine)
- : d(new QSpatialAudioSoundSourcePrivate(this))
+QSpatialSound::QSpatialSound(QAudioEngine *engine)
+ : d(new QSpatialSoundPrivate(this))
{
setEngine(engine);
}
@@ -75,100 +41,99 @@ QSpatialAudioSoundSource::QSpatialAudioSoundSource(QSpatialAudioEngine *engine)
/*!
Destroys the sound source.
*/
-QSpatialAudioSoundSource::~QSpatialAudioSoundSource()
+QSpatialSound::~QSpatialSound()
{
setEngine(nullptr);
}
/*!
- \property QSpatialAudioSoundSource::position
+ \property QSpatialSound::position
Defines the position of the sound source in 3D space. Units are in centimeters
by default.
- \sa QSpatialAudioEngine::distanceScale
+ \sa QAudioEngine::distanceScale
*/
-void QSpatialAudioSoundSource::setPosition(QVector3D pos)
+void QSpatialSound::setPosition(QVector3D pos)
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
pos *= ep->distanceScale;
d->pos = pos;
if (ep)
- ep->api->SetSourcePosition(d->sourceId, pos.x(), pos.y(), pos.z());
- d->updateRoomEffects();
+ ep->resonanceAudio->api->SetSourcePosition(d->sourceId, pos.x(), pos.y(), pos.z());
emit positionChanged();
}
-QVector3D QSpatialAudioSoundSource::position() const
+QVector3D QSpatialSound::position() const
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
return d->pos/ep->distanceScale;
}
/*!
- \property QSpatialAudioSoundSource::rotation
+ \property QSpatialSound::rotation
Defines the orientation of the sound source in 3D space.
*/
-void QSpatialAudioSoundSource::setRotation(const QQuaternion &q)
+void QSpatialSound::setRotation(const QQuaternion &q)
{
d->rotation = q;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
- ep->api->SetSourceRotation(d->sourceId, q.x(), q.y(), q.z(), q.scalar());
+ ep->resonanceAudio->api->SetSourceRotation(d->sourceId, q.x(), q.y(), q.z(), q.scalar());
emit rotationChanged();
}
-QQuaternion QSpatialAudioSoundSource::rotation() const
+QQuaternion QSpatialSound::rotation() const
{
return d->rotation;
}
/*!
- \property QSpatialAudioSoundSource::volume
+ \property QSpatialSound::volume
Defines the volume of the sound.
Values between 0 and 1 will attenuate the sound, while values above 1
provide an additional gain boost.
*/
-void QSpatialAudioSoundSource::setVolume(float volume)
+void QSpatialSound::setVolume(float volume)
{
if (d->volume == volume)
return;
d->volume = volume;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
- ep->api->SetSourceVolume(d->sourceId, d->volume*d->wallDampening);
+ ep->resonanceAudio->api->SetSourceVolume(d->sourceId, d->volume*d->wallDampening);
emit volumeChanged();
}
-float QSpatialAudioSoundSource::volume() const
+float QSpatialSound::volume() const
{
return d->volume;
}
/*!
- \enum QSpatialAudioSoundSource::DistanceModel
+ \enum QSpatialSound::DistanceModel
Defines how the volume of the sound scales with distance to the listener.
- \value DistanceModel_Logarithmic Volume decreases logarithmically with distance.
- \value DistanceModel_Linear Volume decreases linearly with distance.
- \value DistanceModel_ManualAttenutation Attenuation is defined manually using the
+ \value Logarithmic Volume decreases logarithmically with distance.
+ \value Linear Volume decreases linearly with distance.
+ \value ManualAttenuation Attenuation is defined manually using the
\l manualAttenuation property.
*/
/*!
- \property QSpatialAudioSoundSource::distanceModel
+ \property QSpatialSound::distanceModel
Defines distance model for this sound source. The volume starts scaling down
from \l size to \l distanceCutoff. The volume is constant for distances smaller
than size and zero for distances larger than the cutoff distance.
- \sa QSpatialAudioSoundSource::DistanceModel
+ \sa QSpatialSound::DistanceModel
*/
-void QSpatialAudioSoundSource::setDistanceModel(DistanceModel model)
+void QSpatialSound::setDistanceModel(DistanceModel model)
{
if (d->distanceModel == model)
return;
@@ -178,35 +143,37 @@ void QSpatialAudioSoundSource::setDistanceModel(DistanceModel model)
emit distanceModelChanged();
}
-void QSpatialAudioSoundSourcePrivate::updateDistanceModel()
+void QSpatialSoundPrivate::updateDistanceModel()
{
if (!engine || sourceId < 0)
return;
- auto *ep = QSpatialAudioEnginePrivate::get(engine);
+ auto *ep = QAudioEnginePrivate::get(engine);
vraudio::DistanceRolloffModel dm = vraudio::kLogarithmic;
switch (distanceModel) {
- case QSpatialAudioSoundSource::DistanceModel_Linear:
+ case QSpatialSound::DistanceModel::Linear:
dm = vraudio::kLinear;
break;
- case QSpatialAudioSoundSource::DistanceModel_ManualAttenutation:
+ case QSpatialSound::DistanceModel::ManualAttenuation:
dm = vraudio::kNone;
break;
default:
break;
}
- ep->api->SetSourceDistanceModel(sourceId, dm, size, distanceCutoff);
+ ep->resonanceAudio->api->SetSourceDistanceModel(sourceId, dm, size, distanceCutoff);
}
-void QSpatialAudioSoundSourcePrivate::updateRoomEffects()
+void QSpatialSoundPrivate::updateRoomEffects()
{
if (!engine || sourceId < 0)
return;
- auto *ep = QSpatialAudioEnginePrivate::get(engine);
+ auto *ep = QAudioEnginePrivate::get(engine);
if (!ep->currentRoom)
return;
- auto *rp = QSpatialAudioRoomPrivate::get(ep->currentRoom);
+ auto *rp = QAudioRoomPrivate::get(ep->currentRoom);
+ if (!rp)
+ return;
QVector3D roomDim2 = ep->currentRoom->dimensions()/2.;
QVector3D roomPos = ep->currentRoom->position();
@@ -218,7 +185,7 @@ void QSpatialAudioSoundSourcePrivate::updateRoomEffects()
qAbs(dist.y()) <= roomDim2.y() &&
qAbs(dist.z()) <= roomDim2.z()) {
// Source is inside room, apply
- ep->api->SetSourceRoomEffectsGain(sourceId, 1);
+ ep->resonanceAudio->api->SetSourceRoomEffectsGain(sourceId, 1);
wallDampening = 1.;
wallOcclusion = 0.;
} else {
@@ -237,10 +204,10 @@ void QSpatialAudioSoundSourcePrivate::updateRoomEffects()
// Very rough approximation, use the size of the source plus twice the size of our head.
// One could probably improve upon this.
const float transitionDistance = size + 0.4;
- QSpatialAudioRoom::Wall walls[3];
- walls[X] = direction.x() > 0 ? QSpatialAudioRoom::RightWall : QSpatialAudioRoom::LeftWall;
- walls[Y] = direction.y() > 0 ? QSpatialAudioRoom::FrontWall : QSpatialAudioRoom::BackWall;
- walls[Z] = direction.z() > 0 ? QSpatialAudioRoom::Ceiling : QSpatialAudioRoom::Floor;
+ QAudioRoom::Wall walls[3];
+ walls[X] = direction.x() > 0 ? QAudioRoom::RightWall : QAudioRoom::LeftWall;
+ walls[Y] = direction.y() > 0 ? QAudioRoom::FrontWall : QAudioRoom::BackWall;
+ walls[Z] = direction.z() > 0 ? QAudioRoom::Ceiling : QAudioRoom::Floor;
float factors[3] = { 0., 0., 0. };
bool foundWall = false;
if (direction.x() != 0) {
@@ -297,27 +264,27 @@ void QSpatialAudioSoundSourcePrivate::updateRoomEffects()
}
// qDebug() << "intersection with wall" << walls[0] << walls[1] << walls[2] << factors[0] << factors[1] << factors[2] << wallDampening << wallOcclusion;
- ep->api->SetSourceRoomEffectsGain(sourceId, 0);
+ ep->resonanceAudio->api->SetSourceRoomEffectsGain(sourceId, 0);
}
- ep->api->SetSoundObjectOcclusionIntensity(sourceId, occlusionIntensity + wallOcclusion);
- ep->api->SetSourceVolume(sourceId, volume*wallDampening);
+ ep->resonanceAudio->api->SetSoundObjectOcclusionIntensity(sourceId, occlusionIntensity + wallOcclusion);
+ ep->resonanceAudio->api->SetSourceVolume(sourceId, volume*wallDampening);
}
-QSpatialAudioSoundSource::DistanceModel QSpatialAudioSoundSource::distanceModel() const
+QSpatialSound::DistanceModel QSpatialSound::distanceModel() const
{
return d->distanceModel;
}
/*!
- \property QSpatialAudioSoundSource::size
+ \property QSpatialSound::size
Defines the size of the sound source. If the listener is closer to the sound
object than the size, volume will stay constant. The size is also used to for
occlusion calculations, where large sources can be partially occluded by a wall.
*/
-void QSpatialAudioSoundSource::setSize(float size)
+void QSpatialSound::setSize(float size)
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
size *= ep->distanceScale;
if (d->size == size)
return;
@@ -327,22 +294,22 @@ void QSpatialAudioSoundSource::setSize(float size)
emit sizeChanged();
}
-float QSpatialAudioSoundSource::size() const
+float QSpatialSound::size() const
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
return d->size/ep->distanceScale;
}
/*!
- \property QSpatialAudioSoundSource::distanceCutoff
+ \property QSpatialSound::distanceCutoff
Defines a distance beyond which sound coming from the source will cutoff.
If the listener is further away from the sound object than the cutoff
distance it won't be audible anymore.
*/
-void QSpatialAudioSoundSource::setDistanceCutoff(float cutoff)
+void QSpatialSound::setDistanceCutoff(float cutoff)
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
cutoff *= ep->distanceScale;
if (d->distanceCutoff == cutoff)
return;
@@ -352,36 +319,36 @@ void QSpatialAudioSoundSource::setDistanceCutoff(float cutoff)
emit distanceCutoffChanged();
}
-float QSpatialAudioSoundSource::distanceCutoff() const
+float QSpatialSound::distanceCutoff() const
{
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
return d->distanceCutoff/ep->distanceScale;
}
/*!
- \property QSpatialAudioSoundSource::manualAttenuation
+ \property QSpatialSound::manualAttenuation
Defines a manual attenuation factor if \l distanceModel is set to
- QSpatialAudioSoundSource::DistanceModel_ManualAttenutation.
+ QSpatialSound::DistanceModel::ManualAttenuation.
*/
-void QSpatialAudioSoundSource::setManualAttenuation(float attenuation)
+void QSpatialSound::setManualAttenuation(float attenuation)
{
if (d->manualAttenuation == attenuation)
return;
d->manualAttenuation = attenuation;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
- ep->api->SetSourceDistanceAttenuation(d->sourceId, d->manualAttenuation);
+ ep->resonanceAudio->api->SetSourceDistanceAttenuation(d->sourceId, d->manualAttenuation);
emit manualAttenuationChanged();
}
-float QSpatialAudioSoundSource::manualAttenuation() const
+float QSpatialSound::manualAttenuation() const
{
return d->manualAttenuation;
}
/*!
- \property QSpatialAudioSoundSource::occlusionIntensity
+ \property QSpatialSound::occlusionIntensity
Defines how much the object is occluded. 0 implies the object is
not occluded at all, 1 implies the sound source is fully occluded by
@@ -396,24 +363,24 @@ float QSpatialAudioSoundSource::manualAttenuation() const
The default is 0.
*/
-void QSpatialAudioSoundSource::setOcclusionIntensity(float occlusion)
+void QSpatialSound::setOcclusionIntensity(float occlusion)
{
if (d->occlusionIntensity == occlusion)
return;
d->occlusionIntensity = occlusion;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
- ep->api->SetSoundObjectOcclusionIntensity(d->sourceId, d->occlusionIntensity + d->wallOcclusion);
+ ep->resonanceAudio->api->SetSoundObjectOcclusionIntensity(d->sourceId, d->occlusionIntensity + d->wallOcclusion);
emit occlusionIntensityChanged();
}
-float QSpatialAudioSoundSource::occlusionIntensity() const
+float QSpatialSound::occlusionIntensity() const
{
return d->occlusionIntensity;
}
/*!
- \property QSpatialAudioSoundSource::directivity
+ \property QSpatialSound::directivity
Defines the directivity of the sound source. A value of 0 implies that the sound is
emitted equally in all directions, while a value of 1 implies that the source mainly
@@ -421,85 +388,85 @@ float QSpatialAudioSoundSource::occlusionIntensity() const
Valid values are between 0 and 1, the default is 0.
*/
-void QSpatialAudioSoundSource::setDirectivity(float alpha)
+void QSpatialSound::setDirectivity(float alpha)
{
alpha = qBound(0., alpha, 1.);
if (alpha == d->directivity)
return;
d->directivity = alpha;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
- ep->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
+ ep->resonanceAudio->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
emit directivityChanged();
}
-float QSpatialAudioSoundSource::directivity() const
+float QSpatialSound::directivity() const
{
return d->directivity;
}
/*!
- \property QSpatialAudioSoundSource::directivityOrder
+ \property QSpatialSound::directivityOrder
Defines the order of the directivity of the sound source. A higher order
implies a sharper localization of the sound cone.
The minimum value and default for this property is 1.
*/
-void QSpatialAudioSoundSource::setDirectivityOrder(float order)
+void QSpatialSound::setDirectivityOrder(float order)
{
order = qMax(order, 1.);
if (order == d->directivityOrder)
return;
d->directivityOrder = order;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
- ep->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
+ ep->resonanceAudio->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
emit directivityChanged();
}
-float QSpatialAudioSoundSource::directivityOrder() const
+float QSpatialSound::directivityOrder() const
{
return d->directivityOrder;
}
/*!
- \property QSpatialAudioSoundSource::nearFieldGain
+ \property QSpatialSound::nearFieldGain
Defines the near field gain for the sound source. Valid values are between 0 and 1.
A near field gain of 1 will raise the volume of the sound signal by approx 20 dB for
distances very close to the listener.
*/
-void QSpatialAudioSoundSource::setNearFieldGain(float gain)
+void QSpatialSound::setNearFieldGain(float gain)
{
gain = qBound(0., gain, 1.);
if (gain == d->nearFieldGain)
return;
d->nearFieldGain = gain;
- auto *ep = QSpatialAudioEnginePrivate::get(d->engine);
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
- ep->api->SetSoundObjectNearFieldEffectGain(d->sourceId, d->nearFieldGain/9.);
+ ep->resonanceAudio->api->SetSoundObjectNearFieldEffectGain(d->sourceId, d->nearFieldGain*9.f);
emit nearFieldGainChanged();
}
-float QSpatialAudioSoundSource::nearFieldGain() const
+float QSpatialSound::nearFieldGain() const
{
return d->nearFieldGain;
}
/*!
- \property QSpatialAudioSoundSource::source
+ \property QSpatialSound::source
The source file for the sound to be played.
*/
-void QSpatialAudioSoundSource::setSource(const QUrl &url)
+void QSpatialSound::setSource(const QUrl &url)
{
if (d->url == url)
return;
@@ -509,25 +476,33 @@ void QSpatialAudioSoundSource::setSource(const QUrl &url)
emit sourceChanged();
}
-QUrl QSpatialAudioSoundSource::source() const
+QUrl QSpatialSound::source() const
{
return d->url;
}
/*!
- \property QSpatialAudioSoundSource::loops
+ \enum QSpatialSound::Loops
+
+ Lets you control the sound playback loop using the following values:
+
+ \value Infinite Playback infinitely
+ \value Once Playback once
+*/
+/*!
+ \property QSpatialSound::loops
Determines how many times the sound is played before the player stops.
- Set to QSpatialAudioSoundSource::Infinite to play the current sound in a loop forever.
+ Set to QSpatialSound::Infinite to play the current sound in a loop forever.
The default value is \c 1.
*/
-int QSpatialAudioSoundSource::loops() const
+int QSpatialSound::loops() const
{
return d->m_loops.loadRelaxed();
}
-void QSpatialAudioSoundSource::setLoops(int loops)
+void QSpatialSound::setLoops(int loops)
{
int oldLoops = d->m_loops.fetchAndStoreRelaxed(loops);
if (oldLoops != loops)
@@ -535,19 +510,19 @@ void QSpatialAudioSoundSource::setLoops(int loops)
}
/*!
- \property QSpatialAudioSoundSource::autoPlay
+ \property QSpatialSound::autoPlay
Determines whether the sound should automatically start playing when a source
gets specified.
The default value is \c true.
*/
-bool QSpatialAudioSoundSource::autoPlay() const
+bool QSpatialSound::autoPlay() const
{
return d->m_autoPlay.loadRelaxed();
}
-void QSpatialAudioSoundSource::setAutoPlay(bool autoPlay)
+void QSpatialSound::setAutoPlay(bool autoPlay)
{
bool old = d->m_autoPlay.fetchAndStoreRelaxed(autoPlay);
if (old != autoPlay)
@@ -557,7 +532,7 @@ void QSpatialAudioSoundSource::setAutoPlay(bool autoPlay)
/*!
Starts playing back the sound. Does nothing if the sound is already playing.
*/
-void QSpatialAudioSoundSource::play()
+void QSpatialSound::play()
{
d->play();
}
@@ -565,7 +540,7 @@ void QSpatialAudioSoundSource::play()
/*!
Pauses sound playback. Calling play() will continue playback.
*/
-void QSpatialAudioSoundSource::pause()
+void QSpatialSound::pause()
{
d->pause();
}
@@ -574,7 +549,7 @@ void QSpatialAudioSoundSource::pause()
Stops sound playback and resets the current position and current loop count to 0.
Calling play() will start playback at the beginning of the sound file.
*/
-void QSpatialAudioSoundSource::stop()
+void QSpatialSound::stop()
{
d->stop();
}
@@ -582,24 +557,27 @@ void QSpatialAudioSoundSource::stop()
/*!
\internal
*/
-void QSpatialAudioSoundSource::setEngine(QSpatialAudioEngine *engine)
+void QSpatialSound::setEngine(QAudioEngine *engine)
{
if (d->engine == engine)
return;
- auto *ep = QSpatialAudioEnginePrivate::get(engine);
+ // Remove self from old engine (if necessary)
+ auto *ep = QAudioEnginePrivate::get(d->engine);
if (ep)
ep->removeSpatialSound(this);
+
d->engine = engine;
- ep = QSpatialAudioEnginePrivate::get(engine);
+ // Add self to new engine if necessary
+ ep = QAudioEnginePrivate::get(d->engine);
if (ep) {
ep->addSpatialSound(this);
- ep->api->SetSourcePosition(d->sourceId, d->pos.x(), d->pos.y(), d->pos.z());
- ep->api->SetSourceRotation(d->sourceId, d->rotation.x(), d->rotation.y(), d->rotation.z(), d->rotation.scalar());
- ep->api->SetSourceVolume(d->sourceId, d->volume);
- ep->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
- ep->api->SetSoundObjectNearFieldEffectGain(d->sourceId, d->nearFieldGain);
+ ep->resonanceAudio->api->SetSourcePosition(d->sourceId, d->pos.x(), d->pos.y(), d->pos.z());
+ ep->resonanceAudio->api->SetSourceRotation(d->sourceId, d->rotation.x(), d->rotation.y(), d->rotation.z(), d->rotation.scalar());
+ ep->resonanceAudio->api->SetSourceVolume(d->sourceId, d->volume);
+ ep->resonanceAudio->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
+ ep->resonanceAudio->api->SetSoundObjectNearFieldEffectGain(d->sourceId, d->nearFieldGain);
d->updateDistanceModel();
}
}
@@ -607,11 +585,11 @@ void QSpatialAudioSoundSource::setEngine(QSpatialAudioEngine *engine)
/*!
Returns the engine associated with this listener.
*/
-QSpatialAudioEngine *QSpatialAudioSoundSource::engine() const
+QAudioEngine *QSpatialSound::engine() const
{
return d->engine;
}
QT_END_NAMESPACE
-#include "moc_qspatialaudiosoundsource.cpp"
+#include "moc_qspatialsound.cpp"
diff --git a/src/multimedia/spatial/qspatialaudiosoundsource.h b/src/spatialaudio/qspatialsound.h
index cd0d8fe75..d171e0526 100644
--- a/src/multimedia/spatial/qspatialaudiosoundsource.h
+++ b/src/spatialaudio/qspatialsound.h
@@ -1,54 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QSPATIALAUDIOSOURCE_H
-#define QSPATIALAUDIOSOURCE_H
-
-#include <QtMultimedia/qtmultimediaglobal.h>
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QSPATIALSOUND_H
+#define QSPATIALSOUND_H
+
+#include <QtSpatialAudio/qtspatialaudioglobal.h>
#include <QtCore/QObject>
#include <QtGui/qvector3d.h>
#include <QtGui/qquaternion.h>
QT_BEGIN_NAMESPACE
-class QSpatialAudioEngine;
-class QSpatialAudioSound;
+class QAudioEngine;
+class QAmbientSoundPrivate;
-class QSpatialAudioSoundSourcePrivate;
-class Q_MULTIMEDIA_EXPORT QSpatialAudioSoundSource : public QObject
+class QSpatialSoundPrivate;
+class Q_SPATIALAUDIO_EXPORT QSpatialSound : public QObject
{
Q_OBJECT
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
@@ -67,8 +33,8 @@ class Q_MULTIMEDIA_EXPORT QSpatialAudioSoundSource : public QObject
Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged)
public:
- explicit QSpatialAudioSoundSource(QSpatialAudioEngine *engine);
- ~QSpatialAudioSoundSource();
+ explicit QSpatialSound(QAudioEngine *engine);
+ ~QSpatialSound();
void setSource(const QUrl &url);
QUrl source() const;
@@ -95,10 +61,10 @@ public:
void setVolume(float volume);
float volume() const;
- enum DistanceModel {
- DistanceModel_Logarithmic,
- DistanceModel_Linear,
- DistanceModel_ManualAttenutation
+ enum class DistanceModel {
+ Logarithmic,
+ Linear,
+ ManualAttenuation
};
Q_ENUM(DistanceModel);
@@ -126,7 +92,7 @@ public:
void setNearFieldGain(float gain);
float nearFieldGain() const;
- QSpatialAudioEngine *engine() const;
+ QAudioEngine *engine() const;
Q_SIGNALS:
void sourceChanged();
@@ -150,10 +116,10 @@ public Q_SLOTS:
void stop();
private:
- void setEngine(QSpatialAudioEngine *engine);
- friend class QSpatialAudioSound;
- friend class QSpatialAudioSoundSourcePrivate;
- QSpatialAudioSoundSourcePrivate *d = nullptr;
+ void setEngine(QAudioEngine *engine);
+ friend class QAmbientSoundPrivate;
+ friend class QSpatialSoundPrivate;
+ QSpatialSoundPrivate *d = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/spatialaudio/qspatialsound_p.h b/src/spatialaudio/qspatialsound_p.h
new file mode 100644
index 000000000..6e1b5d422
--- /dev/null
+++ b/src/spatialaudio/qspatialsound_p.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QSPATIALAUDIOSOUNDSOURCE_P_H
+#define QSPATIALAUDIOSOUNDSOURCE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qspatialsound.h>
+#include <qambientsound_p.h>
+#include <qaudioengine_p.h>
+#include <qurl.h>
+#include <qvector3d.h>
+#include <qquaternion.h>
+#include <qaudiobuffer.h>
+#include <qaudiodevice.h>
+#include <qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioDecoder;
+class QAudioEnginePrivate;
+
+class QSpatialSoundPrivate : public QAmbientSoundPrivate
+{
+public:
+ QSpatialSoundPrivate(QObject *parent)
+ : QAmbientSoundPrivate(parent, 1)
+ {}
+
+ static QSpatialSoundPrivate *get(QSpatialSound *soundSource)
+ { return soundSource ? soundSource->d : nullptr; }
+
+ QVector3D pos;
+ QQuaternion rotation;
+ QSpatialSound::DistanceModel distanceModel = QSpatialSound::DistanceModel::Logarithmic;
+ float size = .1f;
+ float distanceCutoff = 50.f;
+ float manualAttenuation = 0.f;
+ float occlusionIntensity = 0.f;
+ float directivity = 0.f;
+ float directivityOrder = 1.f;
+ float nearFieldGain = 0.f;
+ float wallDampening = 1.f;
+ float wallOcclusion = 0.f;
+
+ void updateDistanceModel();
+ void updateRoomEffects();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudio/qtspatialaudioglobal.h b/src/spatialaudio/qtspatialaudioglobal.h
new file mode 100644
index 000000000..6e5b1ec0c
--- /dev/null
+++ b/src/spatialaudio/qtspatialaudioglobal.h
@@ -0,0 +1,10 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QTSPATIALAUDIOGLOBAL_H
+#define QTSPATIALAUDIOGLOBAL_H
+
+#include <QtMultimedia/qtmultimediaglobal.h>
+#include <QtSpatialAudio/qtspatialaudioexports.h>
+
+#endif // QTMULTIMEDIAGLOBAL_H
diff --git a/src/spatialaudio/qtspatialaudioglobal_p.h b/src/spatialaudio/qtspatialaudioglobal_p.h
new file mode 100644
index 000000000..a504da6cd
--- /dev/null
+++ b/src/spatialaudio/qtspatialaudioglobal_p.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QTSPATIALAUDIOGLOBAL_P_H
+#define QTSPATIALAUDIOGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtMultimedia/private/qtmultimediaglobal_p.h>
+#include <QtSpatialAudio/qtspatialaudioglobal.h>
+
+#endif // QTQMLGLOBAL_P_H
diff --git a/src/multimediaquick3d/CMakeLists.txt b/src/spatialaudioquick3d/CMakeLists.txt
index af4639df8..64b154158 100644
--- a/src/multimediaquick3d/CMakeLists.txt
+++ b/src/spatialaudioquick3d/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#####################################################################
## Quick3D.Sound Module:
#####################################################################
@@ -5,7 +8,7 @@
qt_internal_add_qml_module(Quick3DSpatialAudioPrivate
URI "QtQuick3D.SpatialAudio"
VERSION "${PROJECT_VERSION}"
- CLASS_NAME QQuick3DSpatialAudioModule
+ CLASS_NAME QQuick3DAudioModule
PLUGIN_TARGET quick3dspatialaudio
NO_GENERATE_PLUGIN_SOURCE
NO_PLUGIN_OPTIONAL
@@ -13,18 +16,18 @@ qt_internal_add_qml_module(Quick3DSpatialAudioPrivate
CONFIG_MODULE_NAME quick3dspatialaudio
INTERNAL_MODULE
SOURCES
- qquick3dspatialaudiolistener.cpp qquick3dspatialaudiolistener_p.h
- qquick3dspatialaudioroom.cpp qquick3dspatialaudioroom_p.h
- qquick3dspatialaudiosoundsource.cpp qquick3dspatialaudiosoundsource_p.h
- qquick3dspatialaudiostereosource.cpp qquick3dspatialaudiostereosource_p.h
- qquick3dspatialaudioengine.cpp qquick3dspatialaudioengine_p.h
- qtquick3dsoundglobal_p.h
- qtquick3dsoundtypes_p.h
+ qquick3daudiolistener.cpp qquick3daudiolistener_p.h
+ qquick3daudioroom.cpp qquick3daudioroom_p.h
+ qquick3dspatialsound.cpp qquick3dspatialsound_p.h
+ qquick3dambientsound.cpp qquick3dambientsound_p.h
+ qquick3daudioengine.cpp qquick3daudioengine_p.h
+ qtquick3daudioglobal_p.h
+ qtquick3daudiotypes_p.h
QML_FILES
PUBLIC_LIBRARIES
Qt::Quick3DPrivate
- Qt::Multimedia
+ Qt::SpatialAudio
GENERATE_CPP_EXPORTS
)
-target_sources(quick3dspatialaudio PRIVATE quick3dspatialaudio_plugin.cpp)
+target_sources(quick3dspatialaudio PRIVATE qquick3dspatialaudio_plugin.cpp)
diff --git a/src/spatialaudioquick3d/qquick3dambientsound.cpp b/src/spatialaudioquick3d/qquick3dambientsound.cpp
new file mode 100644
index 000000000..3b8ca4918
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3dambientsound.cpp
@@ -0,0 +1,146 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include "qquick3dambientsound_p.h"
+#include "qquick3daudioengine_p.h"
+#include "qambientsound.h"
+#include <QAudioFormat>
+#include <qdir.h>
+#include <QQmlContext>
+#include <QQmlFile>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype AmbientSound
+ \inqmlmodule QtQuick3D.SpatialAudio
+ \ingroup quick3d_spatialaudio
+ \ingroup multimedia_audio_qml
+
+ \brief A stereo overlay sound.
+
+ A AmbientSound represents a position and orientation independent sound.
+ It's commonly used for background sounds (e.g. music) that is supposed to be independent
+ of the listeners position and orientation.
+ */
+
+QQuick3DAmbientSound::QQuick3DAmbientSound()
+{
+ m_sound = new QAmbientSound(QQuick3DAudioEngine::getEngine());
+
+ connect(m_sound, &QAmbientSound::sourceChanged, this, &QQuick3DAmbientSound::sourceChanged);
+ connect(m_sound, &QAmbientSound::volumeChanged, this, &QQuick3DAmbientSound::volumeChanged);
+ connect(m_sound, &QAmbientSound::loopsChanged, this, &QQuick3DAmbientSound::loopsChanged);
+ connect(m_sound, &QAmbientSound::autoPlayChanged, this, &QQuick3DAmbientSound::autoPlayChanged);
+}
+
+QQuick3DAmbientSound::~QQuick3DAmbientSound()
+{
+ delete m_sound;
+}
+
+/*!
+ \qmlproperty url AmbientSound::source
+
+ The source file for the sound to be played.
+ */
+QUrl QQuick3DAmbientSound::source() const
+{
+ return m_sound->source();
+}
+
+void QQuick3DAmbientSound::setSource(QUrl source)
+{
+ const QQmlContext *context = qmlContext(this);
+ QUrl url;
+ if (context) {
+ url = context->resolvedUrl(source);
+ } else {
+ url = QUrl::fromLocalFile(QDir::currentPath() + u"/");
+ url = url.resolved(source);
+ }
+ m_sound->setSource(url);
+}
+
+/*!
+ \qmlproperty float AmbientSound::volume
+
+ Defines an overall volume for this sound source.
+ */
+void QQuick3DAmbientSound::setVolume(float volume)
+{
+ m_sound->setVolume(volume);
+}
+
+float QQuick3DAmbientSound::volume() const
+{
+ return m_sound->volume();
+}
+
+/*!
+ \qmlproperty int AmbientSound::loops
+
+ Determines how often the sound is played before the player stops.
+ Set to QAmbienSound::Infinite to loop the current sound forever.
+
+ The default value is \c 1.
+ */
+int QQuick3DAmbientSound::loops() const
+{
+ return m_sound->loops();
+}
+
+void QQuick3DAmbientSound::setLoops(int loops)
+{
+ m_sound->setLoops(loops);
+}
+
+/*!
+ \qmlproperty bool AmbientSound::autoPlay
+
+ Determines whether the sound should automatically start playing when a source
+ gets specified.
+
+ The default value is \c true.
+ */
+bool QQuick3DAmbientSound::autoPlay() const
+{
+ return m_sound->autoPlay();
+}
+
+void QQuick3DAmbientSound::setAutoPlay(bool autoPlay)
+{
+ m_sound->setAutoPlay(autoPlay);
+}
+
+/*!
+ \qmlmethod AmbientSound::play()
+
+ Starts playing back the sound. Does nothing if the sound is already playing.
+ */
+void QQuick3DAmbientSound::play()
+{
+ m_sound->play();
+}
+
+/*!
+ \qmlmethod AmbientSound::pause()
+
+ Pauses sound playback at the current position. Calling play() will continue playback.
+ */
+void QQuick3DAmbientSound::pause()
+{
+ m_sound->pause();
+}
+
+/*!
+ \qmlmethod AmbientSound::stop()
+
+ Stops sound playback and resets the current position and loop count to 0. Calling play() will
+ begin playback at the beginning of the sound file.
+ */
+void QQuick3DAmbientSound::stop()
+{
+ m_sound->stop();
+}
+
+QT_END_NAMESPACE
diff --git a/src/spatialaudioquick3d/qquick3dambientsound_p.h b/src/spatialaudioquick3d/qquick3dambientsound_p.h
new file mode 100644
index 000000000..6185179ce
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3dambientsound_p.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QQUICK3DAMBIENTSOUND_H
+#define QQUICK3DAMBIENTSOUND_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qquick3dnode_p.h>
+#include <QUrl>
+#include <qvector3d.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAmbientSound;
+
+class QQuick3DAmbientSound : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)
+ Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged)
+ Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged)
+ QML_NAMED_ELEMENT(AmbientSound)
+
+public:
+ QQuick3DAmbientSound();
+ ~QQuick3DAmbientSound();
+
+ void setSource(QUrl source);
+ QUrl source() const;
+
+ void setVolume(float volume);
+ float volume() const;
+
+ enum Loops
+ {
+ Infinite = -1,
+ Once = 1
+ };
+ Q_ENUM(Loops)
+
+ int loops() const;
+ void setLoops(int loops);
+
+ bool autoPlay() const;
+ void setAutoPlay(bool autoPlay);
+
+public Q_SLOTS:
+ void play();
+ void pause();
+ void stop();
+
+Q_SIGNALS:
+ void sourceChanged();
+ void volumeChanged();
+ void loopsChanged();
+ void autoPlayChanged();
+
+private:
+ QAmbientSound *m_sound = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudioquick3d/qquick3daudio-qml-types.qdoc b/src/spatialaudioquick3d/qquick3daudio-qml-types.qdoc
new file mode 100644
index 000000000..0ccb69bb9
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3daudio-qml-types.qdoc
@@ -0,0 +1,32 @@
+// Copyright (C) 2015 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\qmlmodule QtQuick3D.Audio
+\title QtQuick3D.SpatialAudio QML Types
+\ingroup qmlmodules
+\brief Provides QML types for spatial audio in Qt Quick 3D.
+
+The QML types Spatial Audio support the basic use cases such as:
+\list
+ \li have a person listening to sounds in 3D space,
+ \li place sound sources in 3D space,
+ \li support room acoustics with direct reflections and reverb,
+ \li support stereo overlay sources (e.g. for sound tracks).
+ \li support output to Headphones using binaural (virtual 3D) rendering of the sound field
+ \li support output to stereo or surround speaker configurations
+\endlist
+
+\section1 QML Types
+
+Qt Quick3D Spatial Audio QML types can be imported into your application using the
+following import statement in your .qml file:
+
+\qml \QtMinorVersion
+import QtQuick3D.Audio
+\endqml
+
+\generatelist qmltypesbymodule QtQuick3D.Audio
+
+\noautolist
+*/
diff --git a/src/spatialaudioquick3d/qquick3daudioengine.cpp b/src/spatialaudioquick3d/qquick3daudioengine.cpp
new file mode 100644
index 000000000..1288effb3
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3daudioengine.cpp
@@ -0,0 +1,128 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include <qquick3daudioengine_p.h>
+#include <qaudiodevice.h>
+
+QT_BEGIN_NAMESPACE
+
+static QAudioEngine *globalEngine = nullptr;
+
+/*!
+ \qmltype AudioEngine
+ \inqmlmodule QtQuick3D.SpatialAudio
+ \ingroup quick3d_spatialaudio
+ \ingroup multimedia_audio_qml
+
+ \brief AudioEngine manages sound objects inside a 3D scene.
+
+ AudioEngine manages sound objects inside a 3D scene. You can add
+ SpatialSound objects to the scene to define sounds that happen
+ at a specified location in 3D space. AmbientSound allows you to add
+ a stereo overlay (for example voice over or a sound track).
+
+ You can use AudioListener to define the position of the person listening
+ to the sound field relative to the sound sources. Sound sources will be less audible
+ if the listener is further away from source. They will also get mapped to the corresponding
+ loudspeakers depending on the direction between listener and source. In many cases, the
+ AudioListener object can simply be instantiated as a child object of the QtQuick3D.Camera
+ object.
+
+ Create AudioRoom objcects to simulate the sound (reflections and reverb) of a room with
+ certain dimensions and different types of walls.
+
+ AudioEngine does offer a mode where Qt is using simulating the effects of the ear
+ using head related impulse reponse functions (see also https://en.wikipedia.org/wiki/Sound_localization)
+ to localize the sound in 3D space when using headphones and create a spatial audio effect through
+ headphones.
+
+ As the rest of Qt Quick 3D, the audio engine uses a coordinate system that is in centimeters by default.
+ The axes are defined so that positive x points to the right, positive y points up and positive z points
+ backwards.
+*/
+
+
+QQuick3DAudioEngine::QQuick3DAudioEngine()
+{
+ auto *e = getEngine();
+ connect(e, &QAudioEngine::outputModeChanged, this, &QQuick3DAudioEngine::outputModeChanged);
+ connect(e, &QAudioEngine::outputDeviceChanged, this, &QQuick3DAudioEngine::outputDeviceChanged);
+ connect(e, &QAudioEngine::masterVolumeChanged, this, &QQuick3DAudioEngine::masterVolumeChanged);
+}
+
+QQuick3DAudioEngine::~QQuick3DAudioEngine()
+{
+}
+
+/*!
+ \qmlproperty enumeration AudioEngine::outputMode
+
+ Sets or retrieves the current output mode of the engine.
+
+ \table
+ \header \li Property value
+ \li Description
+ \row \li Surround
+ \li Map the sounds to the loudspeaker configuration of the output device.
+ This is normally a stereo or surround speaker setup.
+ \row \li Stereo
+ \li Map the sounds to the stereo loudspeaker configuration of the output device.
+ This will ignore any additional speakers and only use the left and right channels
+ to create a stero rendering of the sound field.
+ \row \li Headphone
+ \li Use Headphone spatialization to create a 3D audio effect when listening
+ to the sound field through headphones.
+ \endtable
+ */
+
+void QQuick3DAudioEngine::setOutputMode(OutputMode mode)
+{
+ globalEngine->setOutputMode(QAudioEngine::OutputMode(mode));
+}
+
+QQuick3DAudioEngine::OutputMode QQuick3DAudioEngine::outputMode() const
+{
+ return OutputMode(globalEngine->outputMode());
+}
+
+/*!
+ \qmlproperty QtMultimedia.AudioDevice AudioEngine::outputDevice
+
+ Sets or returns the device that is being used for outputting the sound field.
+ */
+void QQuick3DAudioEngine::setOutputDevice(const QAudioDevice &device)
+{
+ globalEngine->setOutputDevice(device);
+}
+
+QAudioDevice QQuick3DAudioEngine::outputDevice() const
+{
+ return globalEngine->outputDevice();
+}
+
+/*!
+ \qmlproperty float AudioEngine::masterVolume
+
+ Sets or returns overall volume being used to render the sound field.
+ */
+void QQuick3DAudioEngine::setMasterVolume(float volume)
+{
+ globalEngine->setMasterVolume(volume);
+}
+
+float QQuick3DAudioEngine::masterVolume() const
+{
+ return globalEngine->masterVolume();
+}
+
+QAudioEngine *QQuick3DAudioEngine::getEngine()
+{
+ if (!globalEngine) {
+ globalEngine = new QAudioEngine;
+ globalEngine->start();
+ }
+ return globalEngine;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquick3daudioengine_p.cpp"
diff --git a/src/spatialaudioquick3d/qquick3daudioengine_p.h b/src/spatialaudioquick3d/qquick3daudioengine_p.h
new file mode 100644
index 000000000..9cde33e97
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3daudioengine_p.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QQUICK3DAUDIOENGINE_H
+#define QQUICK3DAUDIOENGINE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qquick3dnode_p.h>
+#include <QtGui/qvector3d.h>
+#include <qaudioengine.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuick3DSpatialSound;
+
+class QQuick3DAudioEngine : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(AudioEngine)
+ Q_PROPERTY(OutputMode outputMode READ outputMode WRITE setOutputMode NOTIFY outputModeChanged)
+ Q_PROPERTY(QAudioDevice outputDevice READ outputDevice WRITE setOutputDevice NOTIFY outputDeviceChanged)
+ Q_PROPERTY(float masterVolume READ masterVolume WRITE setMasterVolume NOTIFY masterVolumeChanged)
+
+public:
+ // Keep in sync with QAudioEngine::OutputMode
+ enum OutputMode {
+ Surround,
+ Stereo,
+ Headphone
+ };
+ Q_ENUM(OutputMode)
+
+ QQuick3DAudioEngine();
+ ~QQuick3DAudioEngine();
+
+ void setOutputMode(OutputMode mode);
+ OutputMode outputMode() const;
+
+ void setOutputDevice(const QAudioDevice &device);
+ QAudioDevice outputDevice() const;
+
+ void setMasterVolume(float volume);
+ float masterVolume() const;
+
+ static QAudioEngine *getEngine();
+
+Q_SIGNALS:
+ void outputModeChanged();
+ void outputDeviceChanged();
+ void masterVolumeChanged();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudioquick3d/qquick3daudiolistener.cpp b/src/spatialaudioquick3d/qquick3daudiolistener.cpp
new file mode 100644
index 000000000..b8445a799
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3daudiolistener.cpp
@@ -0,0 +1,49 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include <qquick3daudiolistener_p.h>
+#include <qquick3dspatialsound_p.h>
+#include <qquick3daudioengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype AudioListener
+ \inqmlmodule QtQuick3D.SpatialAudio
+ \ingroup quick3d_spatialaudio
+ \ingroup multimedia_audio_qml
+
+ \brief defines the position and orientation of the person listening to a sound field
+ defined by a AudioEngine.
+
+ A AudioEngine can have exactly one listener, that defines the position and orientation
+ of the person listening to the sounds defined by the objects placed within the audio engine.
+
+ In most cases, the AudioListener should simply be a child of the Camera element in QtQuick3D.
+ This will ensure that the sound experience is aligned with the visual rendering of the scene.
+ */
+
+QQuick3DAudioListener::QQuick3DAudioListener()
+{
+ m_listener = new QAudioListener(QQuick3DAudioEngine::getEngine());
+ connect(this, &QQuick3DNode::scenePositionChanged, this, &QQuick3DAudioListener::updatePosition);
+ connect(this, &QQuick3DNode::sceneRotationChanged, this, &QQuick3DAudioListener::updateRotation);
+ updatePosition();
+ updateRotation();
+}
+
+QQuick3DAudioListener::~QQuick3DAudioListener()
+{
+ delete m_listener;
+}
+
+void QQuick3DAudioListener::updatePosition()
+{
+ m_listener->setPosition(scenePosition());
+}
+
+void QQuick3DAudioListener::updateRotation()
+{
+ m_listener->setRotation(sceneRotation());
+}
+
+QT_END_NAMESPACE
diff --git a/src/spatialaudioquick3d/qquick3daudiolistener_p.h b/src/spatialaudioquick3d/qquick3daudiolistener_p.h
new file mode 100644
index 000000000..1a24d8f0d
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3daudiolistener_p.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QQUICK3DLISTENER_H
+#define QQUICK3DLISTENER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qquick3dnode_p.h>
+#include <QtGui/qvector3d.h>
+
+#include <qaudiolistener.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuick3DAudioListener : public QQuick3DNode
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(AudioListener)
+
+public:
+ QQuick3DAudioListener();
+ ~QQuick3DAudioListener();
+
+ QAudioListener *listener() { return m_listener; }
+protected:
+ QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *) override { return nullptr; }
+
+protected Q_SLOTS:
+ void updatePosition();
+ void updateRotation();
+
+private:
+ QAudioListener *m_listener;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudioquick3d/qquick3daudioroom.cpp b/src/spatialaudioquick3d/qquick3daudioroom.cpp
new file mode 100644
index 000000000..b79a6f609
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3daudioroom.cpp
@@ -0,0 +1,270 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include <qquick3daudioroom_p.h>
+#include <qquick3daudioengine_p.h>
+#include <qaudioroom.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype AudioRoom
+ \inqmlmodule QtQuick3D.SpatialAudio
+ \ingroup quick3d_spatialaudio
+ \ingroup multimedia_audio_qml
+
+ Defines a room for the spatial audio engine.
+
+ If the listener is inside a room, first order sound reflections and reverb
+ matching the rooms properties will get applied to the sound field.
+
+ A room is always square and defined by its center position, its orientation and dimensions.
+ Each of the 6 walls of the room can be made of different materials that will contribute
+ to the computed reflections and reverb that the listener will experience while being inside
+ the room.
+
+ If multiple rooms cover the same position, the engine will use the room with the smallest
+ volume.
+ */
+
+QQuick3DAudioRoom::QQuick3DAudioRoom()
+{
+ m_room = new QAudioRoom(QQuick3DAudioEngine::getEngine());
+
+ connect(this, &QQuick3DNode::scenePositionChanged, this, &QQuick3DAudioRoom::updatePosition);
+ connect(this, &QQuick3DNode::sceneRotationChanged, this, &QQuick3DAudioRoom::updateRotation);
+ connect(m_room, &QAudioRoom::dimensionsChanged, this, &QQuick3DAudioRoom::dimensionsChanged);
+ connect(m_room, &QAudioRoom::rotationChanged, this, &QQuick3DAudioRoom::rotationChanged);
+ connect(m_room, &QAudioRoom::wallsChanged, this, &QQuick3DAudioRoom::wallsChanged);
+ connect(m_room, &QAudioRoom::reflectionGainChanged, this, &QQuick3DAudioRoom::reflectionGainChanged);
+ connect(m_room, &QAudioRoom::reverbGainChanged, this, &QQuick3DAudioRoom::reverbGainChanged);
+ connect(m_room, &QAudioRoom::reverbTimeChanged, this, &QQuick3DAudioRoom::reverbTimeChanged);
+ connect(m_room, &QAudioRoom::reverbBrightnessChanged, this, &QQuick3DAudioRoom::reverbBrightnessChanged);
+}
+
+QQuick3DAudioRoom::~QQuick3DAudioRoom()
+{
+ delete m_room;
+}
+
+/*!
+ \qmlproperty vector3D AudioRoom::dimensions
+
+ Defines the dimensions of the room in 3D space. Units are in centimeters
+ by default.
+
+ \sa QtQuick3D::Node::position
+ */
+void QQuick3DAudioRoom::setDimensions(QVector3D dim)
+{
+ m_room->setDimensions(dim);
+}
+
+QVector3D QQuick3DAudioRoom::dimensions() const
+{
+ return m_room->dimensions();
+}
+
+/*!
+ \qmlproperty AudioRoom::Material AudioRoom::leftMaterial
+ \qmlproperty AudioRoom::Material AudioRoom::rightMaterial
+ \qmlproperty AudioRoom::Material AudioRoom::frontMaterial
+ \qmlproperty AudioRoom::Material AudioRoom::backMaterial
+ \qmlproperty AudioRoom::Material AudioRoom::floorMaterial
+ \qmlproperty AudioRoom::Material AudioRoom::ceilingMaterial
+
+ Sets the material to use for the different sides of the room. Properties correlate to
+ coordinates as follows:
+
+ \table
+ \header
+ \li Property
+ \li Coordinate
+ \row \li left \li Negative x
+ \row \li right \li Positive x
+ \row \li back \li Negative z
+ \row \li front \li Positive z
+ \row \li floor \li Negative y
+ \row \li ceiling \li Positive y
+ \endtable
+
+ Valid values for the material are:
+
+ \table
+ \header
+ \li Property value
+ \li Description
+ \row \li Transparent \li The side of the room is open and won't contribute to reflections or reverb.
+ \row \li AcousticCeilingTiles \li Acoustic tiles that suppress most reflections and reverb.
+ \row \li BrickBare \li A bare brick wall.
+ \row \li BrickPainted \li A painted brick wall.
+ \row \li ConcreteBlockCoarse \li A raw concrete wall
+ \row \li ConcreteBlockPainted \li A painted concrete wall
+ \row \li CurtainHeavy \li A heavy curtain. Will mostly reflect low frequencies
+ \row \li FiberGlassInsulation \li Fiber glass insulation. Only reflects very low frequencies
+ \row \li GlassThin \li A thin glass wall
+ \row \li GlassThick \li A thick glass wall
+ \row \li Grass \li Grass
+ \row \li LinoleumOnConcrete \li A Linoleum floor
+ \row \li Marble \li A marble floor
+ \row \li Metal \li Metal
+ \row \li ParquetOnConcrete \li Parquet wooden floor on concrete
+ \row \li PlasterRough \li Rough plaster
+ \row \li PlasterSmooth \li Smooth plaster
+ \row \li PlywoodPanel \li Plywodden panel
+ \row \li PolishedConcreteOrTile \li Polished concrete or tiles
+ \row \li Sheetrock \li Rock
+ \row \li WaterOrIceSurface \li Water or ice
+ \row \li WoodCeiling \li A wooden ceiling
+ \row \li WoodPanel \li Wooden panel
+ \row \li Uniform \li Artificial material giving uniform reflections on all frequencies
+ \endtable
+ */
+void QQuick3DAudioRoom::setLeftMaterial(Material material)
+{
+ m_room->setWallMaterial(QAudioRoom::LeftWall, QAudioRoom::Material(material));
+}
+
+QQuick3DAudioRoom::Material QQuick3DAudioRoom::leftMaterial() const
+{
+ return Material(m_room->wallMaterial(QAudioRoom::LeftWall));
+}
+
+void QQuick3DAudioRoom::setRightMaterial(Material material)
+{
+ m_room->setWallMaterial(QAudioRoom::RightWall, QAudioRoom::Material(material));
+}
+
+QQuick3DAudioRoom::Material QQuick3DAudioRoom::rightMaterial() const
+{
+ return Material(m_room->wallMaterial(QAudioRoom::RightWall));
+}
+
+void QQuick3DAudioRoom::setFrontMaterial(Material material)
+{
+ m_room->setWallMaterial(QAudioRoom::FrontWall, QAudioRoom::Material(material));
+}
+
+QQuick3DAudioRoom::Material QQuick3DAudioRoom::frontMaterial() const
+{
+ return Material(m_room->wallMaterial(QAudioRoom::FrontWall));
+}
+
+void QQuick3DAudioRoom::setBackMaterial(Material material)
+{
+ m_room->setWallMaterial(QAudioRoom::BackWall, QAudioRoom::Material(material));
+}
+
+QQuick3DAudioRoom::Material QQuick3DAudioRoom::backMaterial() const
+{
+ return Material(m_room->wallMaterial(QAudioRoom::BackWall));
+}
+
+void QQuick3DAudioRoom::setFloorMaterial(Material material)
+{
+ m_room->setWallMaterial(QAudioRoom::Floor, QAudioRoom::Material(material));
+}
+
+QQuick3DAudioRoom::Material QQuick3DAudioRoom::floorMaterial() const
+{
+ return Material(m_room->wallMaterial(QAudioRoom::Floor));
+}
+
+void QQuick3DAudioRoom::setCeilingMaterial(Material material)
+{
+ m_room->setWallMaterial(QAudioRoom::Ceiling, QAudioRoom::Material(material));
+}
+
+QQuick3DAudioRoom::Material QQuick3DAudioRoom::ceilingMaterial() const
+{
+ return Material(m_room->wallMaterial(QAudioRoom::Ceiling));
+}
+
+/*!
+ \qmlproperty float AudioRoom::reflectionGain
+
+ A gain factor for reflections generated in this room. A value
+ from 0 to 1 will dampen reflections, while a value larger than 1
+ will apply a gain to reflections, making them louder.
+
+ The default is 1, a factor of 0 disables reflections. Negative
+ values are mapped to 0.
+ */
+void QQuick3DAudioRoom::setReflectionGain(float factor)
+{
+ m_room->setReflectionGain(factor);
+}
+
+float QQuick3DAudioRoom::reflectionGain() const
+{
+ return m_room->reflectionGain();
+}
+
+/*!
+ \qmlproperty float AudioRoom::reverbGain
+
+ A gain factor for reverb generated in this room. A value
+ from 0 to 1 will dampen reverb, while a value larger than 1
+ will apply a gain to the reverb, making it louder.
+
+ The default is 1, a factor of 0 disables reverb. Negative
+ values are mapped to 0.
+ */
+void QQuick3DAudioRoom::setReverbGain(float factor)
+{
+ m_room->setReverbGain(factor);
+}
+
+float QQuick3DAudioRoom::reverbGain() const
+{
+ return m_room->reverbGain();
+}
+
+/*!
+ \qmlproperty float AudioRoom::reverbTime
+
+ A factor to be applies to all reverb timings generated for this room.
+ Larger values will lead to longer reverb timings, making the room sound
+ larger.
+
+ The default is 1. Negative values are mapped to 0.
+ */
+void QQuick3DAudioRoom::setReverbTime(float factor)
+{
+ m_room->setReverbTime(factor);
+}
+
+float QQuick3DAudioRoom::reverbTime() const
+{
+ return m_room->reverbTime();
+}
+
+/*!
+ \qmlproperty float AudioRoom::reverbBrightness
+
+ A brightness factor to be applied to the generated reverb.
+ A positive value will increase reverb for higher frequencies and
+ dampen lower frequencies, a negative value does the reverse.
+
+ The default is 0.
+ */
+void QQuick3DAudioRoom::setReverbBrightness(float factor)
+{
+ m_room->setReverbBrightness(factor);
+}
+
+float QQuick3DAudioRoom::reverbBrightness() const
+{
+ return m_room->reverbBrightness();
+}
+
+void QQuick3DAudioRoom::updatePosition()
+{
+ m_room->setPosition(scenePosition());
+}
+
+void QQuick3DAudioRoom::updateRotation()
+{
+ m_room->setRotation(sceneRotation());
+}
+
+QT_END_NAMESPACE
diff --git a/src/spatialaudioquick3d/qquick3daudioroom_p.h b/src/spatialaudioquick3d/qquick3daudioroom_p.h
new file mode 100644
index 000000000..8198b9307
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3daudioroom_p.h
@@ -0,0 +1,129 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QQUICK3DAUDIOROOM_H
+#define QQUICK3DAUDIOROOM_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qquick3dnode_p.h>
+#include <QtGui/qvector3d.h>
+#include <qaudioroom.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAudioEngine;
+class QAudioRoomPrivate;
+
+class QQuick3DAudioRoom : public QQuick3DNode
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector3D position READ position WRITE setPosition NOTIFY positionChanged)
+ Q_PROPERTY(QVector3D dimensions READ dimensions WRITE setDimensions NOTIFY dimensionsChanged)
+ Q_PROPERTY(QQuaternion rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
+ Q_PROPERTY(Material leftMaterial READ leftMaterial WRITE setLeftMaterial NOTIFY wallsChanged)
+ Q_PROPERTY(Material rightMaterial READ rightMaterial WRITE setRightMaterial NOTIFY wallsChanged)
+ Q_PROPERTY(Material frontMaterial READ frontMaterial WRITE setFrontMaterial NOTIFY wallsChanged)
+ Q_PROPERTY(Material backMaterial READ backMaterial WRITE setBackMaterial NOTIFY wallsChanged)
+ Q_PROPERTY(Material floorMaterial READ floorMaterial WRITE setFloorMaterial NOTIFY wallsChanged)
+ Q_PROPERTY(Material ceilingMaterial READ ceilingMaterial WRITE setCeilingMaterial NOTIFY wallsChanged)
+ Q_PROPERTY(float reflectionGain READ reflectionGain WRITE setReflectionGain NOTIFY reflectionGainChanged)
+ Q_PROPERTY(float reverbGain READ reverbGain WRITE setReverbGain NOTIFY reverbGainChanged)
+ Q_PROPERTY(float reverbTime READ reverbTime WRITE setReverbTime NOTIFY reverbTimeChanged)
+ Q_PROPERTY(float reverbBrightness READ reverbBrightness WRITE setReverbBrightness NOTIFY reverbBrightnessChanged)
+ QML_NAMED_ELEMENT(AudioRoom)
+public:
+ QQuick3DAudioRoom();
+ ~QQuick3DAudioRoom();
+
+ enum Material {
+ Transparent,
+ AcousticCeilingTiles,
+ BrickBare,
+ BrickPainted,
+ ConcreteBlockCoarse,
+ ConcreteBlockPainted,
+ CurtainHeavy,
+ FiberGlassInsulation,
+ GlassThin,
+ GlassThick,
+ Grass,
+ LinoleumOnConcrete,
+ Marble,
+ Metal,
+ ParquetOnConcrete,
+ PlasterRough,
+ PlasterSmooth,
+ PlywoodPanel,
+ PolishedConcreteOrTile,
+ Sheetrock,
+ WaterOrIceSurface,
+ WoodCeiling,
+ WoodPanel,
+ Uniform,
+ };
+ Q_ENUM(Material)
+
+ void setDimensions(QVector3D pos);
+ QVector3D dimensions() const;
+
+ void setLeftMaterial(Material material);
+ Material leftMaterial() const;
+
+ void setRightMaterial(Material material);
+ Material rightMaterial() const;
+
+ void setFrontMaterial(Material material);
+ Material frontMaterial() const;
+
+ void setBackMaterial(Material material);
+ Material backMaterial() const;
+
+ void setFloorMaterial(Material material);
+ Material floorMaterial() const;
+
+ void setCeilingMaterial(Material material);
+ Material ceilingMaterial() const;
+
+ void setReflectionGain(float factor);
+ float reflectionGain() const;
+
+ void setReverbGain(float factor);
+ float reverbGain() const;
+
+ void setReverbTime(float factor);
+ float reverbTime() const;
+
+ void setReverbBrightness(float factor);
+ float reverbBrightness() const;
+
+Q_SIGNALS:
+ void positionChanged();
+ void dimensionsChanged();
+ void rotationChanged();
+ void wallsChanged();
+ void reflectionGainChanged();
+ void reverbGainChanged();
+ void reverbTimeChanged();
+ void reverbBrightnessChanged();
+
+protected Q_SLOTS:
+ void updatePosition();
+ void updateRotation();
+
+private:
+ QAudioRoom *m_room;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/spatialaudioquick3d/qquick3dspatialaudio_plugin.cpp b/src/spatialaudioquick3d/qquick3dspatialaudio_plugin.cpp
new file mode 100644
index 000000000..399a215ad
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3dspatialaudio_plugin.cpp
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include "qtquick3daudioglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuick3DAudioModule : public QQmlEngineExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
+
+public:
+ QQuick3DAudioModule(QObject *parent = nullptr)
+ : QQmlEngineExtensionPlugin(parent)
+ {
+ volatile auto registration = qml_register_types_QtQuick3D_SpatialAudio;
+ Q_UNUSED(registration);
+ }
+
+ void initializeEngine(QQmlEngine *engine, const char *uri) override
+ {
+ Q_UNUSED(engine);
+ Q_UNUSED(uri);
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "qquick3dspatialaudio_plugin.moc"
+
diff --git a/src/spatialaudioquick3d/qquick3dspatialsound.cpp b/src/spatialaudioquick3d/qquick3dspatialsound.cpp
new file mode 100644
index 000000000..04c935c56
--- /dev/null
+++ b/src/spatialaudioquick3d/qquick3dspatialsound.cpp
@@ -0,0 +1,323 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#include "qquick3dspatialsound_p.h"
+#include "qquick3daudioengine_p.h"
+#include "qspatialsound.h"
+#include <QAudioFormat>
+#include <qdir.h>
+#include <QQmlContext>
+#include <QQmlFile>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype SpatialSound
+ \inqmlmodule QtQuick3D.SpatialAudio
+ \ingroup quick3d_spatialaudio
+ \ingroup multimedia_audio_qml
+
+ \brief A sound object in 3D space.
+
+ A SpatialSound represents an audible object in 3D space. You can define
+ it's position and orientation in space, set the sound it is playing and define a
+ volume for the object.
+
+ The object can have different attenuation behavior, emit sound mainly in one direction
+ or spherically, and behave as if occluded by some other object.
+ */
+
+QQuick3DSpatialSound::QQuick3DSpatialSound()
+{
+ m_sound = new QSpatialSound(QQuick3DAudioEngine::getEngine());
+
+ connect(this, &QQuick3DNode::scenePositionChanged, this, &QQuick3DSpatialSound::updatePosition);
+ connect(this, &QQuick3DNode::sceneRotationChanged, this, &QQuick3DSpatialSound::updateRotation);
+ connect(m_sound, &QSpatialSound::sourceChanged, this, &QQuick3DSpatialSound::sourceChanged);
+ connect(m_sound, &QSpatialSound::volumeChanged, this, &QQuick3DSpatialSound::volumeChanged);
+ connect(m_sound, &QSpatialSound::distanceModelChanged, this, &QQuick3DSpatialSound::distanceModelChanged);
+ connect(m_sound, &QSpatialSound::sizeChanged, this, &QQuick3DSpatialSound::sizeChanged);
+ connect(m_sound, &QSpatialSound::distanceCutoffChanged, this, &QQuick3DSpatialSound::distanceCutoffChanged);
+ connect(m_sound, &QSpatialSound::manualAttenuationChanged, this, &QQuick3DSpatialSound::manualAttenuationChanged);
+ connect(m_sound, &QSpatialSound::occlusionIntensityChanged, this, &QQuick3DSpatialSound::occlusionIntensityChanged);
+ connect(m_sound, &QSpatialSound::directivityChanged, this, &QQuick3DSpatialSound::directivityChanged);
+ connect(m_sound, &QSpatialSound::directivityOrderChanged, this, &QQuick3DSpatialSound::directivityOrderChanged);
+ connect(m_sound, &QSpatialSound::nearFieldGainChanged, this, &QQuick3DSpatialSound::nearFieldGainChanged);
+ connect(m_sound, &QSpatialSound::loopsChanged, this, &QQuick3DSpatialSound::loopsChanged);
+ connect(m_sound, &QSpatialSound::autoPlayChanged, this, &QQuick3DSpatialSound::autoPlayChanged);
+}
+
+QQuick3DSpatialSound::~QQuick3DSpatialSound()
+{
+ delete m_sound;
+}
+
+/*!
+ \qmlproperty url SpatialSound::source
+
+ The source file for the sound to be played.
+ */
+QUrl QQuick3DSpatialSound::source() const
+{
+ return m_sound->source();
+}
+
+void QQuick3DSpatialSound::setSource(QUrl source)
+{
+ const QQmlContext *context = qmlContext(this);
+ QUrl url;
+ if (context) {
+ url = context->resolvedUrl(source);
+ } else {
+ url = QUrl::fromLocalFile(QDir::currentPath() + u"/");
+ url = url.resolved(source);
+ }
+ m_sound->setSource(url);
+}
+
+/*!
+ \qmlproperty float SpatialSound::volume
+
+ Defines an overall volume for this sound source.
+
+ Values between 0 and 1 will attenuate the sound, while values above 1
+ provide an additional gain boost.
+ */
+void QQuick3DSpatialSound::setVolume(float volume)
+{
+ m_sound->setVolume(volume);
+}
+
+float QQuick3DSpatialSound::volume() const
+{
+ return m_sound->volume();
+}
+
+/*!
+ \qmlproperty enumeration SpatialSound::distanceModel
+
+ Defines how the volume of the sound scales with distance to the listener.
+ The volume starts scaling down
+ from \l size to \l distanceCutoff. The volume is constant for distances smaller
+ than size and zero for distances larger than the cutoff distance.
+
+ \table
+ \header \li Property value
+ \li Description
+ \row \li Logarithmic
+ \li Volume decreases logarithmically with distance.
+ \row \li Linear
+ \li Volume decreases linearly with distance.
+ \row \li ManualAttenuation
+ \li Attenuation is defined manually using the \l manualAttenuation property.
+ \endtable
+ */
+void QQuick3DSpatialSound::setDistanceModel(DistanceModel model)
+{
+ m_sound->setDistanceModel(QSpatialSound::DistanceModel(model));
+}
+
+QQuick3DSpatialSound::DistanceModel QQuick3DSpatialSound::distanceModel() const
+{
+ return DistanceModel(m_sound->distanceModel());
+}
+
+/*!
+ \qmlproperty float SpatialSound::size
+
+ Defines the size of the sound source. If the listener is closer to the sound
+ object than the size, volume will stay constant. The size is also used to for
+ occlusion calculations, where large sources can be partially occluded by a wall.
+ */
+void QQuick3DSpatialSound::setSize(float min)
+{
+ m_sound->setSize(min);
+}
+
+float QQuick3DSpatialSound::size() const
+{
+ return m_sound->size();
+}
+
+/*!
+ \qmlproperty float SpatialSound::distanceCutoff
+
+ Defines a distance beyond which sound coming from the source will cutoff.
+ If the listener is further away from the sound object than the cutoff
+ distance it won't be audible anymore.
+ */
+void QQuick3DSpatialSound::setDistanceCutoff(float max)
+{
+ m_sound->setDistanceCutoff(max);
+}
+
+float QQuick3DSpatialSound::distanceCutoff() const
+{
+ return m_sound->distanceCutoff();
+}
+
+/*!
+ \qmlproperty float SpatialSound::manualAttenuation
+
+ Defines a manual attenuation factor if \l distanceModel is set to
+ SpatialSound.ManualAttenuation.
+ */
+void QQuick3DSpatialSound::setManualAttenuation(float attenuation)
+{
+ m_sound->setManualAttenuation(attenuation);
+}
+
+float QQuick3DSpatialSound::manualAttenuation() const
+{
+ return m_sound->manualAttenuation();
+}
+
+/*!
+ \qmlproperty float SpatialSound::occlusionIntensity
+
+ Defines how much the object is occluded. 0 implies the object is
+ not occluded at all, while a large number implies a large occlusion.
+
+ The default is 0.
+ */
+void QQuick3DSpatialSound::setOcclusionIntensity(float occlusion)
+{
+ m_sound->setOcclusionIntensity(occlusion);
+}
+
+float QQuick3DSpatialSound::occlusionIntensity() const
+{
+ return m_sound->occlusionIntensity();
+}
+
+/*!
+ \qmlproperty float SpatialSound::directivity
+
+ Defines the directivity of the sound source. A value of 0 implies that the sound is
+ emitted equally in all directions, while a value of 1 implies that the source mainly
+ emits sound in the forward direction.
+
+ Valid values are between 0 and 1, the default is 0.
+ */
+void QQuick3DSpatialSound::setDirectivity(float alpha)
+{
+ m_sound->setDirectivity(alpha);
+}
+
+float QQuick3DSpatialSound::directivity() const
+{
+ return m_sound->directivity();
+}
+
+/*!
+ \qmlproperty float SpatialSound::directivityOrder
+
+ Defines the order of the directivity of the sound source. A higher order
+ implies a sharper localization of the sound cone.
+
+ The minimum value and default for this property is 1.
+ */
+void QQuick3DSpatialSound::setDirectivityOrder(float alpha)
+{
+ m_sound->setDirectivityOrder(alpha);
+}
+
+float QQuick3DSpatialSound::directivityOrder() const
+{
+ return m_sound->directivityOrder();
+}
+
+/*!
+ \qmlproperty float SpatialSound::nearFieldGain
+
+ Defines the near field gain for the sound source. Valid values are between 0 and 1.
+ A near field gain of 1 will raise the volume of the sound signal by approx 20 dB for
+ distances very close to the listener.
+ */
+void QQuick3DSpatialSound::setNearFieldGain(float gain)
+{
+ m_sound->setNearFieldGain(gain);
+}
+
+float QQuick3DSpatialSound::nearFieldGain() const
+{
+ return m_sound->nearFieldGain();
+}
+
+void QQuick3DSpatialSound::updatePosition()
+{
+ m_sound->setPosition(scenePosition());
+}
+
+void QQuick3DSpatialSound::updateRotation()
+{
+ m_sound->setRotation(sceneRotation());
+}
+
+/*!
+ \qmlproperty int SpatialSound::loops
+
+ Determines how often the sound is played before the player stops.
+ Set to SpatialSound::Infinite to loop the current sound forever.
+
+ The default value is \c 1.
+ */
+int QQuick3DSpatialSound::loops() const
+{
+ return m_sound->loops();
+}
+
+void QQuick3DSpatialSound::setLoops(int loops)
+{
+ m_sound->setLoops(loops);
+}
+
+/*!
+ \qmlproperty bool SpatialSound::autoPlay
+
+ Determines whether the sound should automatically start playing when a source
+ gets specified.
+
+ The default value is \c true.
+ */
+bool QQuick3DSpatialSound::autoPlay() const
+{
+ return m_sound->autoPlay();
+}
+
+void QQuick3DSpatialSound::setAutoPlay(bool autoPlay)
+{
+ m_sound->setAutoPlay(autoPlay);
+}
+
+/*!
+ \qmlmethod SpatialSound::play()
+
+ Starts playing back the sound. Does nothing if the sound is already playing.
+ */
+void QQuick3DSpatialSound::play()
+{
+ m_sound->play();
+}
+
+/*!
+ \qmlmethod SpatialSound::pause()
+
+ Pauses sound playback at the current position. Calling play() will continue playback.
+ */
+void QQuick3DSpatialSound::pause()
+{
+ m_sound->pause();
+}
+
+/*!
+ \qmlmethod SpatialSound::stop()
+
+ Stops sound playback and resets the current position and loop count to 0. Calling play() will
+ begin playback at the beginning of the sound file.
+ */
+void QQuick3DSpatialSound::stop()
+{
+ m_sound->stop();
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimediaquick3d/qquick3dspatialaudiosoundsource_p.h b/src/spatialaudioquick3d/qquick3dspatialsound_p.h
index 9cc835fe9..d24a63056 100644
--- a/src/multimediaquick3d/qquick3dspatialaudiosoundsource_p.h
+++ b/src/spatialaudioquick3d/qquick3dspatialsound_p.h
@@ -1,50 +1,27 @@
-/****************************************************************************
-**
-** Copyright (C) 2022 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Spatial Audio module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL-NOGPL2$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QQUICK3DSOUND_H
-#define QQUICK3DSOUND_H
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+#ifndef QQUICK3DSPATIALSOUND_H
+#define QQUICK3DSPATIALSOUND_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
#include <private/qquick3dnode_p.h>
#include <QUrl>
#include <qvector3d.h>
-#include <qspatialaudiosoundsource.h>
+#include <qspatialsound.h>
QT_BEGIN_NAMESPACE
-class QQuick3DSpatialAudioSoundSource : public QQuick3DNode
+class QQuick3DSpatialSound : public QQuick3DNode
{
Q_OBJECT
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
@@ -59,11 +36,11 @@ class QQuick3DSpatialAudioSoundSource : public QQuick3DNode
Q_PROPERTY(float nearFieldGain READ nearFieldGain WRITE setNearFieldGain NOTIFY nearFieldGainChanged)
Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged)
Q_PROPERTY(bool autoPlay READ autoPlay WRITE setAutoPlay NOTIFY autoPlayChanged)
- QML_NAMED_ELEMENT(SpatialAudioSoundSource)
+ QML_NAMED_ELEMENT(SpatialSound)
public:
- QQuick3DSpatialAudioSoundSource();
- ~QQuick3DSpatialAudioSoundSource();
+ QQuick3DSpatialSound();
+ ~QQuick3DSpatialSound();
void setSource(QUrl source);
QUrl source() const;
@@ -74,7 +51,7 @@ public:
enum DistanceModel {
Logarithmic,
Linear,
- ManualAttenutation
+ ManualAttenuation
};
Q_ENUM(DistanceModel);
@@ -142,7 +119,7 @@ protected:
QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *) override { return nullptr; }
private:
- QSpatialAudioSoundSource *m_sound = nullptr;
+ QSpatialSound *m_sound = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/spatialaudioquick3d/qtquick3daudioglobal_p.h b/src/spatialaudioquick3d/qtquick3daudioglobal_p.h
new file mode 100644
index 000000000..9fbad2173
--- /dev/null
+++ b/src/spatialaudioquick3d/qtquick3daudioglobal_p.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QQUICK3DAUDIOGLOBAL_P_H
+#define QQUICK3DAUDIOGLOBAL_P_H
+
+#include <QtCore/qglobal.h>
+#include <QtMultimediaQuick/qtmultimediaquickexports.h>
+
+QT_BEGIN_NAMESPACE
+
+void Q_MULTIMEDIAQUICK_EXPORT qml_register_types_QtQuick3D_SpatialAudio();
+
+QT_END_NAMESPACE
+
+#endif // QMULTIMEDIAQUICKDEFS_P_H
+
diff --git a/src/spatialaudioquick3d/qtquick3daudiotypes_p.h b/src/spatialaudioquick3d/qtquick3daudiotypes_p.h
new file mode 100644
index 000000000..814cb4077
--- /dev/null
+++ b/src/spatialaudioquick3d/qtquick3daudiotypes_p.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
+
+#ifndef QQUICK3DAUDIOTYPES_H
+#define QQUICK3DAUDIOTYPES_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/qqml.h>
+#include <private/qtquick3daudioglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// Nothing for now
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/sync.profile b/sync.profile
deleted file mode 100644
index d5bb0671d..000000000
--- a/sync.profile
+++ /dev/null
@@ -1,23 +0,0 @@
-%modules = ( # path to module name map
- "QtMultimedia" => "$basedir/src/multimedia",
- "QtMultimediaWidgets" => "$basedir/src/multimediawidgets",
- "QtMultimediaQuick" => "$basedir/src/multimediaquick",
- "QtQuick3DSpatialAudio" => "$basedir/src/multimediaquick3d",
-);
-
-%moduleheaders = ( # restrict the module headers to those found in relative path
-);
-
-%classnames = (
- "qaudio.h" => "QAudio",
- "qmediametadata.h" => "QMediaMetaData",
- "qmultimedia.h" => "QMultimedia"
-);
-%deprecatedheaders = (
- "QtMultimedia" => {
- "qtmultimediadefs.h" => "QtMultimedia/qtmultimediaglobal.h"
- },
- "QtMultimediaWidgets" => {
- "qtmultimediawidgetdefs.h" => "QtMultimediaWidgets/qtmultimediawidgetsglobal.h"
- }
-);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 221413713..800bc265b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from tests.pro.
if(QT_BUILD_STANDALONE_TESTS)
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
index 7a402f847..66b291222 100644
--- a/tests/auto/CMakeLists.txt
+++ b/tests/auto/CMakeLists.txt
@@ -1,7 +1,6 @@
-# Generated from auto.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+add_subdirectory(cmake)
add_subdirectory(unit)
add_subdirectory(integration)
-# special case begin
-# add_subdirectory(cmake)
-# special case end
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index 62b88dcc1..6e556ff93 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -1,24 +1,26 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
-
-project(qmake_cmake_files)
+project(qtmultimedia_cmake_tests)
enable_testing()
-find_package(Qt5Core REQUIRED)
+find_package(Qt6 REQUIRED COMPONENTS Core)
+find_package(Qt6 OPTIONAL_COMPONENTS Widgets)
-include("${_Qt5CTestMacros}")
+include("${_Qt6CTestMacros}")
set(qt_module_includes
Multimedia QCamera
)
-if (NOT NO_WIDGETS)
+if(TARGET Qt6::Widgets)
list(APPEND qt_module_includes
MultimediaWidgets QVideoWidget
)
endif()
-test_module_includes(
+_qt_internal_test_module_includes(
${qt_module_includes}
)
diff --git a/tests/auto/integration/CMakeLists.txt b/tests/auto/integration/CMakeLists.txt
index 3f27496cb..cbacf0c1a 100644
--- a/tests/auto/integration/CMakeLists.txt
+++ b/tests/auto/integration/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from integration.pro.
# special case begin
@@ -7,9 +10,14 @@ add_subdirectory(qaudiosource)
add_subdirectory(qaudiosink)
add_subdirectory(qmediaplayerbackend)
add_subdirectory(qsoundeffect)
+add_subdirectory(qvideoframebackend)
+add_subdirectory(backends)
+add_subdirectory(multiapp)
if(TARGET Qt::Widgets)
add_subdirectory(qmediacapturesession)
add_subdirectory(qcamerabackend)
+ add_subdirectory(qscreencapturebackend)
+ add_subdirectory(qwindowcapturebackend)
endif()
if(TARGET Qt::Quick)
add_subdirectory(qquickvideooutput)
diff --git a/tests/auto/integration/backends/CMakeLists.txt b/tests/auto/integration/backends/CMakeLists.txt
new file mode 100644
index 000000000..b65293b5e
--- /dev/null
+++ b/tests/auto/integration/backends/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_backends
+ SOURCES
+ tst_backends.cpp
+ LIBRARIES
+ Qt::MultimediaPrivate
+)
diff --git a/tests/auto/integration/backends/tst_backends.cpp b/tests/auto/integration/backends/tst_backends.cpp
new file mode 100644
index 000000000..2cc1df256
--- /dev/null
+++ b/tests/auto/integration/backends/tst_backends.cpp
@@ -0,0 +1,60 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QDebug>
+#include <QtCore/qsysinfo.h>
+#include <private/qplatformmediaintegration_p.h>
+
+QT_USE_NAMESPACE
+
+class tst_backends : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void initTestCase()
+ {
+ // Log operating system name and currently supported backends
+ qDebug() << QSysInfo::prettyProductName() << "supports backends"
+ << QPlatformMediaIntegration::availableBackends().join(", ");
+ }
+
+private slots:
+ void availableBackends_returns_expectedBackends_data()
+ {
+ QTest::addColumn<QStringList>("expectedBackends");
+ QStringList backends;
+
+#if defined(Q_OS_WIN)
+ backends << "windows";
+ if (QSysInfo::currentCpuArchitecture() == "x86_64")
+ backends << "ffmpeg";
+#elif defined(Q_OS_ANDROID)
+ backends << "android" << "ffmpeg";
+#elif defined(Q_OS_DARWIN)
+ backends << "darwin" << "ffmpeg";
+#elif defined(Q_OS_WASM)
+ backends << "wasm";
+#elif defined(Q_OS_QNX)
+ backends << "qnx";
+#else
+ backends << "ffmpeg" << "gstreamer";
+#endif
+
+ QTest::addRow("backends") << backends;
+ }
+
+ void availableBackends_returns_expectedBackends()
+ {
+ QFETCH(QStringList, expectedBackends);
+ QStringList actualBackends = QPlatformMediaIntegration::availableBackends();
+ for (const auto &expectedBackend : expectedBackends) {
+ QVERIFY(actualBackends.contains(expectedBackend));
+ }
+ }
+};
+
+QTEST_MAIN(tst_backends)
+
+#include "tst_backends.moc"
diff --git a/tests/auto/integration/multiapp/CMakeLists.txt b/tests/auto/integration/multiapp/CMakeLists.txt
new file mode 100644
index 000000000..8a297cafc
--- /dev/null
+++ b/tests/auto/integration/multiapp/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_multiapp
+ SOURCES
+ tst_multiapp.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::MultimediaPrivate
+)
+
+set(resources_resource_files
+ "double-drop.wav"
+)
+
+qt_add_resources(tst_multiapp "resources"
+ PREFIX
+ "/"
+ FILES
+ ${resources_resource_files}
+)
diff --git a/tests/auto/integration/multiapp/double-drop.wav b/tests/auto/integration/multiapp/double-drop.wav
new file mode 100644
index 000000000..bd9a507c7
--- /dev/null
+++ b/tests/auto/integration/multiapp/double-drop.wav
Binary files differ
diff --git a/tests/auto/integration/multiapp/tst_multiapp.cpp b/tests/auto/integration/multiapp/tst_multiapp.cpp
new file mode 100644
index 000000000..793a56e9d
--- /dev/null
+++ b/tests/auto/integration/multiapp/tst_multiapp.cpp
@@ -0,0 +1,161 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QtCore/qdebug.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qmetaobject.h>
+#include <QtMultimedia/qsoundeffect.h>
+#include <QtMultimedia/qmediadevices.h>
+#include <QtMultimedia/qaudiodevice.h>
+
+using namespace Qt::StringLiterals;
+
+QT_USE_NAMESPACE
+
+namespace {
+bool executeTestOutOfProcess(const QString &testName);
+void playSound();
+} // namespace
+
+class tst_multiapp : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void initTestCase()
+ {
+#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
+ QSKIP("Out-of-process testing does not behave correctly on mobile OS");
+#endif
+ }
+
+private slots:
+ void mediaDevices_doesNotCrash_whenRecreatingApplication()
+ {
+ QVERIFY(executeTestOutOfProcess(
+ "mediaDevices_doesNotCrash_whenRecreatingApplication_impl"_L1));
+ }
+
+ bool mediaDevices_doesNotCrash_whenRecreatingApplication_impl(int argc, char ** argv)
+ {
+ {
+ QCoreApplication app{ argc, argv };
+ QMediaDevices::defaultAudioOutput();
+ }
+ {
+ QCoreApplication app{ argc, argv };
+ QMediaDevices::defaultAudioOutput();
+ }
+
+ return true;
+ }
+
+ void soundEffect_doesNotCrash_whenRecreatingApplication()
+ {
+ QVERIFY(executeTestOutOfProcess(
+ "soundEffect_doesNotCrash_whenRecreatingApplication_impl"_L1));
+ }
+
+ bool soundEffect_doesNotCrash_whenRecreatingApplication_impl(int argc, char **argv)
+ {
+ Q_ASSERT(!qApp);
+
+ // Play a sound twice under two different application objects
+ // This verifies that QSoundEffect works in use cases where
+ // client application recreates Qt application instances,
+ // for example when the client application loads plugins
+ // implemented using Qt.
+ {
+ QCoreApplication app{ argc, argv };
+ playSound();
+ }
+ {
+ QCoreApplication app{ argc, argv };
+ playSound();
+ }
+
+ return true;
+ }
+
+};
+
+namespace {
+
+void playSound()
+{
+ const QUrl url{ "qrc:double-drop.wav"_L1 };
+
+ QSoundEffect effect;
+ effect.setSource(url);
+ effect.play();
+
+ QObject::connect(&effect, &QSoundEffect::playingChanged, qApp, [&]() {
+ if (!effect.isPlaying())
+ qApp->quit();
+ });
+
+ // In some CI configurations, we do not have any audio devices. We must therefore
+ // close the qApp on error signal instead of on playingChanged.
+ QObject::connect(&effect, &QSoundEffect::statusChanged, qApp, [&]() {
+ if (effect.status() == QSoundEffect::Status::Error) {
+ qDebug() << "Failed to play sound effect";
+ qApp->quit();
+ }
+ });
+
+ qApp->exec();
+}
+
+bool executeTestOutOfProcess(const QString &testName)
+{
+ const QStringList args{ "--run-test"_L1, testName };
+ const QString processName = QCoreApplication::applicationFilePath();
+ const int status = QProcess::execute(processName, args);
+ return status == 0;
+}
+
+} // namespace
+
+// This main function executes tests like normal qTest, and adds support
+// for executing specific test functions when called out of process. In this
+// case we don't create a QApplication, because the intent is to test how features
+// behave when no QApplication exists.
+int main(int argc, char *argv[])
+{
+ QCommandLineParser cmd;
+ const QCommandLineOption runTest{ QStringList{ "run-test" }, "Executes a named test",
+ "runTest" };
+ cmd.addOption(runTest);
+ cmd.parse({ argv, argv + argc });
+
+ if (cmd.isSet(runTest)) {
+ // We are requested to run a test case in a separate process without a Qt application
+ const QString testName = cmd.value(runTest);
+
+ bool returnValue = false;
+ tst_multiapp tc;
+
+ // Call the requested function on the test class
+ const bool invokeResult =
+ QMetaObject::invokeMethod(&tc, testName.toLatin1(), Qt::DirectConnection,
+ qReturnArg(returnValue), argc, argv);
+
+ return (invokeResult && returnValue) ? 0 : 1;
+ }
+
+ // If no special arguments are set, enter the regular QTest main routine
+ // The below lines are the same that QTEST_GUILESS_MAIN would stamp out,
+ // except the `int main(...)`
+ TESTLIB_SELFCOVERAGE_START("tst_multiapp")
+ QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<tst_multiapp>();
+ QCoreApplication app(argc, argv);
+ app.setAttribute(Qt::AA_Use96Dpi, true);
+ tst_multiapp tc;
+ QTEST_SET_MAIN_SOURCE_PATH
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include "tst_multiapp.moc"
diff --git a/tests/auto/integration/qaudiodecoderbackend/CMakeLists.txt b/tests/auto/integration/qaudiodecoderbackend/CMakeLists.txt
index bef856a1e..0133c7498 100644
--- a/tests/auto/integration/qaudiodecoderbackend/CMakeLists.txt
+++ b/tests/auto/integration/qaudiodecoderbackend/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qaudiodecoderbackend.pro.
#####################################################################
@@ -12,11 +15,13 @@ list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_qaudiodecoderbackend
SOURCES
- ../shared/mediafileselector.h
tst_qaudiodecoderbackend.cpp
+ ../shared/mediafileselector.h
+ ../shared/mediabackendutils.h
INCLUDE_DIRECTORIES
+ ../shared/
../../../../src/multimedia/audio
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::Multimedia
Qt::MultimediaPrivate
diff --git a/tests/auto/integration/qaudiodecoderbackend/testdata/test-no-audio-track.mp4 b/tests/auto/integration/qaudiodecoderbackend/testdata/test-no-audio-track.mp4
new file mode 100644
index 000000000..6b67a3433
--- /dev/null
+++ b/tests/auto/integration/qaudiodecoderbackend/testdata/test-no-audio-track.mp4
Binary files differ
diff --git a/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp b/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp
index c04d7ea2f..5fb3a81a6 100644
--- a/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp
+++ b/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp
@@ -1,43 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
#include "qaudiodecoder.h"
-#ifdef WAV_SUPPORT_NOT_FORCED
-#include "../shared/mediafileselector.h"
-#endif
+#include "mediafileselector.h"
+#include "mediabackendutils.h"
-#define TEST_FILE_NAME "testdata/test.wav"
-#define TEST_UNSUPPORTED_FILE_NAME "testdata/test-unsupported.avi"
-#define TEST_CORRUPTED_FILE_NAME "testdata/test-corrupted.wav"
-#define TEST_INVALID_SOURCE "invalid"
+constexpr char TEST_FILE_NAME[] = "testdata/test.wav";
+constexpr char TEST_UNSUPPORTED_FILE_NAME[] = "testdata/test-unsupported.avi";
+constexpr char TEST_CORRUPTED_FILE_NAME[] = "testdata/test-corrupted.wav";
+constexpr char TEST_INVALID_SOURCE[] = "invalid";
+constexpr char TEST_NO_AUDIO_TRACK[] = "testdata/test-no-audio-track.mp4";
QT_USE_NAMESPACE
@@ -57,14 +32,29 @@ public slots:
void initTestCase();
private slots:
+ void testMediaFilesAreSupported();
+ void directBruteForceReading();
+ void indirectReadingByBufferReadySignal();
+ void indirectReadingByBufferAvailableSignal();
+ void stopOnBufferReady();
+ void restartOnBufferReady();
+ void restartOnFinish();
void fileTest();
void unsupportedFileTest();
void corruptedFileTest();
void invalidSource();
void deviceTest();
+ void play_emitsFormatError_whenMediaHasNoAudioTrack();
private:
- bool isWavSupported();
+ QUrl testFileUrl(const QString filePath);
+ void checkNoMoreChanges(QAudioDecoder &decoder);
+#ifdef Q_OS_ANDROID
+ QTemporaryFile *temporaryFile = nullptr;
+#endif
+
+ MediaFileSelector m_mediaSelector;
+ MaybeUrl m_wavFile = QUnexpect{};
};
void tst_QAudioDecoderBackend::init()
@@ -76,25 +66,287 @@ void tst_QAudioDecoderBackend::initTestCase()
QAudioDecoder d;
if (!d.isSupported())
QSKIP("Audio decoder service is not available");
+
+ m_wavFile = m_mediaSelector.select(QFINDTESTDATA(TEST_FILE_NAME));
}
void tst_QAudioDecoderBackend::cleanup()
{
+#ifdef Q_OS_ANDROID
+ if (temporaryFile) {
+ delete temporaryFile;
+ temporaryFile = nullptr;
+ }
+#endif
}
-bool tst_QAudioDecoderBackend::isWavSupported()
+QUrl tst_QAudioDecoderBackend::testFileUrl(const QString filePath)
{
-#ifdef WAV_SUPPORT_NOT_FORCED
- return !MediaFileSelector::selectMediaFile(QStringList() << QFINDTESTDATA(TEST_FILE_NAME)).isNull();
+ QUrl url;
+#ifndef Q_OS_ANDROID
+ QFileInfo fileInfo(QFINDTESTDATA(filePath));
+ url = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
#else
- return true;
+ QFile file(":/" + filePath);
+ if (temporaryFile) {
+ delete temporaryFile;
+ temporaryFile = nullptr;
+ }
+ if (file.open(QIODevice::ReadOnly)) {
+ temporaryFile = QTemporaryFile::createNativeFile(file);
+ url = QUrl(temporaryFile->fileName());
+ }
#endif
+ return url;
+}
+
+void tst_QAudioDecoderBackend::checkNoMoreChanges(QAudioDecoder &decoder)
+{
+ QSignalSpy finishedSpy(&decoder, &QAudioDecoder::finished);
+ QSignalSpy bufferReadySpy(&decoder, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferAvailableSpy(&decoder, &QAudioDecoder::bufferAvailableChanged);
+
+ QTest::qWait(50); // wait a bit to check nothing happened after finish
+
+ QCOMPARE(finishedSpy.size(), 0);
+ QCOMPARE(bufferReadySpy.size(), 0);
+ QCOMPARE(bufferAvailableSpy.size(), 0);
+}
+
+void tst_QAudioDecoderBackend::testMediaFilesAreSupported()
+{
+ QCOMPARE(m_mediaSelector.dumpErrors(), "");
+}
+
+void tst_QAudioDecoderBackend::directBruteForceReading()
+{
+ CHECK_SELECTED_URL(m_wavFile);
+
+ QAudioDecoder decoder;
+ if (decoder.error() == QAudioDecoder::NotSupportedError)
+ QSKIP("There is no audio decoding support on this platform.");
+
+ int sampleCount = 0;
+
+ decoder.setSource(*m_wavFile);
+ QVERIFY(!decoder.isDecoding());
+ QVERIFY(!decoder.bufferAvailable());
+
+ decoder.start();
+ QTRY_VERIFY(decoder.isDecoding());
+
+ auto waitAndCheck = [](auto &&predicate) { QVERIFY(QTest::qWaitFor(predicate)); };
+
+ auto waitForBufferAvailable = [&]() {
+ waitAndCheck([&]() { return !decoder.isDecoding() || decoder.bufferAvailable(); });
+
+ return decoder.bufferAvailable();
+ };
+
+ while (waitForBufferAvailable()) {
+ auto buffer = decoder.read();
+ QVERIFY(buffer.isValid());
+
+ sampleCount += buffer.sampleCount();
+ }
+
+ checkNoMoreChanges(decoder);
+
+ QCOMPARE(sampleCount, 44094);
+}
+
+void tst_QAudioDecoderBackend::indirectReadingByBufferReadySignal()
+{
+ CHECK_SELECTED_URL(m_wavFile);
+
+ QAudioDecoder decoder;
+ if (decoder.error() == QAudioDecoder::NotSupportedError)
+ QSKIP("There is no audio decoding support on this platform.");
+
+ int sampleCount = 0;
+
+ connect(&decoder, &QAudioDecoder::bufferReady, this, [&]() {
+ QVERIFY(decoder.bufferAvailable());
+
+ auto buffer = decoder.read();
+ QVERIFY(buffer.isValid());
+ QVERIFY(!decoder.bufferAvailable());
+
+ sampleCount += buffer.sampleCount();
+ });
+
+ QSignalSpy decodingSpy(&decoder, &QAudioDecoder::isDecodingChanged);
+ QSignalSpy finishSpy(&decoder, &QAudioDecoder::finished);
+
+ decoder.setSource(*m_wavFile);
+ QVERIFY(!decoder.isDecoding());
+ QVERIFY(!decoder.bufferAvailable());
+
+ decoder.start();
+ QTRY_VERIFY(decodingSpy.size() >= 1);
+
+ QTRY_VERIFY(finishSpy.size() == 1);
+ QVERIFY(!decoder.isDecoding());
+
+ checkNoMoreChanges(decoder);
+
+ QCOMPARE(sampleCount, 44094);
+ QCOMPARE(finishSpy.size(), 1);
+}
+
+void tst_QAudioDecoderBackend::indirectReadingByBufferAvailableSignal() {
+ CHECK_SELECTED_URL(m_wavFile);
+
+ QAudioDecoder decoder;
+ if (decoder.error() == QAudioDecoder::NotSupportedError)
+ QSKIP("There is no audio decoding support on this platform.");
+
+ int sampleCount = 0;
+
+ connect(&decoder, &QAudioDecoder::bufferAvailableChanged, this, [&](bool available) {
+ QCOMPARE(decoder.bufferAvailable(), available);
+
+ if (!available)
+ return;
+
+ while (decoder.bufferAvailable()) {
+ auto buffer = decoder.read();
+ QVERIFY(buffer.isValid());
+
+ sampleCount += buffer.sampleCount();
+ }
+ });
+
+ QSignalSpy decodingSpy(&decoder, &QAudioDecoder::isDecodingChanged);
+ QSignalSpy finishSpy(&decoder, &QAudioDecoder::finished);
+
+ decoder.setSource(*m_wavFile);
+ QVERIFY(!decoder.isDecoding());
+ QVERIFY(!decoder.bufferAvailable());
+
+ decoder.start();
+ QTRY_VERIFY(decodingSpy.size() >= 1);
+
+ QTRY_VERIFY(finishSpy.size() == 1);
+ QVERIFY(!decoder.isDecoding());
+
+ checkNoMoreChanges(decoder);
+
+ QCOMPARE(sampleCount, 44094);
+ QCOMPARE(finishSpy.size(), 1);
+}
+
+void tst_QAudioDecoderBackend::stopOnBufferReady()
+{
+ CHECK_SELECTED_URL(m_wavFile);
+
+ QAudioDecoder decoder;
+ if (decoder.error() == QAudioDecoder::NotSupportedError)
+ QSKIP("There is no audio decoding support on this platform.");
+
+ connect(&decoder, &QAudioDecoder::bufferReady, this, [&]() {
+ decoder.read(); // run next reading
+ decoder.stop();
+ });
+
+ QSignalSpy finishSpy(&decoder, &QAudioDecoder::finished);
+ QSignalSpy bufferReadySpy(&decoder, &QAudioDecoder::bufferReady);
+
+ decoder.setSource(*m_wavFile);
+ decoder.start();
+
+ bufferReadySpy.wait();
+ QVERIFY(!decoder.isDecoding());
+
+ checkNoMoreChanges(decoder);
+
+ QCOMPARE(bufferReadySpy.size(), 1);
+}
+
+void tst_QAudioDecoderBackend::restartOnBufferReady()
+{
+ CHECK_SELECTED_URL(m_wavFile);
+
+ QAudioDecoder decoder;
+ if (decoder.error() == QAudioDecoder::NotSupportedError)
+ QSKIP("There is no audio decoding support on this platform.");
+
+ int sampleCount = 0;
+
+ std::once_flag restartOnce;
+ connect(&decoder, &QAudioDecoder::bufferReady, this, [&]() {
+ QVERIFY(decoder.bufferAvailable());
+
+ auto buffer = decoder.read();
+ QVERIFY(buffer.isValid());
+ QVERIFY(!decoder.bufferAvailable());
+
+ sampleCount += buffer.sampleCount();
+
+ std::call_once(restartOnce, [&]() {
+ sampleCount = 0;
+ decoder.stop();
+ decoder.start();
+ });
+ });
+
+ QSignalSpy finishSpy(&decoder, &QAudioDecoder::finished);
+
+ decoder.setSource(*m_wavFile);
+ decoder.start();
+
+ QTRY_VERIFY2(finishSpy.size() == 2, "Wait for signals after restart and after finishing");
+ QVERIFY(!decoder.isDecoding());
+
+ checkNoMoreChanges(decoder);
+
+ QCOMPARE(sampleCount, 44094);
+}
+
+void tst_QAudioDecoderBackend::restartOnFinish()
+{
+ CHECK_SELECTED_URL(m_wavFile);
+
+ QAudioDecoder decoder;
+ if (decoder.error() == QAudioDecoder::NotSupportedError)
+ QSKIP("There is no audio decoding support on this platform.");
+
+ int sampleCount = 0;
+
+ connect(&decoder, &QAudioDecoder::bufferReady, this, [&]() {
+ auto buffer = decoder.read();
+ QVERIFY(buffer.isValid());
+
+ sampleCount += buffer.sampleCount();
+ });
+
+ QSignalSpy finishSpy(&decoder, &QAudioDecoder::finished);
+
+ std::once_flag restartOnce;
+ connect(&decoder, &QAudioDecoder::finished, this, [&]() {
+ QVERIFY(!decoder.bufferAvailable());
+ QVERIFY(!decoder.isDecoding());
+
+ std::call_once(restartOnce, [&]() {
+ sampleCount = 0;
+ decoder.start();
+ });
+ });
+
+ decoder.setSource(*m_wavFile);
+ decoder.start();
+
+ QTRY_VERIFY(finishSpy.size() == 2);
+
+ QVERIFY(!decoder.isDecoding());
+
+ checkNoMoreChanges(decoder);
+ QCOMPARE(sampleCount, 44094);
}
void tst_QAudioDecoderBackend::fileTest()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ CHECK_SELECTED_URL(m_wavFile);
QAudioDecoder d;
if (d.error() == QAudioDecoder::NotSupportedError)
@@ -106,33 +358,37 @@ void tst_QAudioDecoderBackend::fileTest()
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QCOMPARE(d.source(), QString(""));
+ QCOMPARE(d.source(), QStringLiteral(""));
QVERIFY(d.audioFormat() == QAudioFormat());
// Test local file
- QFileInfo fileInfo(QFINDTESTDATA(TEST_FILE_NAME));
- QUrl url = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
- d.setSource(url);
+
+ d.setSource(*m_wavFile);
QVERIFY(!d.isDecoding());
QVERIFY(!d.bufferAvailable());
- QCOMPARE(d.source(), url);
+ QCOMPARE(d.source(), *m_wavFile);
- QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
- QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged);
QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
- QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool)));
- QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64)));
- QSignalSpy finishedSpy(&d, SIGNAL(finished()));
- QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64)));
+ QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged);
+ QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged);
+ QSignalSpy finishedSpy(&d, &QAudioDecoder::finished);
+ QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged);
d.start();
- QTRY_VERIFY(d.isDecoding());
+
QTRY_VERIFY(!isDecodingSpy.isEmpty());
QTRY_VERIFY(!readySpy.isEmpty());
QTRY_VERIFY(!bufferChangedSpy.isEmpty());
QVERIFY(d.bufferAvailable());
QTRY_VERIFY(!durationSpy.isEmpty());
- QVERIFY(qAbs(d.duration() - 1000) < 20);
+
+ QVERIFY(qAbs(durationSpy.front().front().value<qint64>() - 1000) < 20);
+ if (finishedSpy.empty())
+ QVERIFY(qAbs(d.duration() - 1000) < 20);
+ else
+ QCOMPARE(d.duration(), -1);
buffer = d.read();
QVERIFY(buffer.isValid());
@@ -176,13 +432,13 @@ void tst_QAudioDecoderBackend::fileTest()
QCOMPARE(byteCount, 44094 * 2);
QVERIFY(qAbs(qint64(duration) - 1000000) < 20000);
QVERIFY(qAbs((d.position() + (buffer.duration() / 1000)) - 1000) < 20);
- QTRY_COMPARE(finishedSpy.count(), 1);
+ QTRY_COMPARE(finishedSpy.size(), 1);
QVERIFY(!d.bufferAvailable());
QTRY_VERIFY(!d.isDecoding());
d.stop();
QTRY_VERIFY(!d.isDecoding());
- QTRY_COMPARE(durationSpy.count(), 2);
+ QTRY_COMPARE(durationSpy.size(), 2);
QCOMPARE(d.duration(), qint64(-1));
QVERIFY(!d.bufferAvailable());
readySpy.clear();
@@ -192,6 +448,9 @@ void tst_QAudioDecoderBackend::fileTest()
finishedSpy.clear();
positionSpy.clear();
+#ifdef Q_OS_ANDROID
+ QSKIP("Setting a desired audio format is not yet supported on Android", QTest::SkipSingle);
+#endif
// change output audio format
QAudioFormat format;
format.setChannelCount(2);
@@ -211,13 +470,16 @@ void tst_QAudioDecoderBackend::fileTest()
byteCount = 0;
d.start();
- QTRY_VERIFY(d.isDecoding());
QTRY_VERIFY(!isDecodingSpy.isEmpty());
QTRY_VERIFY(!readySpy.isEmpty());
QTRY_VERIFY(!bufferChangedSpy.isEmpty());
QVERIFY(d.bufferAvailable());
QTRY_VERIFY(!durationSpy.isEmpty());
- QVERIFY(qAbs(d.duration() - 1000) < 20);
+ QVERIFY(qAbs(durationSpy.front().front().value<qint64>() - 1000) < 20);
+ if (finishedSpy.empty())
+ QVERIFY(qAbs(d.duration() - 1000) < 20);
+ else
+ QCOMPARE(d.duration(), -1);
buffer = d.read();
QVERIFY(buffer.isValid());
@@ -242,8 +504,8 @@ void tst_QAudioDecoderBackend::fileTest()
buffer = d.read();
QVERIFY(buffer.isValid());
QTRY_VERIFY(!positionSpy.isEmpty());
- QCOMPARE(positionSpy.takeLast().at(0).toLongLong(), qint64(duration / 1000));
- QVERIFY(d.position() - (duration / 1000) < 20);
+ QCOMPARE(positionSpy.takeLast().at(0).toLongLong(), qlonglong(duration / 1000));
+ QCOMPARE_LT(d.position() - (duration / 1000), 20u);
duration += buffer.duration();
sampleCount += buffer.sampleCount();
@@ -256,17 +518,17 @@ void tst_QAudioDecoderBackend::fileTest()
// Resampling might end up with fewer or more samples
// so be a bit sloppy
- QVERIFY(qAbs(sampleCount - 22047) < 100);
- QVERIFY(qAbs(byteCount - 22047) < 100);
- QVERIFY(qAbs(qint64(duration) - 1000000) < 20000);
- QVERIFY(qAbs((d.position() + (buffer.duration() / 1000)) - 1000) < 20);
- QTRY_COMPARE(finishedSpy.count(), 1);
+ QCOMPARE_LT(qAbs(sampleCount - 22047), 100);
+ QCOMPARE_LT(qAbs(byteCount - 22047), 100);
+ QCOMPARE_LT(qAbs(qint64(duration) - 1000000), 20000);
+ QCOMPARE_LT(qAbs((d.position() + (buffer.duration() / 1000)) - 1000), 20);
+ QTRY_COMPARE(finishedSpy.size(), 1);
QVERIFY(!d.bufferAvailable());
QVERIFY(!d.isDecoding());
d.stop();
QTRY_VERIFY(!d.isDecoding());
- QTRY_COMPARE(durationSpy.count(), 2);
+ QTRY_COMPARE(durationSpy.size(), 2);
QCOMPARE(d.duration(), qint64(-1));
QVERIFY(!d.bufferAvailable());
}
@@ -283,24 +545,23 @@ void tst_QAudioDecoderBackend::unsupportedFileTest()
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QCOMPARE(d.source(), QString(""));
+ QCOMPARE(d.source(), QStringLiteral(""));
QVERIFY(d.audioFormat() == QAudioFormat());
// Test local file
- QFileInfo fileInfo(QFINDTESTDATA(TEST_UNSUPPORTED_FILE_NAME));
- QUrl url = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
+ QUrl url = testFileUrl(TEST_UNSUPPORTED_FILE_NAME);
d.setSource(url);
QVERIFY(!d.isDecoding());
QVERIFY(!d.bufferAvailable());
QCOMPARE(d.source(), url);
- QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
- QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged);
QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
- QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool)));
- QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64)));
- QSignalSpy finishedSpy(&d, SIGNAL(finished()));
- QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64)));
+ QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged);
+ QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged);
+ QSignalSpy finishedSpy(&d, &QAudioDecoder::finished);
+ QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged);
d.start();
QTRY_VERIFY(!d.isDecoding());
@@ -324,7 +585,7 @@ void tst_QAudioDecoderBackend::unsupportedFileTest()
QVERIFY(finishedSpy.isEmpty());
QVERIFY(positionSpy.isEmpty());
// Either reject the file directly, or set the duration to 5secs on setUrl() and back to -1 on start()
- QVERIFY(durationSpy.isEmpty() || durationSpy.count() == 2);
+ QVERIFY(durationSpy.isEmpty() || durationSpy.size() == 2);
errorSpy.clear();
@@ -341,7 +602,7 @@ void tst_QAudioDecoderBackend::unsupportedFileTest()
QVERIFY(isDecodingSpy.isEmpty());
QVERIFY(finishedSpy.isEmpty());
QVERIFY(positionSpy.isEmpty());
- QVERIFY(durationSpy.isEmpty() || durationSpy.count() == 2);
+ QVERIFY(durationSpy.isEmpty() || durationSpy.size() == 2);
d.stop();
@@ -367,20 +628,19 @@ void tst_QAudioDecoderBackend::corruptedFileTest()
QVERIFY(d.audioFormat() == QAudioFormat());
// Test local file
- QFileInfo fileInfo(QFINDTESTDATA(TEST_CORRUPTED_FILE_NAME));
- QUrl url = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
+ QUrl url = testFileUrl(TEST_CORRUPTED_FILE_NAME);
d.setSource(url);
QVERIFY(!d.isDecoding());
QVERIFY(!d.bufferAvailable());
QCOMPARE(d.source(), url);
- QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
- QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged);
QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
- QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool)));
- QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64)));
- QSignalSpy finishedSpy(&d, SIGNAL(finished()));
- QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64)));
+ QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged);
+ QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged);
+ QSignalSpy finishedSpy(&d, &QAudioDecoder::finished);
+ QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged);
d.start();
QTRY_VERIFY(!d.isDecoding());
@@ -422,7 +682,6 @@ void tst_QAudioDecoderBackend::corruptedFileTest()
QVERIFY(positionSpy.isEmpty());
QVERIFY(durationSpy.isEmpty());
-
d.stop();
QTRY_VERIFY(!d.isDecoding());
QCOMPARE(d.duration(), qint64(-1));
@@ -449,13 +708,13 @@ void tst_QAudioDecoderBackend::invalidSource()
QVERIFY(!d.bufferAvailable());
QCOMPARE(d.source(), url);
- QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
- QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged);
QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
- QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool)));
- QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64)));
- QSignalSpy finishedSpy(&d, SIGNAL(finished()));
- QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64)));
+ QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged);
+ QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged);
+ QSignalSpy finishedSpy(&d, &QAudioDecoder::finished);
+ QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged);
d.start();
QTRY_VERIFY(!d.isDecoding());
@@ -522,8 +781,7 @@ void tst_QAudioDecoderBackend::invalidSource()
void tst_QAudioDecoderBackend::deviceTest()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ CHECK_SELECTED_URL(m_wavFile);
QAudioDecoder d;
if (d.error() == QAudioDecoder::NotSupportedError)
@@ -532,21 +790,19 @@ void tst_QAudioDecoderBackend::deviceTest()
quint64 duration = 0;
int sampleCount = 0;
- QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
- QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged);
QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
- QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool)));
- QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64)));
- QSignalSpy finishedSpy(&d, SIGNAL(finished()));
- QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64)));
+ QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged);
+ QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged);
+ QSignalSpy finishedSpy(&d, &QAudioDecoder::finished);
+ QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged);
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QCOMPARE(d.source(), QString(""));
+ QCOMPARE(d.source(), QStringLiteral(""));
QVERIFY(d.audioFormat() == QAudioFormat());
-
- QFileInfo fileInfo(QFINDTESTDATA(TEST_FILE_NAME));
- QFile file(fileInfo.absoluteFilePath());
+ QFile file(m_wavFile->toString());
QVERIFY(file.open(QIODevice::ReadOnly));
d.setSourceDevice(&file);
@@ -558,13 +814,15 @@ void tst_QAudioDecoderBackend::deviceTest()
d.start();
- QTRY_VERIFY(d.isDecoding());
QTRY_VERIFY(!isDecodingSpy.isEmpty());
QTRY_VERIFY(!readySpy.isEmpty());
QTRY_VERIFY(!bufferChangedSpy.isEmpty());
QVERIFY(d.bufferAvailable());
QTRY_VERIFY(!durationSpy.isEmpty());
- QVERIFY(qAbs(d.duration() - 1000) < 20);
+ if (finishedSpy.empty())
+ QVERIFY(qAbs(d.duration() - 1000) < 20);
+ else
+ QCOMPARE(d.duration(), -1);
buffer = d.read();
QVERIFY(buffer.isValid());
@@ -602,14 +860,14 @@ void tst_QAudioDecoderBackend::deviceTest()
QCOMPARE(sampleCount, 44094);
QVERIFY(qAbs(qint64(duration) - 1000000) < 20000);
QVERIFY(qAbs((d.position() + (buffer.duration() / 1000)) - 1000) < 20);
- QTRY_COMPARE(finishedSpy.count(), 1);
+ QTRY_COMPARE(finishedSpy.size(), 1);
QVERIFY(!d.bufferAvailable());
QTRY_VERIFY(!d.isDecoding());
d.stop();
QTRY_VERIFY(!d.isDecoding());
QVERIFY(!d.bufferAvailable());
- QTRY_COMPARE(durationSpy.count(), 2);
+ QTRY_COMPARE(durationSpy.size(), 2);
QCOMPARE(d.duration(), qint64(-1));
readySpy.clear();
bufferChangedSpy.clear();
@@ -618,6 +876,9 @@ void tst_QAudioDecoderBackend::deviceTest()
finishedSpy.clear();
positionSpy.clear();
+#ifdef Q_OS_ANDROID
+ QSKIP("Setting a desired audio format is not yet supported on Android", QTest::SkipSingle);
+#endif
// Now try changing formats
QAudioFormat format;
format.setChannelCount(2);
@@ -631,13 +892,17 @@ void tst_QAudioDecoderBackend::deviceTest()
d.start();
QVERIFY(d.error() == QAudioDecoder::NoError);
- QTRY_VERIFY(d.isDecoding());
QTRY_VERIFY(!isDecodingSpy.isEmpty());
QTRY_VERIFY(!readySpy.isEmpty());
QTRY_VERIFY(!bufferChangedSpy.isEmpty());
QVERIFY(d.bufferAvailable());
QTRY_VERIFY(!durationSpy.isEmpty());
- QVERIFY(qAbs(d.duration() - 1000) < 20);
+
+ QVERIFY(qAbs(durationSpy.front().front().value<qint64>() - 1000) < 20);
+ if (finishedSpy.empty())
+ QVERIFY(qAbs(d.duration() - 1000) < 20);
+ else
+ QCOMPARE(d.duration(), -1);
buffer = d.read();
QVERIFY(buffer.isValid());
@@ -652,10 +917,26 @@ void tst_QAudioDecoderBackend::deviceTest()
d.stop();
QTRY_VERIFY(!d.isDecoding());
QVERIFY(!d.bufferAvailable());
- QTRY_COMPARE(durationSpy.count(), 2);
+ QTRY_COMPARE(durationSpy.size(), 2);
QCOMPARE(d.duration(), qint64(-1));
}
+void tst_QAudioDecoderBackend::play_emitsFormatError_whenMediaHasNoAudioTrack()
+{
+ QSKIP_GSTREAMER("QTBUG-124206: gstreamer does not emit errors");
+
+ QAudioDecoder decoder;
+
+ QSignalSpy errors{ &decoder, qOverload<QAudioDecoder::Error>(&QAudioDecoder::error) };
+
+ decoder.setSource(testFileUrl(TEST_NO_AUDIO_TRACK));
+ decoder.start();
+
+ QTRY_VERIFY(!errors.empty());
+
+ QCOMPARE_EQ(decoder.error(), QAudioDecoder::Error::FormatError);
+}
+
QTEST_MAIN(tst_QAudioDecoderBackend)
#include "tst_qaudiodecoderbackend.moc"
diff --git a/tests/auto/integration/qaudiodevice/CMakeLists.txt b/tests/auto/integration/qaudiodevice/CMakeLists.txt
index 059e62f87..93f0d49dd 100644
--- a/tests/auto/integration/qaudiodevice/CMakeLists.txt
+++ b/tests/auto/integration/qaudiodevice/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qaudiodevice.pro.
#####################################################################
@@ -7,7 +10,7 @@
qt_internal_add_test(tst_qaudiodevice
SOURCES
tst_qaudiodevice.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/integration/qaudiodevice/tst_qaudiodevice.cpp b/tests/auto/integration/qaudiodevice/tst_qaudiodevice.cpp
index af48c4f3a..cd686bd08 100644
--- a/tests/auto/integration/qaudiodevice/tst_qaudiodevice.cpp
+++ b/tests/auto/integration/qaudiodevice/tst_qaudiodevice.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -35,8 +10,6 @@
#include <QList>
#include <QMediaDevices>
-//TESTED_COMPONENT=src/multimedia
-
class tst_QAudioDevice : public QObject
{
Q_OBJECT
@@ -45,7 +18,6 @@ public:
private slots:
void initTestCase();
- void cleanupTestCase();
void checkAvailableDefaultInput();
void checkAvailableDefaultOutput();
void channels();
@@ -59,7 +31,7 @@ private slots:
void equalityOperator();
private:
- QAudioDevice* device;
+ std::unique_ptr<QAudioDevice> device;
};
void tst_QAudioDevice::initTestCase()
@@ -69,21 +41,18 @@ void tst_QAudioDevice::initTestCase()
if (devices.size() == 0) {
QSKIP("NOTE: no audio output device found, no tests will be performed");
} else {
- device = new QAudioDevice(devices.at(0));
+ device = std::make_unique<QAudioDevice>(devices.at(0));
}
}
-void tst_QAudioDevice::cleanupTestCase()
-{
- delete device;
-}
-
void tst_QAudioDevice::checkAvailableDefaultInput()
{
// Only perform tests if audio input device exists!
QList<QAudioDevice> devices = QMediaDevices::audioInputs();
if (devices.size() > 0) {
- QVERIFY(!QMediaDevices::defaultAudioInput().isNull());
+ auto defaultInput = QMediaDevices::defaultAudioInput();
+ QVERIFY(!defaultInput.isNull());
+ QCOMPARE(std::count(devices.begin(), devices.end(), defaultInput), 1);
}
}
@@ -92,7 +61,9 @@ void tst_QAudioDevice::checkAvailableDefaultOutput()
// Only perform tests if audio input device exists!
QList<QAudioDevice> devices = QMediaDevices::audioOutputs();
if (devices.size() > 0) {
- QVERIFY(!QMediaDevices::defaultAudioOutput().isNull());
+ auto defaultOutput = QMediaDevices::defaultAudioOutput();
+ QVERIFY(!defaultOutput.isNull());
+ QCOMPARE(std::count(devices.begin(), devices.end(), defaultOutput), 1);
}
}
diff --git a/tests/auto/integration/qaudiosink/BLACKLIST b/tests/auto/integration/qaudiosink/BLACKLIST
index 966b48af6..0b8789267 100644
--- a/tests/auto/integration/qaudiosink/BLACKLIST
+++ b/tests/auto/integration/qaudiosink/BLACKLIST
@@ -1 +1,11 @@
-linux ci
+#QTBUG-113194
+[pullSuspendResume]
+macos ci
+
+#QTBUG-113194
+[pushSuspendResume]
+macos ci
+
+#QTBUG-122309
+[pullResumeFromUnderrun]
+rhel-9.2
diff --git a/tests/auto/integration/qaudiosink/CMakeLists.txt b/tests/auto/integration/qaudiosink/CMakeLists.txt
index 26b1c50a2..902326dcc 100644
--- a/tests/auto/integration/qaudiosink/CMakeLists.txt
+++ b/tests/auto/integration/qaudiosink/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#####################################################################
## tst_qaudiosink Test:
#####################################################################
@@ -5,7 +8,7 @@
qt_internal_add_test(tst_qaudiosink
SOURCES
tst_qaudiosink.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/integration/qaudiosink/tst_qaudiosink.cpp b/tests/auto/integration/qaudiosink/tst_qaudiosink.cpp
index d957cecc7..6fdfe8221 100644
--- a/tests/auto/integration/qaudiosink/tst_qaudiosink.cpp
+++ b/tests/auto/integration/qaudiosink/tst_qaudiosink.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/qlocale.h>
@@ -75,6 +48,8 @@ private slots:
void pushSuspendResume_data(){generate_audiofile_testrows();}
void pushSuspendResume();
+ void pushResetResume();
+
void pushUnderrun_data(){generate_audiofile_testrows();}
void pushUnderrun();
@@ -84,8 +59,16 @@ private slots:
private:
using FilePtr = QSharedPointer<QFile>;
- QString formatToFileName(const QAudioFormat &format);
+ static QString formatToFileName(const QAudioFormat &format);
void createSineWaveData(const QAudioFormat &format, qint64 length, int sampleRate = 440);
+ static QString dumpStateSignalSpy(const QSignalSpy &stateSignalSpy);
+
+ static qint64 wavDataSize(QIODevice &input);
+
+ template<typename Checker>
+ static void pushDataToAudioSink(QAudioSink &sink, QIODevice &input, QIODevice &feed,
+ qint64 &allWritten, qint64 writtenLimit, Checker &&checker,
+ bool checkOnlyFirst = false);
void generate_audiofile_testrows();
@@ -100,10 +83,10 @@ private:
QString tst_QAudioSink::formatToFileName(const QAudioFormat &format)
{
- return QString("%1_%2_%3")
- .arg(format.sampleRate())
- .arg(format.bytesPerSample())
- .arg(format.channelCount());
+ return QStringLiteral("%1_%2_%3")
+ .arg(format.sampleRate())
+ .arg(format.bytesPerSample())
+ .arg(format.channelCount());
}
void tst_QAudioSink::createSineWaveData(const QAudioFormat &format, qint64 length, int sampleRate)
@@ -155,15 +138,69 @@ void tst_QAudioSink::createSineWaveData(const QAudioFormat &format, qint64 lengt
Q_ASSERT(m_buffer->open(QIODevice::ReadOnly));
}
+QString tst_QAudioSink::dumpStateSignalSpy(const QSignalSpy& stateSignalSpy) {
+ QString result = "[";
+ bool first = true;
+ for (auto& params : stateSignalSpy)
+ {
+ if (!std::exchange(first, false))
+ result += ',';
+ result += QString::number(params.front().value<QAudio::State>());
+ }
+ result.append(']');
+ return result;
+}
+
+qint64 tst_QAudioSink::wavDataSize(QIODevice &input)
+{
+ return input.size() - QWaveDecoder::headerLength();
+}
+
+template<typename Checker>
+void tst_QAudioSink::pushDataToAudioSink(QAudioSink &sink, QIODevice &input, QIODevice &feed,
+ qint64 &allWritten, qint64 writtenLimit, Checker &&checker,
+ bool checkOnlyFirst)
+{
+ bool firstBuffer = true;
+ qint64 offset = 0;
+ QByteArray buffer;
+
+ while ((allWritten < writtenLimit || writtenLimit < 0) && !input.atEnd()
+ && !QTest::currentTestFailed()) {
+ if (sink.bytesFree() > 0) {
+ if (buffer.isNull())
+ buffer = input.read(sink.bytesFree());
+
+ const auto written = feed.write(buffer);
+ allWritten += written;
+ offset += written;
+
+ if (offset >= buffer.size()) {
+ offset = 0;
+ buffer.clear();
+ }
+
+ if (!checkOnlyFirst || firstBuffer)
+ checker();
+
+ firstBuffer = false;
+ } else {
+ // wait a bit to ensure some the sink has consumed some data
+ // The delay getting might need some improvements
+ const auto delay = qMin(10, sink.format().durationForBytes(sink.bufferSize()) / 1000 / 2);
+ QTest::qWait(delay);
+ }
+ }
+}
+
void tst_QAudioSink::generate_audiofile_testrows()
{
QTest::addColumn<FilePtr>("audioFile");
QTest::addColumn<QAudioFormat>("audioFormat");
- for (int i=0; i<audioFiles.count(); i++) {
- QTest::newRow(QString("Audio File %1").arg(i).toUtf8().constData())
+ for (int i=0; i<audioFiles.size(); i++) {
+ QTest::newRow(QStringLiteral("Audio File %1").arg(i).toUtf8().constData())
<< audioFiles.at(i) << testFormats.at(i);
-
}
}
@@ -205,6 +242,13 @@ void tst_QAudioSink::initTestCase()
// PCM 44100 stereo S16LE
format.setSampleRate(44100);
if (audioDevice.isFormatSupported(format))
+#ifdef Q_OS_ANDROID
+ // Testset crash on emulator x86 with API 23 (Android 6) for 44,1 MHz.
+ // It is not happen on x86 with API 24. What is more, there is no crash when
+ // tested sample rate is 44,999 or any other value. Seems like problem on
+ // emulator side. Let's turn off this frequency for API 23
+ if (QNativeInterface::QAndroidApplication::sdkVersion() > __ANDROID_API_M__)
+#endif
testFormats.append(format);
// PCM 48000 stereo S16LE
@@ -224,7 +268,7 @@ void tst_QAudioSink::initTestCase()
QVERIFY(m_temporaryDir->isValid());
const QString temporaryAudioPath = m_temporaryDir->path() + slash;
- for (const QAudioFormat &format : qAsConst(testFormats)) {
+ for (const QAudioFormat &format : std::as_const(testFormats)) {
qint64 len = format.sampleRate()*format.bytesPerFrame(); // 1 second
createSineWaveData(format, len);
// Write generate sine wave data to file
@@ -250,11 +294,23 @@ void tst_QAudioSink::format()
QAudioFormat actual = audioOutput.format();
QVERIFY2((requested.channelCount() == actual.channelCount()),
- QString("channels: requested=%1, actual=%2").arg(requested.channelCount()).arg(actual.channelCount()).toUtf8().constData());
+ QStringLiteral("channels: requested=%1, actual=%2")
+ .arg(requested.channelCount())
+ .arg(actual.channelCount())
+ .toUtf8()
+ .constData());
QVERIFY2((requested.sampleRate() == actual.sampleRate()),
- QString("sampleRate: requested=%1, actual=%2").arg(requested.sampleRate()).arg(actual.sampleRate()).toUtf8().constData());
+ QStringLiteral("sampleRate: requested=%1, actual=%2")
+ .arg(requested.sampleRate())
+ .arg(actual.sampleRate())
+ .toUtf8()
+ .constData());
QVERIFY2((requested.sampleFormat() == actual.sampleFormat()),
- QString("sampleFormat: requested=%1, actual=%2").arg((ushort)requested.sampleFormat()).arg((ushort)actual.sampleFormat()).toUtf8().constData());
+ QStringLiteral("sampleFormat: requested=%1, actual=%2")
+ .arg((ushort)requested.sampleFormat())
+ .arg((ushort)actual.sampleFormat())
+ .toUtf8()
+ .constData());
QVERIFY(requested == actual);
}
@@ -314,12 +370,20 @@ void tst_QAudioSink::bufferSize()
QFETCH(int, bufferSize);
QAudioSink audioOutput(audioDevice.preferredFormat(), this);
- QVERIFY2((audioOutput.error() == QAudio::NoError), QString("error() was not set to QAudio::NoError on creation(%1)").arg(bufferSize).toUtf8().constData());
+ QVERIFY2((audioOutput.error() == QAudio::NoError),
+ QStringLiteral("error() was not set to QAudio::NoError on creation(%1)")
+ .arg(bufferSize)
+ .toUtf8()
+ .constData());
audioOutput.setBufferSize(bufferSize);
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize");
QVERIFY2((audioOutput.bufferSize() == bufferSize),
- QString("bufferSize: requested=%1, actual=%2").arg(bufferSize).arg(audioOutput.bufferSize()).toUtf8().constData());
+ QStringLiteral("bufferSize: requested=%1, actual=%2")
+ .arg(bufferSize)
+ .arg(audioOutput.bufferSize())
+ .toUtf8()
+ .constData());
}
void tst_QAudioSink::stopWhileStopped()
@@ -334,11 +398,11 @@ void tst_QAudioSink::stopWhileStopped()
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
audioOutput.stop();
// Check that no state transition occurred
- QVERIFY2((stateSignal.count() == 0), "stop() while stopped is emitting a signal and it shouldn't");
+ QVERIFY2((stateSignal.size() == 0), "stop() while stopped is emitting a signal and it shouldn't");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
}
@@ -354,11 +418,11 @@ void tst_QAudioSink::suspendWhileStopped()
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
audioOutput.suspend();
// Check that no state transition occurred
- QVERIFY2((stateSignal.count() == 0), "stop() while suspended is emitting a signal and it shouldn't");
+ QVERIFY2((stateSignal.size() == 0), "stop() while suspended is emitting a signal and it shouldn't");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
}
@@ -374,11 +438,11 @@ void tst_QAudioSink::resumeWhileStopped()
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
audioOutput.resume();
// Check that no state transition occurred
- QVERIFY2((stateSignal.count() == 0), "resume() while stopped is emitting a signal and it shouldn't");
+ QVERIFY2((stateSignal.size() == 0), "resume() while stopped is emitting a signal and it shouldn't");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after resume()");
}
@@ -391,7 +455,7 @@ void tst_QAudioSink::pull()
audioOutput.setVolume(0.1f);
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -405,8 +469,11 @@ void tst_QAudioSink::pull()
audioOutput.start(audioFile.data());
// Check that QAudioSink immediately transitions to ActiveState
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal on start(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
stateSignal.clear();
@@ -417,8 +484,8 @@ void tst_QAudioSink::pull()
// Wait until playback finishes
QTRY_VERIFY2(audioFile->atEnd(), "didn't play to EOF");
- QTRY_VERIFY(stateSignal.count() > 0);
- QCOMPARE(qvariant_cast<QAudio::State>(stateSignal.last().at(0)), QAudio::IdleState);
+ QTRY_VERIFY(stateSignal.size() > 0);
+ QTRY_COMPARE(qvariant_cast<QAudio::State>(stateSignal.last().at(0)), QAudio::IdleState);
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
stateSignal.clear();
@@ -426,8 +493,11 @@ void tst_QAudioSink::pull()
audioOutput.stop();
QTest::qWait(40);
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
@@ -444,7 +514,7 @@ void tst_QAudioSink::pullSuspendResume()
audioOutput.setVolume(0.1f);
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -457,21 +527,29 @@ void tst_QAudioSink::pullSuspendResume()
audioOutput.start(audioFile.data());
// Check that QAudioSink immediately transitions to ActiveState
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal on start(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
- QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
- stateSignal.clear();
+ QVERIFY2((audioOutput.error() == QAudio::NoError),
+ "error state is not equal to QAudio::NoError after start()");
+ stateSignal.clear();
// Wait for half of clip to play
QTest::qWait(500);
audioOutput.suspend();
QTest::qWait(100);
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead")
- .arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2(
+ (stateSignal.size() == 1),
+ QStringLiteral(
+ "didn't emit SuspendedState signal after suspend(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::SuspendedState), "didn't transition to SuspendedState after suspend()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after suspend()");
stateSignal.clear();
@@ -486,16 +564,19 @@ void tst_QAudioSink::pullSuspendResume()
audioOutput.resume();
// Check that QAudioSink immediately transitions to ActiveState
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal after resume(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal after resume(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
stateSignal.clear();
// Wait until playback finishes
QTRY_VERIFY2(audioFile->atEnd(), "didn't play to EOF");
- QTRY_VERIFY(stateSignal.count() > 0);
- QCOMPARE(qvariant_cast<QAudio::State>(stateSignal.last().at(0)), QAudio::IdleState);
+ QTRY_VERIFY(stateSignal.size() > 0);
+ QTRY_COMPARE(qvariant_cast<QAudio::State>(stateSignal.last().at(0)), QAudio::IdleState);
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
stateSignal.clear();
@@ -503,8 +584,11 @@ void tst_QAudioSink::pullSuspendResume()
audioOutput.stop();
QTest::qWait(40);
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
@@ -513,68 +597,71 @@ void tst_QAudioSink::pullSuspendResume()
audioFile->close();
}
-class AudioPullSource : public QIODevice
+void tst_QAudioSink::pullResumeFromUnderrun()
{
- Q_OBJECT
-public:
- qint64 readData(char *data, qint64 len) override {
- qint64 read = qMin(len, available);
- available -= read;
- memset(data, 0, read);
- return read;
- }
- qint64 writeData(const char *, qint64) override { return 0; }
- bool isSequential() const override { return true; }
+ class AudioPullSource : public QIODevice
+ {
+ public:
+ qint64 readData(char *data, qint64 len) override {
+ qint64 read = qMin(len, available);
+ available -= read;
+ memset(data, 0, read);
+ return read;
+ }
+ qint64 writeData(const char *, qint64) override { return 0; }
+ bool isSequential() const override { return true; }
- qint64 bytesAvailable() const override { return available; }
- bool atEnd() const override { return signalEnd && available == 0; }
+ qint64 bytesAvailable() const override { return available; }
+ bool atEnd() const override { return signalEnd && available == 0; }
- qint64 available = 0;
- bool signalEnd = false;
-};
+ qint64 available = 0;
+ bool signalEnd = false;
+ };
+
+ constexpr int chunkSize = 128;
-void tst_QAudioSink::pullResumeFromUnderrun()
-{
- AudioPullSource audioSource;
QAudioFormat format;
format.setChannelCount(1);
format.setSampleFormat(QAudioFormat::UInt8);
- format.setSampleRate(1024);
- QAudioSink audioOutput(format, this);
+ format.setSampleRate(8000);
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ AudioPullSource audioSource;
+ QAudioSink audioOutput(format, this);
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
audioSource.open(QIODeviceBase::ReadOnly);
- audioSource.available = 128;
+ audioSource.available = chunkSize;
+ QCOMPARE(audioOutput.state(), QAudio::StoppedState);
audioOutput.start(&audioSource);
- QTRY_VERIFY(stateSignal.count() == 1);
- QVERIFY(audioOutput.state() == QAudio::ActiveState);
- QVERIFY(audioOutput.error() == QAudio::NoError);
+ QTRY_COMPARE(stateSignal.size(), 1);
+ QCOMPARE(audioOutput.state(), QAudio::ActiveState);
+ QCOMPARE(audioOutput.error(), QAudio::NoError);
stateSignal.clear();
- QTRY_VERIFY(stateSignal.count() == 1);
- QVERIFY(audioOutput.state() == QAudio::IdleState);
- QVERIFY(audioOutput.error() == QAudio::UnderrunError);
+ QTRY_COMPARE(stateSignal.size(), 1);
+ QCOMPARE(audioOutput.state(), QAudio::IdleState);
+ QCOMPARE(audioOutput.error(), QAudio::UnderrunError);
stateSignal.clear();
QTest::qWait(300);
- audioSource.available = 128;
+ audioSource.available = chunkSize;
audioSource.signalEnd = true;
// Resume pull
emit audioSource.readyRead();
- QTRY_VERIFY(stateSignal.count() == 1);
- QVERIFY(audioOutput.state() == QAudio::ActiveState);
- QVERIFY(audioOutput.error() == QAudio::NoError);
- stateSignal.clear();
+ QTRY_COMPARE(stateSignal.size(), 2);
+ QCOMPARE(stateSignal.at(0).front().value<QAudio::State>(), QAudio::ActiveState);
+ QCOMPARE(stateSignal.at(1).front().value<QAudio::State>(), QAudio::IdleState);
- QTRY_VERIFY(stateSignal.count() == 1);
- QVERIFY(audioOutput.state() == QAudio::IdleState);
- QVERIFY(audioOutput.error() == QAudio::NoError);
+ QCOMPARE(audioOutput.error(), QAudio::NoError);
+ QCOMPARE(audioOutput.state(), QAudio::IdleState);
- QTRY_COMPARE(audioOutput.processedUSecs(), 250000);
+ // we played two chunks, sample rate is per second
+ const int expectedUSecs = (double(chunkSize) / double(format.sampleRate()))
+ * 2 * 1000 * 1000;
+ QTRY_COMPARE(audioOutput.processedUSecs(), expectedUSecs);
}
void tst_QAudioSink::push()
@@ -586,7 +673,7 @@ void tst_QAudioSink::push()
audioOutput.setVolume(0.1f);
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -600,8 +687,11 @@ void tst_QAudioSink::push()
QIODevice* feed = audioOutput.start();
// Check that QAudioSink immediately transitions to IdleState
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal on start(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
stateSignal.clear();
@@ -612,33 +702,29 @@ void tst_QAudioSink::push()
QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
qint64 written = 0;
- bool firstBuffer = true;
- while (written < audioFile->size() - QWaveDecoder::headerLength()) {
-
- if (audioOutput.bytesFree() > 0) {
- auto buffer = audioFile->read(audioOutput.bytesFree());
- written += feed->write(buffer);
-
- if (firstBuffer) {
- // Check for transition to ActiveState when data is provided
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal after receiving data, got %1 signals instead")
- .arg(stateSignal.count()).toUtf8().constData());
- QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
- QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
- firstBuffer = false;
- stateSignal.clear();
- }
- } else
- QTest::qWait(20);
- }
+ auto checker = [&]() {
+ // Check for transition to ActiveState when data is provided
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal after receiving data, got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState),
+ "didn't transition to ActiveState after receiving data");
+ QVERIFY2((audioOutput.error() == QAudio::NoError),
+ "error state is not equal to QAudio::NoError after receiving data");
+ stateSignal.clear();
+ };
+
+ pushDataToAudioSink(audioOutput, *audioFile, *feed, written, wavDataSize(*audioFile), checker,
+ true);
// Wait until playback finishes
QVERIFY2(audioFile->atEnd(), "didn't play to EOF");
QTRY_VERIFY(audioOutput.state() == QAudio::IdleState);
- QTRY_VERIFY(stateSignal.count() > 0);
- QCOMPARE(qvariant_cast<QAudio::State>(stateSignal.last().at(0)), QAudio::IdleState);
+ QTRY_VERIFY(stateSignal.size() > 0);
+ QTRY_COMPARE(qvariant_cast<QAudio::State>(stateSignal.last().at(0)), QAudio::IdleState);
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
stateSignal.clear();
@@ -646,8 +732,11 @@ void tst_QAudioSink::push()
audioOutput.stop();
QTest::qWait(40);
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
@@ -665,7 +754,7 @@ void tst_QAudioSink::pushSuspendResume()
audioOutput.setVolume(0.1f);
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -679,8 +768,11 @@ void tst_QAudioSink::pushSuspendResume()
QIODevice* feed = audioOutput.start();
// Check that QAudioSink immediately transitions to IdleState
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal on start(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
stateSignal.clear();
@@ -690,35 +782,35 @@ void tst_QAudioSink::pushSuspendResume()
QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
- qint64 written = 0;
- bool firstBuffer = true;
+ auto firstHalfChecker = [&]() {
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal after receiving data, got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState),
+ "didn't transition to ActiveState after receiving data");
+ QVERIFY2((audioOutput.error() == QAudio::NoError),
+ "error state is not equal to QAudio::NoError after receiving data");
+ };
+ qint64 written = 0;
// Play half of the clip
- while (written < (audioFile->size() - QWaveDecoder::headerLength()) / 2) {
-
- if (audioOutput.bytesFree() > 0) {
- auto buffer = audioFile->read(audioOutput.bytesFree());
- written += feed->write(buffer);
-
- if (firstBuffer) {
- // Check for transition to ActiveState when data is provided
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal after receiving data, got %1 signals instead")
- .arg(stateSignal.count()).toUtf8().constData());
- QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
- QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
- firstBuffer = false;
- }
- } else
- QTest::qWait(20);
- }
+ pushDataToAudioSink(audioOutput, *audioFile, *feed, written, wavDataSize(*audioFile) / 2,
+ firstHalfChecker, true);
+
stateSignal.clear();
+ const auto suspendedInState = audioOutput.state();
audioOutput.suspend();
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead")
- .arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2(
+ (stateSignal.size() == 1),
+ QStringLiteral(
+ "didn't emit SuspendedState signal after suspend(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::SuspendedState), "didn't transition to SuspendedState after suspend()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after suspend()");
stateSignal.clear();
@@ -737,27 +829,30 @@ void tst_QAudioSink::pushSuspendResume()
QTest::qWait(20);
// Check that QAudioSink immediately transitions to IdleState
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal after resume(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
- QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after resume()");
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal after resume(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
+ QVERIFY2((audioOutput.state() == suspendedInState), "resume() didn't transition to state before suspend()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
stateSignal.clear();
// Play rest of the clip
- while (!audioFile->atEnd()) {
- if (audioOutput.bytesFree() > 0) {
- auto buffer = audioFile->read(audioOutput.bytesFree());
- written += feed->write(buffer);
- QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after writing audio data");
- } else
- QTest::qWait(20);
- }
+
+ auto restChecker = [&]() {
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState),
+ "didn't transition to ActiveState after writing audio data");
+ };
+
+ pushDataToAudioSink(audioOutput, *audioFile, *feed, written, -1, restChecker);
+
QVERIFY(audioOutput.state() != QAudio::IdleState);
stateSignal.clear();
QVERIFY2(audioFile->atEnd(), "didn't play to EOF");
- QTRY_VERIFY(stateSignal.count() > 0);
- QCOMPARE(qvariant_cast<QAudio::State>(stateSignal.last().at(0)), QAudio::IdleState);
+ QTRY_VERIFY(stateSignal.size() > 0);
+ QTRY_COMPARE(qvariant_cast<QAudio::State>(stateSignal.last().at(0)), QAudio::IdleState);
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
stateSignal.clear();
@@ -765,8 +860,11 @@ void tst_QAudioSink::pushSuspendResume()
audioOutput.stop();
QTest::qWait(40);
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
@@ -775,6 +873,46 @@ void tst_QAudioSink::pushSuspendResume()
audioFile->close();
}
+void tst_QAudioSink::pushResetResume()
+{
+ auto audioFile = audioFiles.at(0);
+ auto audioFormat = testFormats.at(0);
+
+ QAudioSink audioOutput(audioFormat, this);
+
+ audioOutput.setBufferSize(8192);
+ audioOutput.setVolume(0.1f);
+
+ audioFile->close();
+ audioFile->open(QIODevice::ReadOnly);
+ audioFile->seek(QWaveDecoder::headerLength());
+
+ QPointer<QIODevice> feed = audioOutput.start();
+
+ QTest::qWait(20);
+
+ auto buffer = audioFile->read(audioOutput.bytesFree());
+ feed->write(buffer);
+
+ QTest::qWait(20);
+ QTRY_COMPARE(audioOutput.state(), QAudio::ActiveState);
+
+ audioOutput.reset();
+ QCOMPARE(audioOutput.state(), QAudio::StoppedState);
+ QCOMPARE(audioOutput.error(), QAudio::NoError);
+
+ const auto processedUSecs = audioOutput.processedUSecs();
+
+ audioOutput.resume();
+ QTest::qWait(40);
+
+ // Nothing changed if resume after reset
+ QCOMPARE(audioOutput.state(), QAudio::StoppedState);
+ QCOMPARE(audioOutput.error(), QAudio::NoError);
+
+ QCOMPARE(audioOutput.processedUSecs(), processedUSecs);
+}
+
void tst_QAudioSink::pushUnderrun()
{
QFETCH(FilePtr, audioFile);
@@ -784,7 +922,7 @@ void tst_QAudioSink::pushUnderrun()
audioOutput.setVolume(0.1f);
- QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -798,8 +936,11 @@ void tst_QAudioSink::pushUnderrun()
QIODevice* feed = audioOutput.start();
// Check that QAudioSink immediately transitions to IdleState
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal on start(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
stateSignal.clear();
@@ -810,64 +951,61 @@ void tst_QAudioSink::pushUnderrun()
QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
qint64 written = 0;
- bool firstBuffer = true;
- QByteArray buffer(AUDIO_BUFFER, 0);
// Play half of the clip
- while (written < (audioFile->size() - QWaveDecoder::headerLength()) / 2) {
-
- if (audioOutput.bytesFree() > 0) {
- auto buffer = audioFile->read(audioOutput.bytesFree());
- written += feed->write(buffer);
-
- if (firstBuffer) {
- // Check for transition to ActiveState when data is provided
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal after receiving data, got %1 signals instead")
- .arg(stateSignal.count()).toUtf8().constData());
- QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
- QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
- firstBuffer = false;
- }
- } else
- QTest::qWait(20);
- }
+
+ auto firstHalfChecker = [&]() {
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal after receiving data, got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState),
+ "didn't transition to ActiveState after receiving data");
+ QVERIFY2((audioOutput.error() == QAudio::NoError),
+ "error state is not equal to QAudio::NoError after receiving data");
+ };
+
+ pushDataToAudioSink(audioOutput, *audioFile, *feed, written, wavDataSize(*audioFile) / 2,
+ firstHalfChecker, true);
+
stateSignal.clear();
// Wait for data to be played
QTest::qWait(700);
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit IdleState signal after suspend(), got %1 signals instead")
- .arg(stateSignal.count()).toUtf8().constData());
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit IdleState signal after suspend(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState, no data");
QVERIFY2((audioOutput.error() == QAudio::UnderrunError), "error state is not equal to QAudio::UnderrunError, no data");
stateSignal.clear();
- firstBuffer = true;
// Play rest of the clip
- while (!audioFile->atEnd()) {
- if (audioOutput.bytesFree() > 0) {
- auto buffer = audioFile->read(audioOutput.bytesFree());
- written += feed->write(buffer);
- if (firstBuffer) {
- // Check for transition to ActiveState when data is provided
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal after receiving data, got %1 signals instead")
- .arg(stateSignal.count()).toUtf8().constData());
- QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
- QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
- firstBuffer = false;
- }
- } else
- QTest::qWait(20);
- }
+ auto restChecker = [&]() {
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal after receiving data, got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState),
+ "didn't transition to ActiveState after receiving data");
+ QVERIFY2((audioOutput.error() == QAudio::NoError),
+ "error state is not equal to QAudio::NoError after receiving data");
+ };
+ pushDataToAudioSink(audioOutput, *audioFile, *feed, written, -1, restChecker, true);
+
stateSignal.clear();
// Wait until playback finishes
QVERIFY2(audioFile->atEnd(), "didn't play to EOF");
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit IdleState signal when at EOF, got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
stateSignal.clear();
@@ -875,8 +1013,11 @@ void tst_QAudioSink::pushUnderrun()
audioOutput.stop();
QTest::qWait(40);
- QVERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QVERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(dumpStateSignalSpy(stateSignal))
+ .toUtf8()
+ .constData());
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
diff --git a/tests/auto/integration/qaudiosource/CMakeLists.txt b/tests/auto/integration/qaudiosource/CMakeLists.txt
index e9026e963..0e572e804 100644
--- a/tests/auto/integration/qaudiosource/CMakeLists.txt
+++ b/tests/auto/integration/qaudiosource/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#####################################################################
## tst_qaudiosource Test:
#####################################################################
@@ -5,7 +8,7 @@
qt_internal_add_test(tst_qaudiosource
SOURCES
tst_qaudiosource.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/integration/qaudiosource/tst_qaudiosource.cpp b/tests/auto/integration/qaudiosource/tst_qaudiosource.cpp
index d343fb8a8..ae100a08b 100644
--- a/tests/auto/integration/qaudiosource/tst_qaudiosource.cpp
+++ b/tests/auto/integration/qaudiosource/tst_qaudiosource.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/qlocale.h>
@@ -40,8 +15,6 @@
#include <qwavedecoder.h>
-//TESTED_COMPONENT=src/multimedia
-
#define RANGE_ERR 0.5
template<typename T> inline bool qTolerantCompare(T value, T expected)
@@ -101,7 +74,7 @@ private:
QScopedPointer<QByteArray> m_byteArray;
QScopedPointer<QBuffer> m_buffer;
- bool m_inCISystem;
+ bool m_inCISystem = false;
};
void tst_QAudioSource::generate_audiofile_testrows()
@@ -109,8 +82,8 @@ void tst_QAudioSource::generate_audiofile_testrows()
QTest::addColumn<FilePtr>("audioFile");
QTest::addColumn<QAudioFormat>("audioFormat");
- for (int i=0; i<audioFiles.count(); i++) {
- QTest::newRow(QString("%1").arg(i).toUtf8().constData())
+ for (int i=0; i<audioFiles.size(); i++) {
+ QTest::newRow(QStringLiteral("%1").arg(i).toUtf8().constData())
<< audioFiles.at(i) << testFormats.at(i);
// Only run first format in CI system to reduce test times
@@ -121,16 +94,30 @@ void tst_QAudioSource::generate_audiofile_testrows()
QString tst_QAudioSource::formatToFileName(const QAudioFormat &format)
{
- return QString("%1_%2_%3")
- .arg(format.sampleRate())
- .arg(format.bytesPerSample())
- .arg(format.channelCount());
+ return QStringLiteral("%1_%2_%3")
+ .arg(format.sampleRate())
+ .arg(format.bytesPerSample())
+ .arg(format.channelCount());
}
void tst_QAudioSource::initTestCase()
{
- // Only perform tests if audio output device exists
- const QList<QAudioDevice> devices = QMediaDevices::audioOutputs();
+#ifdef Q_OS_ANDROID
+ // The test might fail because libOpenSLES cannot create AudioRecorder for that emulator. The
+ // Android documentation states that the emulator doesn't support this at all all
+ // https://developer.android.com/media/platform/mediarecorder. However, in practice this test
+ // fails only prior to Android 10.
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < __ANDROID_API_Q__)
+ QSKIP("Emulated Android version doesn't support audio recording");
+#endif
+
+ m_inCISystem = qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci";
+
+ if (m_inCISystem)
+ QSKIP("SKIP initTestCase on CI. To be fixed");
+
+ // Only perform tests if audio input device exists
+ const QList<QAudioDevice> devices = QMediaDevices::audioInputs();
if (devices.size() <= 0)
QSKIP("No audio backend");
@@ -184,11 +171,10 @@ void tst_QAudioSource::initTestCase()
QVERIFY(m_temporaryDir->isValid());
const QString temporaryAudioPath = m_temporaryDir->path() + slash;
- for (const QAudioFormat &format : qAsConst(testFormats)) {
+ for (const QAudioFormat &format : std::as_const(testFormats)) {
const QString fileName = temporaryAudioPath + formatToFileName(format) + QStringLiteral(".wav");
audioFiles.append(FilePtr::create(fileName));
}
- qgetenv("QT_TEST_CI").toInt(&m_inCISystem,10);
}
void tst_QAudioSource::format()
@@ -199,11 +185,23 @@ void tst_QAudioSource::format()
QAudioFormat actual = audioInput.format();
QVERIFY2((requested.channelCount() == actual.channelCount()),
- QString("channels: requested=%1, actual=%2").arg(requested.channelCount()).arg(actual.channelCount()).toUtf8().constData());
+ QStringLiteral("channels: requested=%1, actual=%2")
+ .arg(requested.channelCount())
+ .arg(actual.channelCount())
+ .toUtf8()
+ .constData());
QVERIFY2((requested.sampleRate() == actual.sampleRate()),
- QString("sampleRate: requested=%1, actual=%2").arg(requested.sampleRate()).arg(actual.sampleRate()).toUtf8().constData());
+ QStringLiteral("sampleRate: requested=%1, actual=%2")
+ .arg(requested.sampleRate())
+ .arg(actual.sampleRate())
+ .toUtf8()
+ .constData());
QVERIFY2((requested.sampleFormat() == actual.sampleFormat()),
- QString("sampleFormat: requested=%1, actual=%2").arg((ushort)requested.sampleFormat()).arg((ushort)actual.sampleFormat()).toUtf8().constData());
+ QStringLiteral("sampleFormat: requested=%1, actual=%2")
+ .arg((ushort)requested.sampleFormat())
+ .arg((ushort)actual.sampleFormat())
+ .toUtf8()
+ .constData());
QCOMPARE(actual, requested);
}
@@ -260,17 +258,26 @@ void tst_QAudioSource::bufferSize()
audioInput.setBufferSize(512);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(512)");
QVERIFY2((audioInput.bufferSize() == 512),
- QString("bufferSize: requested=512, actual=%2").arg(audioInput.bufferSize()).toUtf8().constData());
+ QStringLiteral("bufferSize: requested=512, actual=%2")
+ .arg(audioInput.bufferSize())
+ .toUtf8()
+ .constData());
audioInput.setBufferSize(4096);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(4096)");
QVERIFY2((audioInput.bufferSize() == 4096),
- QString("bufferSize: requested=4096, actual=%2").arg(audioInput.bufferSize()).toUtf8().constData());
+ QStringLiteral("bufferSize: requested=4096, actual=%2")
+ .arg(audioInput.bufferSize())
+ .toUtf8()
+ .constData());
audioInput.setBufferSize(8192);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(8192)");
QVERIFY2((audioInput.bufferSize() == 8192),
- QString("bufferSize: requested=8192, actual=%2").arg(audioInput.bufferSize()).toUtf8().constData());
+ QStringLiteral("bufferSize: requested=8192, actual=%2")
+ .arg(audioInput.bufferSize())
+ .toUtf8()
+ .constData());
}
void tst_QAudioSource::stopWhileStopped()
@@ -285,11 +292,11 @@ void tst_QAudioSource::stopWhileStopped()
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
audioInput.stop();
// Check that no state transition occurred
- QVERIFY2((stateSignal.count() == 0), "stop() while stopped is emitting a signal and it shouldn't");
+ QVERIFY2((stateSignal.size() == 0), "stop() while stopped is emitting a signal and it shouldn't");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
}
@@ -305,11 +312,11 @@ void tst_QAudioSource::suspendWhileStopped()
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
audioInput.suspend();
// Check that no state transition occurred
- QVERIFY2((stateSignal.count() == 0), "stop() while suspended is emitting a signal and it shouldn't");
+ QVERIFY2((stateSignal.size() == 0), "stop() while suspended is emitting a signal and it shouldn't");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
}
@@ -325,11 +332,11 @@ void tst_QAudioSource::resumeWhileStopped()
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
audioInput.resume();
// Check that no state transition occurred
- QVERIFY2((stateSignal.count() == 0), "resume() while stopped is emitting a signal and it shouldn't");
+ QVERIFY2((stateSignal.size() == 0), "resume() while stopped is emitting a signal and it shouldn't");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after resume()");
}
@@ -340,7 +347,7 @@ void tst_QAudioSource::pull()
QAudioSource audioInput(audioFormat, this);
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -360,7 +367,7 @@ void tst_QAudioSource::pull()
audioInput.start(audioFile.data());
// Check that QAudioSource immediately transitions to ActiveState or IdleState
- QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on start()");
+ QTRY_VERIFY2((stateSignal.size() > 0),"didn't emit signals on start()");
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
"didn't transition to ActiveState or IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
@@ -371,17 +378,25 @@ void tst_QAudioSource::pull()
QTRY_VERIFY2((audioInput.processedUSecs() > 0), "elapsedUSecs() is still zero after start()");
// Allow some recording to happen
- QTest::qWait(300); // .3 seconds should be plenty
+ QTest::qWait(3000); // 3 seconds should be plenty
stateSignal.clear();
qint64 processedUs = audioInput.processedUSecs();
- QVERIFY2(qTolerantCompare(processedUs, 300000LL),
- QString("processedUSecs() doesn't fall in acceptable range, should be 300000 (%1)").arg(processedUs).toUtf8().constData());
+ QVERIFY2(qTolerantCompare(processedUs, 3000000LL),
+ QStringLiteral(
+ "processedUSecs() doesn't fall in acceptable range, should be 3000000 (%1)")
+ .arg(processedUs)
+ .toUtf8()
+ .constData());
audioInput.stop();
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2(
+ (stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(stateSignal.size())
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
@@ -396,16 +411,12 @@ void tst_QAudioSource::pull()
void tst_QAudioSource::pullSuspendResume()
{
-#ifdef Q_OS_LINUX
- if (m_inCISystem)
- QSKIP("QTBUG-26504 Fails 20% of time with pulseaudio backend");
-#endif
QFETCH(FilePtr, audioFile);
QFETCH(QAudioFormat, audioFormat);
QAudioSource audioInput(audioFormat, this);
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -425,7 +436,7 @@ void tst_QAudioSource::pullSuspendResume()
audioInput.start(audioFile.data());
// Check that QAudioSource immediately transitions to ActiveState or IdleState
- QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on start()");
+ QTRY_VERIFY2((stateSignal.size() > 0),"didn't emit signals on start()");
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
"didn't transition to ActiveState or IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
@@ -436,7 +447,7 @@ void tst_QAudioSource::pullSuspendResume()
QTRY_VERIFY2((audioInput.processedUSecs() > 0), "elapsedUSecs() is still zero after start()");
// Allow some recording to happen
- QTest::qWait(300); // .3 seconds should be plenty
+ QTest::qWait(3000); // 3 seconds should be plenty
QVERIFY2((audioInput.state() == QAudio::ActiveState),
"didn't transition to ActiveState after some recording");
@@ -446,8 +457,13 @@ void tst_QAudioSource::pullSuspendResume()
audioInput.suspend();
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2(
+ (stateSignal.size() == 1),
+ QStringLiteral(
+ "didn't emit SuspendedState signal after suspend(), got %1 signals instead")
+ .arg(stateSignal.size())
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.state() == QAudio::SuspendedState), "didn't transitions to SuspendedState after stop()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
stateSignal.clear();
@@ -455,24 +471,35 @@ void tst_QAudioSource::pullSuspendResume()
// Check that only 'elapsed', and not 'processed' increases while suspended
qint64 elapsedUs = audioInput.elapsedUSecs();
qint64 processedUs = audioInput.processedUSecs();
- QVERIFY2(qTolerantCompare(processedUs, 300000LL),
- QString("processedUSecs() doesn't fall in acceptable range, should be 300000 (%1)").arg(processedUs).toUtf8().constData());
+ QVERIFY2(qTolerantCompare(processedUs, 3000000LL),
+ QStringLiteral(
+ "processedUSecs() doesn't fall in acceptable range, should be 3000000 (%1)")
+ .arg(processedUs)
+ .toUtf8()
+ .constData());
QTRY_VERIFY(audioInput.elapsedUSecs() > elapsedUs);
QVERIFY(audioInput.processedUSecs() == processedUs);
audioInput.resume();
// Check that QAudioSource immediately transitions to ActiveState
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit signal after resume(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2((stateSignal.size() == 1),
+ QStringLiteral("didn't emit signal after resume(), got %1 signals instead")
+ .arg(stateSignal.size())
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
stateSignal.clear();
audioInput.stop();
QTest::qWait(40);
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2(
+ (stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(stateSignal.size())
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
@@ -491,7 +518,7 @@ void tst_QAudioSource::push()
QAudioSource audioInput(audioFormat, this);
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -514,7 +541,7 @@ void tst_QAudioSource::push()
QIODevice* feed = audioInput.start();
// Check that QAudioSource immediately transitions to IdleState
- QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
+ QTRY_VERIFY2((stateSignal.size() == 1),"didn't emit IdleState signal on start()");
QVERIFY2((audioInput.state() == QAudio::IdleState),
"didn't transition to IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
@@ -534,7 +561,7 @@ void tst_QAudioSource::push()
totalBytesRead += buffer.size();
if (firstBuffer && buffer.size()) {
// Check for transition to ActiveState when data is provided
- QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit ActiveState signal on data");
+ QTRY_VERIFY2((stateSignal.size() == 1),"didn't emit ActiveState signal on data");
QVERIFY2((audioInput.state() == QAudio::ActiveState),
"didn't transition to ActiveState after data");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
@@ -547,12 +574,20 @@ void tst_QAudioSource::push()
qint64 processedUs = audioInput.processedUSecs();
audioInput.stop();
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2(
+ (stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(stateSignal.size())
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2(qTolerantCompare(processedUs, 500000LL),
- QString("processedUSecs() doesn't fall in acceptable range, should be 500000 (%1)").arg(processedUs).toUtf8().constData());
+ QStringLiteral(
+ "processedUSecs() doesn't fall in acceptable range, should be 500000 (%1)")
+ .arg(processedUs)
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
@@ -574,7 +609,7 @@ void tst_QAudioSource::pushSuspendResume()
audioInput.setBufferSize(audioFormat.bytesForDuration(100000));
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -594,7 +629,7 @@ void tst_QAudioSource::pushSuspendResume()
QIODevice* feed = audioInput.start();
// Check that QAudioSource immediately transitions to IdleState
- QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
+ QTRY_VERIFY2((stateSignal.size() == 1),"didn't emit IdleState signal on start()");
QVERIFY2((audioInput.state() == QAudio::IdleState),
"didn't transition to IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
@@ -613,7 +648,7 @@ void tst_QAudioSource::pushSuspendResume()
totalBytesRead += buffer.size();
if (firstBuffer && buffer.size()) {
// Check for transition to ActiveState when data is provided
- QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit ActiveState signal on data");
+ QTRY_VERIFY2((stateSignal.size() == 1),"didn't emit ActiveState signal on data");
QVERIFY2((audioInput.state() == QAudio::ActiveState),
"didn't transition to ActiveState after data");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
@@ -624,8 +659,13 @@ void tst_QAudioSource::pushSuspendResume()
audioInput.suspend();
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2(
+ (stateSignal.size() == 1),
+ QStringLiteral(
+ "didn't emit SuspendedState signal after suspend(), got %1 signals instead")
+ .arg(stateSignal.size())
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.state() == QAudio::SuspendedState), "didn't transitions to SuspendedState after stop()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
stateSignal.clear();
@@ -644,7 +684,7 @@ void tst_QAudioSource::pushSuspendResume()
audioInput.resume();
// Check that QAudioSource immediately transitions to Active or IdleState
- QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on resume()");
+ QTRY_VERIFY2((stateSignal.size() > 0),"didn't emit signals on resume()");
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
"didn't transition to ActiveState or IdleState after resume()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
@@ -665,12 +705,20 @@ void tst_QAudioSource::pushSuspendResume()
processedUs = audioInput.processedUSecs();
audioInput.stop();
- QTRY_VERIFY2((stateSignal.count() == 1),
- QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toUtf8().constData());
+ QTRY_VERIFY2(
+ (stateSignal.size() == 1),
+ QStringLiteral("didn't emit StoppedState signal after stop(), got %1 signals instead")
+ .arg(stateSignal.size())
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2(qTolerantCompare(processedUs, 1000000LL),
- QString("processedUSecs() doesn't fall in acceptable range, should be 2040000 (%1)").arg(processedUs).toUtf8().constData());
+ QStringLiteral(
+ "processedUSecs() doesn't fall in acceptable range, should be 2040000 (%1)")
+ .arg(processedUs)
+ .toUtf8()
+ .constData());
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
//WavHeader::writeDataLength(*audioFile,audioFile->pos()-WavHeader::headerLength());
@@ -687,7 +735,7 @@ void tst_QAudioSource::reset()
{
QAudioSource audioInput(audioFormat, this);
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -696,7 +744,7 @@ void tst_QAudioSource::reset()
QIODevice* device = audioInput.start();
// Check that QAudioSource immediately transitions to IdleState
- QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
+ QTRY_VERIFY2((stateSignal.size() == 1),"didn't emit IdleState signal on start()");
QVERIFY2((audioInput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
QTRY_VERIFY2_WITH_TIMEOUT((audioInput.bytesAvailable() > 0), "no bytes available after starting", 10000);
@@ -707,7 +755,7 @@ void tst_QAudioSource::reset()
stateSignal.clear();
audioInput.reset();
- QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit StoppedState signal after reset()");
+ QTRY_VERIFY2((stateSignal.size() == 1),"didn't emit StoppedState signal after reset()");
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after reset()");
QVERIFY2((audioInput.bytesAvailable() == 0), "buffer not cleared after reset()");
}
@@ -717,7 +765,7 @@ void tst_QAudioSource::reset()
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
- QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
+ QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged);
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
@@ -727,13 +775,13 @@ void tst_QAudioSource::reset()
audioInput.start(&buffer);
// Check that QAudioSource immediately transitions to ActiveState
- QTRY_VERIFY2((stateSignal.count() >= 1),"didn't emit state changed signal on start()");
+ QTRY_VERIFY2((stateSignal.size() >= 1),"didn't emit state changed signal on start()");
QTRY_VERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
stateSignal.clear();
audioInput.reset();
- QTRY_VERIFY2((stateSignal.count() >= 1),"didn't emit StoppedState signal after reset()");
+ QTRY_VERIFY2((stateSignal.size() >= 1),"didn't emit StoppedState signal after reset()");
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after reset()");
QVERIFY2((audioInput.bytesAvailable() == 0), "buffer not cleared after reset()");
}
diff --git a/tests/auto/integration/qcamerabackend/BLACKLIST b/tests/auto/integration/qcamerabackend/BLACKLIST
index 0521c9fbd..e958c2103 100644
--- a/tests/auto/integration/qcamerabackend/BLACKLIST
+++ b/tests/auto/integration/qcamerabackend/BLACKLIST
@@ -1,3 +1,4 @@
+ci
[testCameraCaptureMetadata]
osx
diff --git a/tests/auto/integration/qcamerabackend/CMakeLists.txt b/tests/auto/integration/qcamerabackend/CMakeLists.txt
index c89456e8a..07a6a4cae 100644
--- a/tests/auto/integration/qcamerabackend/CMakeLists.txt
+++ b/tests/auto/integration/qcamerabackend/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qcamerabackend.pro.
#####################################################################
@@ -7,14 +10,17 @@
qt_internal_add_test(tst_qcamerabackend
SOURCES
tst_qcamerabackend.cpp
- PUBLIC_LIBRARIES
+ ../shared/mediabackendutils.h
+ INCLUDE_DIRECTORIES
+ ../shared/
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
Qt::Widgets
)
# special case begin
-if (APPLE)
+if(APPLE)
set_property(TARGET tst_qcamerabackend PROPERTY MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist")
set_property(TARGET tst_qcamerabackend PROPERTY PROPERTY MACOSX_BUNDLE TRUE)
endif()
diff --git a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp
index 0edc9256e..fd6d819eb 100644
--- a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp
+++ b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtGui/QImageReader>
@@ -34,9 +7,11 @@
#include <QtCore/qlocale.h>
#include <QDebug>
#include <QVideoSink>
+#include <QMediaPlayer>
#include <private/qplatformcamera_p.h>
#include <private/qplatformimagecapture_p.h>
+#include <private/qplatformmediaintegration_p.h>
#include <qcamera.h>
#include <qcameradevice.h>
#include <qimagecapture.h>
@@ -47,6 +22,12 @@
#include <qmediaplayer.h>
#include <qaudiooutput.h>
+#ifdef Q_OS_DARWIN
+#include <QtCore/private/qcore_mac_p.h>
+#endif
+
+#include <mediabackendutils.h>
+
QT_USE_NAMESPACE
/*
@@ -83,6 +64,8 @@ private slots:
void testNativeMetadata();
+ void multipleCameraSet();
+
private:
bool noCamera = false;
};
@@ -116,6 +99,14 @@ public Q_SLOTS:
if (surfaceFormat.pixelFormat() == cameraFormat.pixelFormat()
&& surfaceFormat.frameSize() == cameraFormat.resolution()) {
formatMismatch = 0;
+#ifdef Q_OS_ANDROID
+ } else if ((surfaceFormat.pixelFormat() == QVideoFrameFormat::Format_YUV420P
+ || surfaceFormat.pixelFormat() == QVideoFrameFormat::Format_NV12)
+ && (cameraFormat.pixelFormat() == QVideoFrameFormat::Format_YUV420P
+ || cameraFormat.pixelFormat() == QVideoFrameFormat::Format_NV12)
+ && surfaceFormat.frameSize() == cameraFormat.resolution()) {
+ formatMismatch = 0;
+#endif
} else {
formatMismatch = 1;
}
@@ -124,6 +115,9 @@ public Q_SLOTS:
void tst_QCameraBackend::initTestCase()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("SKIP initTestCase on CI, because of QTBUG-118571");
+#endif
QCamera camera;
noCamera = !camera.isAvailable();
}
@@ -222,8 +216,8 @@ void tst_QCameraBackend::testCameraActive()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy errorSignal(&camera, SIGNAL(errorOccurred(QCamera::Error, const QString &)));
- QSignalSpy activeChangedSignal(&camera, SIGNAL(activeChanged(bool)));
+ QSignalSpy errorSignal(&camera, &QCamera::errorOccurred);
+ QSignalSpy activeChangedSignal(&camera, &QCamera::activeChanged);
QCOMPARE(camera.isActive(), false);
@@ -233,7 +227,7 @@ void tst_QCameraBackend::testCameraActive()
QCOMPARE(camera.error(), QCamera::NoError);
camera.start();
- QCOMPARE(camera.isActive(), true);
+ QTRY_COMPARE(camera.isActive(), true);
QTRY_COMPARE(activeChangedSignal.size(), 1);
QCOMPARE(activeChangedSignal.last().first().value<bool>(), true);
@@ -246,6 +240,14 @@ void tst_QCameraBackend::testCameraActive()
void tst_QCameraBackend::testCameraStartParallel()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Multi-camera feature is currently not supported on Android. "
+ "Cannot open same device twice.");
+#endif
+#ifdef Q_OS_LINUX
+ QSKIP("Multi-camera feature is currently not supported on Linux. "
+ "Cannot open same device twice.");
+#endif
if (noCamera)
QSKIP("No camera available");
@@ -266,8 +268,8 @@ void tst_QCameraBackend::testCameraStartParallel()
QCOMPARE(camera2.isActive(), true);
QCOMPARE(camera2.error(), QCamera::NoError);
- QCOMPARE(errorSpy1.count(), 0);
- QCOMPARE(errorSpy2.count(), 0);
+ QCOMPARE(errorSpy1.size(), 0);
+ QCOMPARE(errorSpy2.size(), 0);
}
void tst_QCameraBackend::testCameraFormat()
@@ -278,15 +280,15 @@ void tst_QCameraBackend::testCameraFormat()
if (videoFormats.isEmpty())
QSKIP("No Camera available, skipping test.");
QCameraFormat cameraFormat = videoFormats.first();
- QSignalSpy spy(&camera, SIGNAL(cameraFormatChanged()));
- QVERIFY(spy.count() == 0);
+ QSignalSpy spy(&camera, &QCamera::cameraFormatChanged);
+ QVERIFY(spy.size() == 0);
QMediaCaptureSession session;
session.setCamera(&camera);
- QVERIFY(videoFormats.count());
+ QVERIFY(videoFormats.size());
camera.setCameraFormat(cameraFormat);
QCOMPARE(camera.cameraFormat(), cameraFormat);
- QVERIFY(spy.count() == 1);
+ QVERIFY(spy.size() == 1);
TestVideoFormat videoFormatTester(cameraFormat);
session.setVideoOutput(&videoFormatTester);
@@ -296,11 +298,11 @@ void tst_QCameraBackend::testCameraFormat()
spy.clear();
camera.stop();
// Change camera format
- if (videoFormats.count() > 1) {
+ if (videoFormats.size() > 1) {
QCameraFormat secondFormat = videoFormats.at(1);
camera.setCameraFormat(secondFormat);
QCOMPARE(camera.cameraFormat(), secondFormat);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(camera.cameraFormat(), secondFormat);
videoFormatTester.setCameraFormatToTest(secondFormat);
camera.start();
@@ -315,7 +317,7 @@ void tst_QCameraBackend::testCameraFormat()
spy.clear();
camera.stop();
camera.setCameraFormat({});
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
videoFormatTester.setCameraFormatToTest({});
camera.start();
// In case of a null format, the backend should have picked
@@ -326,7 +328,7 @@ void tst_QCameraBackend::testCameraFormat()
spy.clear();
// Shouldn't change anything as it's the same device
camera.setCameraDevice(device);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
}
void tst_QCameraBackend::testCameraCapture()
@@ -342,9 +344,9 @@ void tst_QCameraBackend::testCameraCapture()
QVERIFY(!imageCapture.isReadyForCapture());
- QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage)));
- QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
- QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,const QString&)));
+ QSignalSpy capturedSignal(&imageCapture, &QImageCapture::imageCaptured);
+ QSignalSpy savedSignal(&imageCapture, &QImageCapture::imageSaved);
+ QSignalSpy errorSignal(&imageCapture, &QImageCapture::errorOccurred);
imageCapture.captureToFile();
QTRY_COMPARE(errorSignal.size(), 1);
@@ -365,7 +367,7 @@ void tst_QCameraBackend::testCameraCapture()
int id = imageCapture.captureToFile();
- QTRY_VERIFY(!savedSignal.isEmpty());
+ QTRY_VERIFY_WITH_TIMEOUT(!savedSignal.isEmpty(), 8s);
QTRY_COMPARE(capturedSignal.size(), 1);
QCOMPARE(capturedSignal.last().first().toInt(), id);
@@ -401,10 +403,10 @@ void tst_QCameraBackend::testCaptureToBuffer()
QTRY_VERIFY(camera.isActive());
- QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage)));
- QSignalSpy imageAvailableSignal(&imageCapture, SIGNAL(imageAvailable(int,QVideoFrame)));
- QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
- QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,const QString&)));
+ QSignalSpy capturedSignal(&imageCapture, &QImageCapture::imageCaptured);
+ QSignalSpy imageAvailableSignal(&imageCapture, &QImageCapture::imageAvailable);
+ QSignalSpy savedSignal(&imageCapture, &QImageCapture::imageSaved);
+ QSignalSpy errorSignal(&imageCapture, &QImageCapture::errorOccurred);
camera.start();
QTRY_VERIFY(imageCapture.isReadyForCapture());
@@ -446,8 +448,14 @@ void tst_QCameraBackend::testCameraCaptureMetadata()
camera.setFlashMode(QCamera::FlashOff);
- QSignalSpy metadataSignal(&imageCapture, SIGNAL(imageMetadataAvailable(int,const QMediaMetaData&)));
- QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
+ QMediaMetaData referenceMetaData;
+ referenceMetaData.insert(QMediaMetaData::Title, QStringLiteral("Title"));
+ referenceMetaData.insert(QMediaMetaData::Language, QVariant::fromValue(QLocale::German));
+ referenceMetaData.insert(QMediaMetaData::Description, QStringLiteral("Description"));
+ imageCapture.setMetaData(referenceMetaData);
+
+ QSignalSpy metadataSignal(&imageCapture, &QImageCapture::imageMetadataAvailable);
+ QSignalSpy savedSignal(&imageCapture, &QImageCapture::imageSaved);
camera.start();
@@ -458,7 +466,19 @@ void tst_QCameraBackend::testCameraCaptureMetadata()
int id = imageCapture.captureToFile(tmpFile);
QTRY_VERIFY(!savedSignal.isEmpty());
QVERIFY(!metadataSignal.isEmpty());
+
QCOMPARE(metadataSignal.first().first().toInt(), id);
+ QMediaMetaData receivedMetaData = metadataSignal.first()[1].value<QMediaMetaData>();
+
+ if (isGStreamerPlatform()) {
+ for (auto key : {
+ QMediaMetaData::Title,
+ QMediaMetaData::Language,
+ QMediaMetaData::Description,
+ })
+ QCOMPARE(receivedMetaData[key], referenceMetaData[key]);
+ QVERIFY(receivedMetaData[QMediaMetaData::Resolution].isValid());
+ }
}
void tst_QCameraBackend::testExposureCompensation()
@@ -470,22 +490,22 @@ void tst_QCameraBackend::testExposureCompensation()
QCamera camera;
session.setCamera(&camera);
- QSignalSpy exposureCompensationSignal(&camera, SIGNAL(exposureCompensationChanged(float)));
+ QSignalSpy exposureCompensationSignal(&camera, &QCamera::exposureCompensationChanged);
- //it should be possible to set exposure parameters in Unloaded state
+ // it should be possible to set exposure parameters in Unloaded state
QCOMPARE(camera.exposureCompensation(), 0.);
if (!(camera.supportedFeatures() & QCamera::Feature::ExposureCompensation))
return;
camera.setExposureCompensation(1.0);
QCOMPARE(camera.exposureCompensation(), 1.0);
- QTRY_COMPARE(exposureCompensationSignal.count(), 1);
+ QTRY_COMPARE(exposureCompensationSignal.size(), 1);
QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0);
//exposureCompensationChanged should not be emitted when value is not changed
camera.setExposureCompensation(1.0);
QTest::qWait(50);
- QCOMPARE(exposureCompensationSignal.count(), 1);
+ QCOMPARE(exposureCompensationSignal.size(), 1);
//exposure compensation should be preserved during start
camera.start();
@@ -496,7 +516,7 @@ void tst_QCameraBackend::testExposureCompensation()
exposureCompensationSignal.clear();
camera.setExposureCompensation(-1.0);
QCOMPARE(camera.exposureCompensation(), -1.0);
- QTRY_COMPARE(exposureCompensationSignal.count(), 1);
+ QTRY_COMPARE(exposureCompensationSignal.size(), 1);
QCOMPARE(exposureCompensationSignal.last().first().toReal(), -1.0);
}
@@ -525,7 +545,7 @@ void tst_QCameraBackend::testExposureMode()
camera.setExposureMode(QCamera::ExposureAuto);
QCOMPARE(camera.exposureMode(), QCamera::ExposureAuto);
camera.start();
- QVERIFY(camera.isActive());
+ QTRY_VERIFY(camera.isActive());
QCOMPARE(camera.exposureMode(), QCamera::ExposureAuto);
// Manual
@@ -569,16 +589,16 @@ void tst_QCameraBackend::testVideoRecording()
QMediaRecorder recorder;
session.setRecorder(&recorder);
- QSignalSpy errorSignal(camera.data(), SIGNAL(errorOccurred(QCamera::Error, const QString &)));
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
- QSignalSpy recorderStateChanged(&recorder, SIGNAL(recorderStateChanged(RecorderState)));
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
+ QSignalSpy errorSignal(camera.data(), &QCamera::errorOccurred);
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
+ QSignalSpy recorderStateChanged(&recorder, &QMediaRecorder::recorderStateChanged);
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
recorder.setVideoResolution(320, 240);
// Insert metadata
QMediaMetaData metaData;
- metaData.insert(QMediaMetaData::Author, QString::fromUtf8("Author"));
+ metaData.insert(QMediaMetaData::Author, QStringLiteral("Author"));
metaData.insert(QMediaMetaData::Date, QDateTime::currentDateTime());
recorder.setMetaData(metaData);
@@ -591,27 +611,41 @@ void tst_QCameraBackend::testVideoRecording()
QTRY_VERIFY(camera->isActive());
- for (int recordings = 0; recordings < 2; ++recordings) {
- //record 200ms clip
- recorder.record();
- durationChanged.clear();
- QTRY_VERIFY(durationChanged.count());
+ recorder.record();
+ if (!recorderErrorSignal.empty() || recorderErrorSignal.wait(550)) {
+ QEXPECT_FAIL_GSTREAMER("", "QTBUG-124148: GStreamer might return ResourceError", Continue);
+
+ QCOMPARE(recorderErrorSignal.last().first().toInt(), QMediaRecorder::FormatError);
+ return;
+ }
+
+ QTRY_VERIFY(durationChanged.size());
- QCOMPARE(recorder.metaData(), metaData);
+ QCOMPARE(recorder.metaData(), metaData);
- recorderStateChanged.clear();
- recorder.stop();
- QTRY_VERIFY(recorderStateChanged.size() > 0);
- QVERIFY(recorder.recorderState() == QMediaRecorder::StoppedState);
+ recorderStateChanged.clear();
+ recorder.stop();
+ QTRY_VERIFY(recorderStateChanged.size() > 0);
+ QVERIFY(recorder.recorderState() == QMediaRecorder::StoppedState);
- QVERIFY(errorSignal.isEmpty());
- QVERIFY(recorderErrorSignal.isEmpty());
+ QVERIFY(errorSignal.isEmpty());
+ QVERIFY(recorderErrorSignal.isEmpty());
- QString fileName = recorder.actualLocation().toLocalFile();
- QVERIFY(!fileName.isEmpty());
- QVERIFY(QFileInfo(fileName).size() > 0);
- QFile(fileName).remove();
- }
+ QString fileName = recorder.actualLocation().toLocalFile();
+ QVERIFY(!fileName.isEmpty());
+ QVERIFY(QFileInfo(fileName).size() > 0);
+
+ QMediaPlayer player;
+ player.setSource(fileName);
+
+ QTRY_COMPARE_WITH_TIMEOUT(player.mediaStatus(), QMediaPlayer::LoadedMedia, 8s);
+ QCOMPARE_EQ(player.metaData().value(QMediaMetaData::Resolution).toSize(), QSize(320, 240));
+ QCOMPARE_GT(player.duration(), 350);
+ QCOMPARE_LT(player.duration(), 650);
+
+ // TODO: integrate with a virtual camera and check mediaplayer output
+
+ QFile(fileName).remove();
}
void tst_QCameraBackend::testNativeMetadata()
@@ -627,10 +661,10 @@ void tst_QCameraBackend::testNativeMetadata()
QMediaRecorder recorder;
session.setRecorder(&recorder);
- QSignalSpy errorSignal(&camera, SIGNAL(errorOccurred(QCamera::Error, const QString &)));
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
- QSignalSpy recorderStateChanged(&recorder, SIGNAL(recorderStateChanged(RecorderState)));
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
+ QSignalSpy errorSignal(&camera, &QCamera::errorOccurred);
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
+ QSignalSpy recorderStateChanged(&recorder, &QMediaRecorder::recorderStateChanged);
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
camera.start();
if (device.isNull()) {
@@ -643,15 +677,15 @@ void tst_QCameraBackend::testNativeMetadata()
// Insert common metadata supported on all platforms
// Don't use Date, as some backends set that on their own
QMediaMetaData metaData;
- metaData.insert(QMediaMetaData::Title, QString::fromUtf8("Title"));
+ metaData.insert(QMediaMetaData::Title, QStringLiteral("Title"));
metaData.insert(QMediaMetaData::Language, QVariant::fromValue(QLocale::German));
- metaData.insert(QMediaMetaData::Description, QString::fromUtf8("Description"));
+ metaData.insert(QMediaMetaData::Description, QStringLiteral("Description"));
recorder.setMetaData(metaData);
recorder.record();
- durationChanged.clear();
- QTRY_VERIFY(durationChanged.count());
+ QTRY_VERIFY(durationChanged.size());
+ QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::RecorderState::RecordingState);
QCOMPARE(recorder.metaData(), metaData);
@@ -659,9 +693,13 @@ void tst_QCameraBackend::testNativeMetadata()
recorder.stop();
QTRY_VERIFY(recorderStateChanged.size() > 0);
+ QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::RecorderState::StoppedState);
QVERIFY(errorSignal.isEmpty());
- QVERIFY(recorderErrorSignal.isEmpty());
+ if (!isGStreamerPlatform()) {
+ // https://bugreports.qt.io/browse/QTBUG-124183
+ QVERIFY(recorderErrorSignal.isEmpty());
+ }
QString fileName = recorder.actualLocation().toLocalFile();
QVERIFY(!fileName.isEmpty());
@@ -669,30 +707,60 @@ void tst_QCameraBackend::testNativeMetadata()
// QMediaRecorder::metaData() can only test that QMediaMetaData is set properly on the recorder.
// Use QMediaPlayer to test that the native metadata is properly set on the track
- QMediaPlayer player;
QAudioOutput output;
+ QMediaPlayer player;
player.setAudioOutput(&output);
- QSignalSpy metadataChangedSpy(&player, SIGNAL(metaDataChanged()));
+ QSignalSpy metadataChangedSpy(&player, &QMediaPlayer::metaDataChanged);
player.setSource(QUrl::fromLocalFile(fileName));
player.play();
- QTRY_VERIFY(metadataChangedSpy.count() > 0);
+ int metadataChangedRequiredCount = isGStreamerPlatform() ? 2 : 1;
+
+ QTRY_VERIFY(metadataChangedSpy.size() >= metadataChangedRequiredCount);
- QCOMPARE(player.metaData().value(QMediaMetaData::Title).toString(), metaData.value(QMediaMetaData::Title).toString());
+ QCOMPARE(player.metaData().value(QMediaMetaData::Title).toString(),
+ metaData.value(QMediaMetaData::Title).toString());
auto lang = player.metaData().value(QMediaMetaData::Language).value<QLocale::Language>();
if (lang != QLocale::AnyLanguage)
QCOMPARE(lang, metaData.value(QMediaMetaData::Language).value<QLocale::Language>());
QCOMPARE(player.metaData().value(QMediaMetaData::Description).toString(), metaData.value(QMediaMetaData::Description).toString());
+ QVERIFY(player.metaData().value(QMediaMetaData::Resolution).isValid());
- metadataChangedSpy.clear();
+ if (isGStreamerPlatform())
+ QVERIFY(player.metaData().value(QMediaMetaData::Date).isValid());
player.stop();
player.setSource({});
QFile(fileName).remove();
}
+void tst_QCameraBackend::multipleCameraSet()
+{
+ if (noCamera)
+ QSKIP("No camera available");
+
+ QMediaCaptureSession session;
+ QCameraDevice device = QMediaDevices::defaultVideoInput();
+
+ QMediaRecorder recorder;
+ session.setRecorder(&recorder);
+
+ for (int i = 0; i < 5; ++i) {
+#ifdef Q_OS_DARWIN
+ QMacAutoReleasePool releasePool;
+#endif
+
+ QCamera camera(device);
+ session.setCamera(&camera);
+
+ camera.start();
+
+ QTest::qWait(100);
+ }
+}
+
QTEST_MAIN(tst_QCameraBackend)
#include "tst_qcamerabackend.moc"
diff --git a/tests/auto/integration/qmediacapturesession/BLACKLIST b/tests/auto/integration/qmediacapturesession/BLACKLIST
new file mode 100644
index 000000000..550ecdd6f
--- /dev/null
+++ b/tests/auto/integration/qmediacapturesession/BLACKLIST
@@ -0,0 +1 @@
+ci
diff --git a/tests/auto/integration/qmediacapturesession/CMakeLists.txt b/tests/auto/integration/qmediacapturesession/CMakeLists.txt
index 63ac2e99f..1aec26493 100644
--- a/tests/auto/integration/qmediacapturesession/CMakeLists.txt
+++ b/tests/auto/integration/qmediacapturesession/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qcamerabackend.pro.
#####################################################################
@@ -7,7 +10,10 @@
qt_internal_add_test(tst_qmediacapturesession
SOURCES
tst_qmediacapturesession.cpp
- PUBLIC_LIBRARIES
+ ../shared/mediabackendutils.h
+ INCLUDE_DIRECTORIES
+ ../shared/
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
Qt::MultimediaWidgets
diff --git a/tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp b/tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp
index a5a3741b2..e8376d54c 100644
--- a/tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp
+++ b/tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtGui/QImageReader>
@@ -34,6 +7,7 @@
#include <QDebug>
#include <QVideoSink>
#include <QVideoWidget>
+#include <QSysInfo>
#include <qcamera.h>
#include <qcameradevice.h>
@@ -52,6 +26,8 @@
#include <QMediaFormat>
#include <QtMultimediaWidgets/QVideoWidget>
+#include <mediabackendutils.h>
+
QT_USE_NAMESPACE
/*
@@ -67,8 +43,22 @@ class tst_QMediaCaptureSession: public QObject
private slots:
+ void initTestCase()
+ {
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci") {
+#ifdef Q_OS_ANDROID
+ QSKIP("SKIP initTestCase on CI, because of QTBUG-118571");
+#endif
+ }
+ }
void testAudioMute();
void stress_test_setup_and_teardown();
+ void stress_test_setup_and_teardown_keep_session();
+ void stress_test_setup_and_teardown_keep_recorder();
+ void stress_test_setup_and_teardown_keep_camera();
+ void stress_test_setup_and_teardown_keep_audioinput();
+ void stress_test_setup_and_teardown_keep_audiooutput();
+ void stress_test_setup_and_teardown_keep_video();
void record_video_without_preview();
@@ -101,6 +91,8 @@ private slots:
void capture_is_not_available_when_Camera_is_null();
void can_add_ImageCapture_and_capture_during_recording();
+ void can_reset_audio_input_output();
+
private:
void recordOk(QMediaCaptureSession &session);
void recordFail(QMediaCaptureSession &session);
@@ -112,8 +104,8 @@ void tst_QMediaCaptureSession::recordOk(QMediaCaptureSession &session)
QMediaRecorder recorder;
session.setRecorder(&recorder);
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
recorder.record();
QTRY_VERIFY_WITH_TIMEOUT(recorder.recorderState() == QMediaRecorder::RecordingState, 2000);
@@ -132,12 +124,12 @@ void tst_QMediaCaptureSession::recordOk(QMediaCaptureSession &session)
void tst_QMediaCaptureSession::recordFail(QMediaCaptureSession &session)
{
QMediaRecorder recorder;
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
session.setRecorder(&recorder);
recorder.record();
- QTRY_VERIFY_WITH_TIMEOUT(recorderErrorSignal.count() == 1, 2000);
+ QTRY_VERIFY_WITH_TIMEOUT(recorderErrorSignal.size() == 1, 2000);
QTRY_VERIFY_WITH_TIMEOUT(recorder.recorderState() == QMediaRecorder::StoppedState, 2000);
}
@@ -162,6 +154,132 @@ void tst_QMediaCaptureSession::stress_test_setup_and_teardown()
}
}
+void tst_QMediaCaptureSession::stress_test_setup_and_teardown_keep_session()
+{
+ QMediaCaptureSession session;
+ for (int i = 0; i < 50; i++) {
+ QMediaRecorder recorder;
+ QCamera camera;
+ QAudioInput input;
+ QAudioOutput output;
+ QVideoWidget video;
+
+ session.setAudioInput(&input);
+ session.setAudioOutput(&output);
+ session.setRecorder(&recorder);
+ session.setCamera(&camera);
+ session.setVideoOutput(&video);
+
+ QRandomGenerator rng;
+ QTest::qWait(rng.bounded(200));
+ }
+}
+
+void tst_QMediaCaptureSession::stress_test_setup_and_teardown_keep_recorder()
+{
+ QMediaCaptureSession session;
+ QMediaRecorder recorder;
+ for (int i = 0; i < 50; i++) {
+ QCamera camera;
+ QAudioInput input;
+ QAudioOutput output;
+ QVideoWidget video;
+
+ session.setAudioInput(&input);
+ session.setAudioOutput(&output);
+ session.setRecorder(&recorder);
+ session.setCamera(&camera);
+ session.setVideoOutput(&video);
+
+ QRandomGenerator rng;
+ QTest::qWait(rng.bounded(200));
+ }
+}
+
+void tst_QMediaCaptureSession::stress_test_setup_and_teardown_keep_camera()
+{
+ QCamera camera;
+ for (int i = 0; i < 50; i++) {
+ QMediaCaptureSession session;
+ QMediaRecorder recorder;
+ QAudioInput input;
+ QAudioOutput output;
+ QVideoWidget video;
+
+ session.setAudioInput(&input);
+ session.setAudioOutput(&output);
+ session.setRecorder(&recorder);
+ session.setCamera(&camera);
+ session.setVideoOutput(&video);
+
+ QRandomGenerator rng;
+ QTest::qWait(rng.bounded(200));
+ }
+}
+
+void tst_QMediaCaptureSession::stress_test_setup_and_teardown_keep_audioinput()
+{
+ QAudioInput input;
+ for (int i = 0; i < 50; i++) {
+ QMediaCaptureSession session;
+ QMediaRecorder recorder;
+ QCamera camera;
+ QAudioOutput output;
+ QVideoWidget video;
+
+ session.setAudioInput(&input);
+ session.setAudioOutput(&output);
+ session.setRecorder(&recorder);
+ session.setCamera(&camera);
+ session.setVideoOutput(&video);
+
+ QRandomGenerator rng;
+ QTest::qWait(rng.bounded(200));
+ }
+}
+
+void tst_QMediaCaptureSession::stress_test_setup_and_teardown_keep_audiooutput()
+{
+ QAudioOutput output;
+ for (int i = 0; i < 50; i++) {
+ QMediaCaptureSession session;
+ QMediaRecorder recorder;
+ QCamera camera;
+ QAudioInput input;
+ QVideoWidget video;
+
+ session.setAudioInput(&input);
+ session.setAudioOutput(&output);
+ session.setRecorder(&recorder);
+ session.setCamera(&camera);
+ session.setVideoOutput(&video);
+
+ QRandomGenerator rng;
+ QTest::qWait(rng.bounded(200));
+ }
+}
+
+void tst_QMediaCaptureSession::stress_test_setup_and_teardown_keep_video()
+{
+ QVideoWidget video;
+ for (int i = 0; i < 50; i++) {
+ QMediaCaptureSession session;
+ QMediaRecorder recorder;
+ QCamera camera;
+ QAudioInput input;
+ QAudioOutput output;
+
+ session.setAudioInput(&input);
+ session.setAudioOutput(&output);
+ session.setRecorder(&recorder);
+ session.setCamera(&camera);
+ session.setVideoOutput(&video);
+
+ QRandomGenerator rng;
+ QTest::qWait(rng.bounded(200));
+ }
+}
+
void tst_QMediaCaptureSession::record_video_without_preview()
{
QCamera camera;
@@ -174,17 +292,18 @@ void tst_QMediaCaptureSession::record_video_without_preview()
session.setRecorder(&recorder);
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
session.setCamera(&camera);
camera.setActive(true);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
+ QTRY_COMPARE(camera.isActive(), true);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
session.setCamera(nullptr);
- QTRY_COMPARE(cameraChanged.count(), 2);
+ QTRY_COMPARE(cameraChanged.size(), 2);
// can't record without audio and video
recordFail(session);
@@ -198,29 +317,29 @@ void tst_QMediaCaptureSession::can_add_and_remove_AudioInput_with_and_without_Au
QSKIP("No audio input available");
QMediaCaptureSession session;
- QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged()));
- QSignalSpy audioOutputChanged(&session, SIGNAL(audioOutputChanged()));
+ QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged);
+ QSignalSpy audioOutputChanged(&session, &QMediaCaptureSession::audioOutputChanged);
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 1);
+ QTRY_COMPARE(audioInputChanged.size(), 1);
session.setAudioInput(nullptr);
- QTRY_COMPARE(audioInputChanged.count(), 2);
+ QTRY_COMPARE(audioInputChanged.size(), 2);
QAudioOutput output;
if (output.device().isNull())
return;
session.setAudioOutput(&output);
- QTRY_COMPARE(audioOutputChanged.count(), 1);
+ QTRY_COMPARE(audioOutputChanged.size(), 1);
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 3);
+ QTRY_COMPARE(audioInputChanged.size(), 3);
session.setAudioOutput(nullptr);
- QTRY_COMPARE(audioOutputChanged.count(), 2);
+ QTRY_COMPARE(audioOutputChanged.size(), 2);
session.setAudioInput(nullptr);
- QTRY_COMPARE(audioInputChanged.count(), 4);
+ QTRY_COMPARE(audioInputChanged.size(), 4);
}
void tst_QMediaCaptureSession::can_change_AudioDevices_on_attached_AudioInput()
@@ -230,25 +349,25 @@ void tst_QMediaCaptureSession::can_change_AudioDevices_on_attached_AudioInput()
QSKIP("Two audio inputs are not available");
QAudioInput input(audioInputs[0]);
- QSignalSpy deviceChanged(&input, SIGNAL(deviceChanged()));
+ QSignalSpy deviceChanged(&input, &QAudioInput::deviceChanged);
QMediaCaptureSession session;
- QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged()));
+ QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged);
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 1);
+ QTRY_COMPARE(audioInputChanged.size(), 1);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
input.setDevice(audioInputs[1]);
- QTRY_COMPARE(deviceChanged.count(), 1);
+ QTRY_COMPARE(deviceChanged.size(), 1);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
input.setDevice(audioInputs[0]);
- QTRY_COMPARE(deviceChanged.count(), 2);
+ QTRY_COMPARE(deviceChanged.size(), 2);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
@@ -265,27 +384,27 @@ void tst_QMediaCaptureSession::can_change_AudioInput_during_recording()
session.setRecorder(&recorder);
- QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged()));
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
+ QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged);
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 1);
+ QTRY_COMPARE(audioInputChanged.size(), 1);
recorder.record();
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::RecordingState);
QVERIFY(durationChanged.wait(2000));
session.setAudioInput(nullptr);
- QTRY_COMPARE(audioInputChanged.count(), 2);
+ QTRY_COMPARE(audioInputChanged.size(), 2);
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 3);
+ QTRY_COMPARE(audioInputChanged.size(), 3);
recorder.stop();
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::StoppedState);
QVERIFY(recorderErrorSignal.isEmpty());
session.setAudioInput(nullptr);
- QTRY_COMPARE(audioInputChanged.count(), 4);
+ QTRY_COMPARE(audioInputChanged.size(), 4);
QString fileName = recorder.actualLocation().toLocalFile();
QVERIFY(!fileName.isEmpty());
@@ -299,14 +418,14 @@ void tst_QMediaCaptureSession::disconnects_deleted_AudioInput()
QSKIP("No audio input available");
QMediaCaptureSession session;
- QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged()));
+ QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged);
{
QAudioInput input;
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 1);
+ QTRY_COMPARE(audioInputChanged.size(), 1);
}
QVERIFY(session.audioInput() == nullptr);
- QTRY_COMPARE(audioInputChanged.count(), 2);
+ QTRY_COMPARE(audioInputChanged.size(), 2);
}
void tst_QMediaCaptureSession::can_move_AudioInput_between_sessions()
@@ -316,24 +435,24 @@ void tst_QMediaCaptureSession::can_move_AudioInput_between_sessions()
QMediaCaptureSession session0;
QMediaCaptureSession session1;
- QSignalSpy audioInputChanged0(&session0, SIGNAL(audioInputChanged()));
- QSignalSpy audioInputChanged1(&session1, SIGNAL(audioInputChanged()));
+ QSignalSpy audioInputChanged0(&session0, &QMediaCaptureSession::audioInputChanged);
+ QSignalSpy audioInputChanged1(&session1, &QMediaCaptureSession::audioInputChanged);
QAudioInput input;
{
QMediaCaptureSession session2;
- QSignalSpy audioInputChanged2(&session2, SIGNAL(audioInputChanged()));
+ QSignalSpy audioInputChanged2(&session2, &QMediaCaptureSession::audioInputChanged);
session2.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged2.count(), 1);
+ QTRY_COMPARE(audioInputChanged2.size(), 1);
}
session0.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged0.count(), 1);
+ QTRY_COMPARE(audioInputChanged0.size(), 1);
QVERIFY(session0.audioInput() != nullptr);
session1.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged0.count(), 2);
+ QTRY_COMPARE(audioInputChanged0.size(), 2);
QVERIFY(session0.audioInput() == nullptr);
- QTRY_COMPARE(audioInputChanged1.count(), 1);
+ QTRY_COMPARE(audioInputChanged1.size(), 1);
QVERIFY(session1.audioInput() != nullptr);
}
@@ -343,14 +462,14 @@ void tst_QMediaCaptureSession::disconnects_deleted_AudioOutput()
QSKIP("No audio output available");
QMediaCaptureSession session;
- QSignalSpy audioOutputChanged(&session, SIGNAL(audioOutputChanged()));
+ QSignalSpy audioOutputChanged(&session, &QMediaCaptureSession::audioOutputChanged);
{
QAudioOutput output;
session.setAudioOutput(&output);
- QTRY_COMPARE(audioOutputChanged.count(), 1);
+ QTRY_COMPARE(audioOutputChanged.size(), 1);
}
QVERIFY(session.audioOutput() == nullptr);
- QTRY_COMPARE(audioOutputChanged.count(), 2);
+ QTRY_COMPARE(audioOutputChanged.size(), 2);
}
void tst_QMediaCaptureSession::can_move_AudioOutput_between_sessions_and_player()
@@ -358,43 +477,44 @@ void tst_QMediaCaptureSession::can_move_AudioOutput_between_sessions_and_player(
if (QMediaDevices::audioOutputs().isEmpty())
QSKIP("No audio output available");
+ QAudioOutput output;
+
QMediaCaptureSession session0;
QMediaCaptureSession session1;
QMediaPlayer player;
- QSignalSpy audioOutputChanged0(&session0, SIGNAL(audioOutputChanged()));
- QSignalSpy audioOutputChanged1(&session1, SIGNAL(audioOutputChanged()));
- QSignalSpy audioOutputChangedPlayer(&player, SIGNAL(audioOutputChanged()));
+ QSignalSpy audioOutputChanged0(&session0, &QMediaCaptureSession::audioOutputChanged);
+ QSignalSpy audioOutputChanged1(&session1, &QMediaCaptureSession::audioOutputChanged);
+ QSignalSpy audioOutputChangedPlayer(&player, &QMediaPlayer::audioOutputChanged);
- QAudioOutput output;
{
QMediaCaptureSession session2;
- QSignalSpy audioOutputChanged2(&session2, SIGNAL(audioOutputChanged()));
+ QSignalSpy audioOutputChanged2(&session2, &QMediaCaptureSession::audioOutputChanged);
session2.setAudioOutput(&output);
- QTRY_COMPARE(audioOutputChanged2.count(), 1);
+ QTRY_COMPARE(audioOutputChanged2.size(), 1);
}
session0.setAudioOutput(&output);
- QTRY_COMPARE(audioOutputChanged0.count(), 1);
+ QTRY_COMPARE(audioOutputChanged0.size(), 1);
QVERIFY(session0.audioOutput() != nullptr);
session1.setAudioOutput(&output);
- QTRY_COMPARE(audioOutputChanged0.count(), 2);
+ QTRY_COMPARE(audioOutputChanged0.size(), 2);
QVERIFY(session0.audioOutput() == nullptr);
- QTRY_COMPARE(audioOutputChanged1.count(), 1);
+ QTRY_COMPARE(audioOutputChanged1.size(), 1);
QVERIFY(session1.audioOutput() != nullptr);
player.setAudioOutput(&output);
- QTRY_COMPARE(audioOutputChanged0.count(), 2);
+ QTRY_COMPARE(audioOutputChanged0.size(), 2);
QVERIFY(session0.audioOutput() == nullptr);
- QTRY_COMPARE(audioOutputChanged1.count(), 2);
+ QTRY_COMPARE(audioOutputChanged1.size(), 2);
QVERIFY(session1.audioOutput() == nullptr);
- QTRY_COMPARE(audioOutputChangedPlayer.count(), 1);
+ QTRY_COMPARE(audioOutputChangedPlayer.size(), 1);
QVERIFY(player.audioOutput() != nullptr);
session0.setAudioOutput(&output);
- QTRY_COMPARE(audioOutputChanged0.count(), 3);
+ QTRY_COMPARE(audioOutputChanged0.size(), 3);
QVERIFY(session0.audioOutput() != nullptr);
- QTRY_COMPARE(audioOutputChangedPlayer.count(), 2);
+ QTRY_COMPARE(audioOutputChangedPlayer.size(), 2);
QVERIFY(player.audioOutput() == nullptr);
}
@@ -411,17 +531,18 @@ void tst_QMediaCaptureSession::can_add_and_remove_Camera()
session.setRecorder(&recorder);
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
session.setCamera(&camera);
camera.setActive(true);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
+ QTRY_COMPARE(camera.isActive(), true);
session.setCamera(nullptr);
- QTRY_COMPARE(cameraChanged.count(), 2);
+ QTRY_COMPARE(cameraChanged.size(), 2);
session.setCamera(&camera);
- QTRY_COMPARE(cameraChanged.count(), 3);
+ QTRY_COMPARE(cameraChanged.size(), 3);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
@@ -431,31 +552,31 @@ void tst_QMediaCaptureSession::can_move_Camera_between_sessions()
{
QMediaCaptureSession session0;
QMediaCaptureSession session1;
- QSignalSpy cameraChanged0(&session0, SIGNAL(cameraChanged()));
- QSignalSpy cameraChanged1(&session1, SIGNAL(cameraChanged()));
+ QSignalSpy cameraChanged0(&session0, &QMediaCaptureSession::cameraChanged);
+ QSignalSpy cameraChanged1(&session1, &QMediaCaptureSession::cameraChanged);
{
QCamera camera;
{
QMediaCaptureSession session2;
- QSignalSpy cameraChanged2(&session2, SIGNAL(cameraChanged()));
+ QSignalSpy cameraChanged2(&session2, &QMediaCaptureSession::cameraChanged);
session2.setCamera(&camera);
- QTRY_COMPARE(cameraChanged2.count(), 1);
+ QTRY_COMPARE(cameraChanged2.size(), 1);
}
QVERIFY(camera.captureSession() == nullptr);
session0.setCamera(&camera);
- QTRY_COMPARE(cameraChanged0.count(), 1);
+ QTRY_COMPARE(cameraChanged0.size(), 1);
QVERIFY(session0.camera() == &camera);
QVERIFY(camera.captureSession() == &session0);
session1.setCamera(&camera);
- QTRY_COMPARE(cameraChanged0.count(), 2);
+ QTRY_COMPARE(cameraChanged0.size(), 2);
QVERIFY(session0.camera() == nullptr);
- QTRY_COMPARE(cameraChanged1.count(), 1);
+ QTRY_COMPARE(cameraChanged1.size(), 1);
QVERIFY(session1.camera() == &camera);
QVERIFY(camera.captureSession() == &session1);
}
- QTRY_COMPARE(cameraChanged1.count(), 2);
+ QTRY_COMPARE(cameraChanged1.size(), 2);
QVERIFY(session1.camera() == nullptr);
}
@@ -471,21 +592,22 @@ void tst_QMediaCaptureSession::can_disconnect_Camera_when_recording()
session.setRecorder(&recorder);
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
camera.setActive(true);
session.setCamera(&camera);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
+ QTRY_COMPARE(camera.isActive(), true);
durationChanged.clear();
recorder.record();
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::RecordingState);
- QTRY_VERIFY(durationChanged.count() > 0);
+ QTRY_VERIFY(durationChanged.size() > 0);
session.setCamera(nullptr);
- QTRY_COMPARE(cameraChanged.count(), 2);
+ QTRY_COMPARE(cameraChanged.size(), 2);
recorder.stop();
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::StoppedState);
@@ -512,18 +634,20 @@ void tst_QMediaCaptureSession::can_add_and_remove_different_Cameras()
session.setRecorder(&recorder);
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
camera.setActive(true);
session.setCamera(&camera);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
+ QTRY_COMPARE(camera.isActive(), true);
session.setCamera(nullptr);
- QTRY_COMPARE(cameraChanged.count(), 2);
+ QTRY_COMPARE(cameraChanged.size(), 2);
session.setCamera(&camera2);
camera2.setActive(true);
- QTRY_COMPARE(cameraChanged.count(), 3);
+ QTRY_COMPARE(cameraChanged.size(), 3);
+ QTRY_COMPARE(camera2.isActive(), true);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
@@ -543,22 +667,25 @@ void tst_QMediaCaptureSession::can_change_CameraDevice_on_attached_Camera()
session.setRecorder(&recorder);
- QSignalSpy cameraDeviceChanged(&camera, SIGNAL(cameraDeviceChanged()));
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
+ QSignalSpy cameraDeviceChanged(&camera, &QCamera::cameraDeviceChanged);
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
session.setCamera(&camera);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
recordFail(session);
QVERIFY(!QTest::currentTestFailed());
camera.setActive(true);
+ QTRY_COMPARE(camera.isActive(), true);
+
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
camera.setCameraDevice(cameraDevices[1]);
camera.setActive(true);
- QTRY_COMPARE(cameraDeviceChanged.count(), 1);
+ QTRY_COMPARE(cameraDeviceChanged.size(), 1);
+ QTRY_COMPARE(camera.isActive(), true);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
@@ -577,26 +704,26 @@ void tst_QMediaCaptureSession::can_change_VideoOutput_with_and_without_camera()
QMediaCaptureSession session;
- QSignalSpy videoOutputChanged(&session, SIGNAL(videoOutputChanged()));
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
+ QSignalSpy videoOutputChanged(&session, &QMediaCaptureSession::videoOutputChanged);
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
session.setCamera(&camera);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
session.setVideoOutput(&videoOutput);
- QTRY_COMPARE(videoOutputChanged.count(), 1);
+ QTRY_COMPARE(videoOutputChanged.size(), 1);
session.setVideoOutput(nullptr);
- QTRY_COMPARE(videoOutputChanged.count(), 2);
+ QTRY_COMPARE(videoOutputChanged.size(), 2);
session.setVideoOutput(&videoOutput2);
- QTRY_COMPARE(videoOutputChanged.count(), 3);
+ QTRY_COMPARE(videoOutputChanged.size(), 3);
session.setCamera(nullptr);
- QTRY_COMPARE(cameraChanged.count(), 2);
+ QTRY_COMPARE(cameraChanged.size(), 2);
session.setVideoOutput(nullptr);
- QTRY_COMPARE(videoOutputChanged.count(), 4);
+ QTRY_COMPARE(videoOutputChanged.size(), 4);
}
void tst_QMediaCaptureSession::can_change_VideoOutput_when_recording()
@@ -613,27 +740,28 @@ void tst_QMediaCaptureSession::can_change_VideoOutput_when_recording()
session.setRecorder(&recorder);
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
- QSignalSpy videoOutputChanged(&session, SIGNAL(videoOutputChanged()));
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
+ QSignalSpy videoOutputChanged(&session, &QMediaCaptureSession::videoOutputChanged);
camera.setActive(true);
session.setCamera(&camera);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
+ QTRY_COMPARE(camera.isActive(), true);
recorder.record();
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::RecordingState);
QVERIFY(durationChanged.wait(2000));
session.setVideoOutput(&videoOutput);
- QTRY_COMPARE(videoOutputChanged.count(), 1);
+ QTRY_COMPARE(videoOutputChanged.size(), 1);
session.setVideoOutput(nullptr);
- QTRY_COMPARE(videoOutputChanged.count(), 2);
+ QTRY_COMPARE(videoOutputChanged.size(), 2);
session.setVideoOutput(&videoOutput);
- QTRY_COMPARE(videoOutputChanged.count(), 3);
+ QTRY_COMPARE(videoOutputChanged.size(), 3);
recorder.stop();
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::StoppedState);
@@ -655,20 +783,20 @@ void tst_QMediaCaptureSession::can_add_and_remove_recorders()
QMediaRecorder recorder2;
QMediaCaptureSession session;
- QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged()));
- QSignalSpy recorderChanged(&session, SIGNAL(recorderChanged()));
+ QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged);
+ QSignalSpy recorderChanged(&session, &QMediaCaptureSession::recorderChanged);
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 1);
+ QTRY_COMPARE(audioInputChanged.size(), 1);
session.setRecorder(&recorder);
- QTRY_COMPARE(recorderChanged.count(), 1);
+ QTRY_COMPARE(recorderChanged.size(), 1);
session.setRecorder(&recorder2);
- QTRY_COMPARE(recorderChanged.count(), 2);
+ QTRY_COMPARE(recorderChanged.size(), 2);
session.setRecorder(&recorder);
- QTRY_COMPARE(recorderChanged.count(), 3);
+ QTRY_COMPARE(recorderChanged.size(), 3);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
@@ -678,31 +806,31 @@ void tst_QMediaCaptureSession::can_move_Recorder_between_sessions()
{
QMediaCaptureSession session0;
QMediaCaptureSession session1;
- QSignalSpy recorderChanged0(&session0, SIGNAL(recorderChanged()));
- QSignalSpy recorderChanged1(&session1, SIGNAL(recorderChanged()));
+ QSignalSpy recorderChanged0(&session0, &QMediaCaptureSession::recorderChanged);
+ QSignalSpy recorderChanged1(&session1, &QMediaCaptureSession::recorderChanged);
{
QMediaRecorder recorder;
{
QMediaCaptureSession session2;
- QSignalSpy recorderChanged2(&session2, SIGNAL(recorderChanged()));
+ QSignalSpy recorderChanged2(&session2, &QMediaCaptureSession::recorderChanged);
session2.setRecorder(&recorder);
- QTRY_COMPARE(recorderChanged2.count(), 1);
+ QTRY_COMPARE(recorderChanged2.size(), 1);
}
QVERIFY(recorder.captureSession() == nullptr);
session0.setRecorder(&recorder);
- QTRY_COMPARE(recorderChanged0.count(), 1);
+ QTRY_COMPARE(recorderChanged0.size(), 1);
QVERIFY(session0.recorder() == &recorder);
QVERIFY(recorder.captureSession() == &session0);
session1.setRecorder(&recorder);
- QTRY_COMPARE(recorderChanged0.count(), 2);
+ QTRY_COMPARE(recorderChanged0.size(), 2);
QVERIFY(session0.recorder() == nullptr);
- QTRY_COMPARE(recorderChanged1.count(), 1);
+ QTRY_COMPARE(recorderChanged1.size(), 1);
QVERIFY(session1.recorder() == &recorder);
QVERIFY(recorder.captureSession() == &session1);
}
- QTRY_COMPARE(recorderChanged1.count(), 2);
+ QTRY_COMPARE(recorderChanged1.size(), 2);
QVERIFY(session1.recorder() == nullptr);
}
@@ -721,10 +849,10 @@ void tst_QMediaCaptureSession::can_record_AudioInput_with_null_AudioDevice()
QAudioInput input(nullDevice);
QMediaCaptureSession session;
- QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged()));
+ QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged);
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 1);
+ QTRY_COMPARE(audioInputChanged.size(), 1);
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
@@ -739,12 +867,14 @@ void tst_QMediaCaptureSession::can_record_Camera_with_null_CameraDevice()
QCamera camera(nullDevice);
QMediaCaptureSession session;
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
session.setCamera(&camera);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
camera.setActive(true);
+ QTRY_COMPARE(camera.isActive(), true);
+
recordOk(session);
QVERIFY(!QTest::currentTestFailed());
}
@@ -758,23 +888,23 @@ void tst_QMediaCaptureSession::recording_stops_when_recorder_removed()
QMediaRecorder recorder;
QMediaCaptureSession session;
- QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged()));
- QSignalSpy recorderChanged(&session, SIGNAL(recorderChanged()));
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
+ QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged);
+ QSignalSpy recorderChanged(&session, &QMediaCaptureSession::recorderChanged);
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
session.setAudioInput(&input);
- QTRY_COMPARE(audioInputChanged.count(), 1);
+ QTRY_COMPARE(audioInputChanged.size(), 1);
session.setRecorder(&recorder);
- QTRY_COMPARE(recorderChanged.count(), 1);
+ QTRY_COMPARE(recorderChanged.size(), 1);
recorder.record();
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::RecordingState);
QVERIFY(durationChanged.wait(2000));
session.setRecorder(nullptr);
- QTRY_COMPARE(recorderChanged.count(), 2);
+ QTRY_COMPARE(recorderChanged.size(), 2);
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::StoppedState);
QVERIFY(recorderErrorSignal.isEmpty());
@@ -795,38 +925,40 @@ void tst_QMediaCaptureSession::can_add_and_remove_ImageCapture()
QImageCapture capture;
QMediaCaptureSession session;
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
- QSignalSpy imageCaptureChanged(&session, SIGNAL(imageCaptureChanged()));
- QSignalSpy readyForCaptureChanged(&capture, SIGNAL(readyForCaptureChanged(bool)));
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
+ QSignalSpy imageCaptureChanged(&session, &QMediaCaptureSession::imageCaptureChanged);
+ QSignalSpy readyForCaptureChanged(&capture, &QImageCapture::readyForCaptureChanged);
QVERIFY(!capture.isAvailable());
QVERIFY(!capture.isReadyForCapture());
session.setImageCapture(&capture);
- QTRY_COMPARE(imageCaptureChanged.count(), 1);
+ QTRY_COMPARE(imageCaptureChanged.size(), 1);
QVERIFY(!capture.isAvailable());
QVERIFY(!capture.isReadyForCapture());
session.setCamera(&camera);
- QTRY_COMPARE(cameraChanged.count(), 1);
+ QTRY_COMPARE(cameraChanged.size(), 1);
QVERIFY(capture.isAvailable());
QVERIFY(!capture.isReadyForCapture());
camera.setActive(true);
- QTRY_COMPARE(readyForCaptureChanged.count(), 1);
+ QTRY_COMPARE(camera.isActive(), true);
+
+ QTRY_COMPARE(readyForCaptureChanged.size(), 1);
QVERIFY(capture.isReadyForCapture());
session.setImageCapture(nullptr);
- QTRY_COMPARE(imageCaptureChanged.count(), 2);
- QTRY_COMPARE(readyForCaptureChanged.count(), 2);
+ QTRY_COMPARE(imageCaptureChanged.size(), 2);
+ QTRY_COMPARE(readyForCaptureChanged.size(), 2);
QVERIFY(!capture.isAvailable());
QVERIFY(!capture.isReadyForCapture());
session.setImageCapture(&capture);
- QTRY_COMPARE(imageCaptureChanged.count(), 3);
- QTRY_COMPARE(readyForCaptureChanged.count(), 3);
+ QTRY_COMPARE(imageCaptureChanged.size(), 3);
+ QTRY_COMPARE(readyForCaptureChanged.size(), 3);
QVERIFY(capture.isAvailable());
QVERIFY(capture.isReadyForCapture());
}
@@ -835,31 +967,31 @@ void tst_QMediaCaptureSession::can_move_ImageCapture_between_sessions()
{
QMediaCaptureSession session0;
QMediaCaptureSession session1;
- QSignalSpy imageCaptureChanged0(&session0, SIGNAL(imageCaptureChanged()));
- QSignalSpy imageCaptureChanged1(&session1, SIGNAL(imageCaptureChanged()));
+ QSignalSpy imageCaptureChanged0(&session0, &QMediaCaptureSession::imageCaptureChanged);
+ QSignalSpy imageCaptureChanged1(&session1, &QMediaCaptureSession::imageCaptureChanged);
{
QImageCapture imageCapture;
{
QMediaCaptureSession session2;
- QSignalSpy imageCaptureChanged2(&session2, SIGNAL(imageCaptureChanged()));
+ QSignalSpy imageCaptureChanged2(&session2, &QMediaCaptureSession::imageCaptureChanged);
session2.setImageCapture(&imageCapture);
- QTRY_COMPARE(imageCaptureChanged2.count(), 1);
+ QTRY_COMPARE(imageCaptureChanged2.size(), 1);
}
QVERIFY(imageCapture.captureSession() == nullptr);
session0.setImageCapture(&imageCapture);
- QTRY_COMPARE(imageCaptureChanged0.count(), 1);
+ QTRY_COMPARE(imageCaptureChanged0.size(), 1);
QVERIFY(session0.imageCapture() == &imageCapture);
QVERIFY(imageCapture.captureSession() == &session0);
session1.setImageCapture(&imageCapture);
- QTRY_COMPARE(imageCaptureChanged0.count(), 2);
+ QTRY_COMPARE(imageCaptureChanged0.size(), 2);
QVERIFY(session0.imageCapture() == nullptr);
- QTRY_COMPARE(imageCaptureChanged1.count(), 1);
+ QTRY_COMPARE(imageCaptureChanged1.size(), 1);
QVERIFY(session1.imageCapture() == &imageCapture);
QVERIFY(imageCapture.captureSession() == &session1);
}
- QTRY_COMPARE(imageCaptureChanged1.count(), 2);
+ QTRY_COMPARE(imageCaptureChanged1.size(), 2);
QVERIFY(session1.imageCapture() == nullptr);
}
@@ -874,26 +1006,27 @@ void tst_QMediaCaptureSession::capture_is_not_available_when_Camera_is_null()
QImageCapture capture;
QMediaCaptureSession session;
- QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged()));
- QSignalSpy capturedSignal(&capture, SIGNAL(imageCaptured(int,QImage)));
- QSignalSpy readyForCaptureChanged(&capture, SIGNAL(readyForCaptureChanged(bool)));
+ QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged);
+ QSignalSpy capturedSignal(&capture, &QImageCapture::imageCaptured);
+ QSignalSpy readyForCaptureChanged(&capture, &QImageCapture::readyForCaptureChanged);
session.setImageCapture(&capture);
session.setCamera(&camera);
camera.setActive(true);
+ QTRY_COMPARE(camera.isActive(), true);
- QTRY_COMPARE(readyForCaptureChanged.count(), 1);
+ QTRY_COMPARE(readyForCaptureChanged.size(), 1);
QVERIFY(capture.isReadyForCapture());
QVERIFY(capture.capture() >= 0);
- QTRY_COMPARE(capturedSignal.count(), 1);
+ QTRY_COMPARE(capturedSignal.size(), 1);
QVERIFY(capture.isReadyForCapture());
- int readyCount = readyForCaptureChanged.count();
+ int readyCount = readyForCaptureChanged.size();
session.setCamera(nullptr);
- QTRY_COMPARE(readyForCaptureChanged.count(), readyCount + 1);
+ QTRY_COMPARE(readyForCaptureChanged.size(), readyCount + 1);
QVERIFY(!capture.isReadyForCapture());
QVERIFY(!capture.isAvailable());
QVERIFY(capture.capture() < 0);
@@ -910,33 +1043,34 @@ void tst_QMediaCaptureSession::can_add_ImageCapture_and_capture_during_recording
QMediaCaptureSession session;
QMediaRecorder recorder;
- QSignalSpy recorderChanged(&session, SIGNAL(recorderChanged()));
- QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &)));
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
- QSignalSpy imageCaptureChanged(&session, SIGNAL(imageCaptureChanged()));
- QSignalSpy readyForCaptureChanged(&capture, SIGNAL(readyForCaptureChanged(bool)));
- QSignalSpy capturedSignal(&capture, SIGNAL(imageCaptured(int,QImage)));
+ QSignalSpy recorderChanged(&session, &QMediaCaptureSession::recorderChanged);
+ QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred);
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
+ QSignalSpy imageCaptureChanged(&session, &QMediaCaptureSession::imageCaptureChanged);
+ QSignalSpy readyForCaptureChanged(&capture, &QImageCapture::readyForCaptureChanged);
+ QSignalSpy capturedSignal(&capture, &QImageCapture::imageCaptured);
session.setCamera(&camera);
camera.setActive(true);
+ QTRY_COMPARE(camera.isActive(), true);
session.setRecorder(&recorder);
- QTRY_COMPARE(recorderChanged.count(), 1);
+ QTRY_COMPARE(recorderChanged.size(), 1);
recorder.record();
QTRY_VERIFY(recorder.recorderState() == QMediaRecorder::RecordingState);
QVERIFY(durationChanged.wait(2000));
session.setImageCapture(&capture);
- QTRY_COMPARE(imageCaptureChanged.count(), 1);
- QTRY_COMPARE(readyForCaptureChanged.count(), 1);
+ QTRY_COMPARE(imageCaptureChanged.size(), 1);
+ QTRY_COMPARE(readyForCaptureChanged.size(), 1);
QVERIFY(capture.isReadyForCapture());
QVERIFY(capture.capture() >= 0);
- QTRY_COMPARE(capturedSignal.count(), 1);
+ QTRY_COMPARE(capturedSignal.size(), 1);
session.setImageCapture(nullptr);
- QVERIFY(readyForCaptureChanged.count() >= 2);
+ QVERIFY(readyForCaptureChanged.size() >= 2);
QVERIFY(!capture.isReadyForCapture());
recorder.stop();
@@ -966,28 +1100,26 @@ void tst_QMediaCaptureSession::testAudioMute()
recorder.setOutputLocation(QStringLiteral("test"));
QSignalSpy spy(&audioInput, &QAudioInput::mutedChanged);
- QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64)));
+ QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged);
QMediaFormat format;
- format.setAudioCodec(QMediaFormat::AudioCodec::MP3);
+ format.setAudioCodec(QMediaFormat::AudioCodec::Wave);
recorder.setMediaFormat(format);
- recorder.record();
audioInput.setMuted(true);
- QCOMPARE(spy.count(), 1);
+ recorder.record();
+
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.last()[0], true);
QTRY_VERIFY_WITH_TIMEOUT(recorder.recorderState() == QMediaRecorder::RecordingState, 2000);
QVERIFY(durationChanged.wait(2000));
- audioInput.setMuted(false);
-
- QCOMPARE(spy.count(), 2);
- QCOMPARE(spy.last()[0], false);
-
recorder.stop();
+ QTRY_COMPARE(recorder.recorderState(), QMediaRecorder::StoppedState);
+
QString actualLocation = recorder.actualLocation().toLocalFile();
QVERIFY2(!actualLocation.isEmpty(), "Recorder did not save a file");
@@ -1016,6 +1148,33 @@ void tst_QMediaCaptureSession::testAudioMute()
decoder.stop();
QFile(actualLocation).remove();
+
+ audioInput.setMuted(false);
+
+ QCOMPARE(spy.size(), 2);
+ QCOMPARE(spy.last()[0], false);
+}
+
+void tst_QMediaCaptureSession::can_reset_audio_input_output()
+{
+ QAudioInput in1;
+ QMediaCaptureSession session;
+ session.setAudioInput(&in1);
+ QVERIFY(session.audioInput() != nullptr);
+ QAudioInput in2;
+ QSignalSpy changeSpy1(&session, &QMediaCaptureSession::audioInputChanged);
+ session.setAudioInput(&in2);
+ QVERIFY(session.audioInput() != nullptr);
+ QCOMPARE(changeSpy1.count(), 1);
+
+ QAudioOutput out1;
+ session.setAudioOutput(&out1);
+ QVERIFY(session.audioOutput() != nullptr);
+ QSignalSpy changeSpy2(&session, &QMediaCaptureSession::audioOutputChanged);
+ QAudioOutput out2;
+ session.setAudioOutput(&out2);
+ QVERIFY(session.audioOutput() != nullptr);
+ QCOMPARE(changeSpy2.count(), 1);
}
QTEST_MAIN(tst_QMediaCaptureSession)
diff --git a/tests/auto/integration/qmediaplayerbackend/BLACKLIST b/tests/auto/integration/qmediaplayerbackend/BLACKLIST
index e107df861..1f24e07a1 100644
--- a/tests/auto/integration/qmediaplayerbackend/BLACKLIST
+++ b/tests/auto/integration/qmediaplayerbackend/BLACKLIST
@@ -1,7 +1,6 @@
# Media player plugin not built at the moment on this platform
opensuse-13.1 64bit
-
[playlist]
redhatenterpriselinuxworkstation-6.6
diff --git a/tests/auto/integration/qmediaplayerbackend/CMakeLists.txt b/tests/auto/integration/qmediaplayerbackend/CMakeLists.txt
index 3848c6099..39684a32d 100644
--- a/tests/auto/integration/qmediaplayerbackend/CMakeLists.txt
+++ b/tests/auto/integration/qmediaplayerbackend/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmediaplayerbackend.pro.
#####################################################################
@@ -13,11 +16,23 @@ list(APPEND testdata_resource_files ${test_data_glob})
qt_internal_add_test(tst_qmediaplayerbackend
SOURCES
../shared/mediafileselector.h
+ ../shared/mediabackendutils.h
+ ../shared/testvideosink.h
+ mediaplayerstate.h
+ fake.h
+ fixture.h
+ server.h
tst_qmediaplayerbackend.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
+ Qt::MultimediaQuickPrivate
+ Qt::Qml
+ Qt::Quick
+ Qt::QuickPrivate
TESTDATA ${testdata_resource_files}
+ INCLUDE_DIRECTORIES
+ ../shared/
)
qt_internal_add_resource(tst_qmediaplayerbackend "testdata"
@@ -25,6 +40,7 @@ qt_internal_add_resource(tst_qmediaplayerbackend "testdata"
"/"
FILES
${testdata_resource_files}
+ "LazyLoad.qml"
)
## Scopes:
diff --git a/tests/auto/integration/qmediaplayerbackend/LazyLoad.qml b/tests/auto/integration/qmediaplayerbackend/LazyLoad.qml
new file mode 100644
index 000000000..04af13186
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/LazyLoad.qml
@@ -0,0 +1,52 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtMultimedia
+
+Rectangle {
+ id: root
+ width: 600
+ height: 800
+ color: "black"
+
+ Component {
+ id: videoOutputComponent
+
+ Item {
+ objectName: "videoPlayer"
+ property alias mediaPlayer: mediaPlayer
+ property alias videoOutput: videoOutput
+ property alias videoSink: videoOutput.videoSink
+
+ property alias playbackState: mediaPlayer.playbackState
+ property alias error: mediaPlayer.error
+
+
+ MediaPlayer {
+ id: mediaPlayer
+ objectName: "mediaPlayer"
+ source: "qrc:/testdata/colors.mp4"
+ }
+ VideoOutput {
+ id: videoOutput
+ objectName: "videoOutput"
+ anchors.fill: parent
+ }
+ }
+ }
+
+ Loader {
+ id: loader
+ objectName: "loader"
+ sourceComponent: videoOutputComponent
+ anchors.fill: parent
+ active: false
+ onActiveChanged: {
+ if (active) {
+ loader.item.mediaPlayer.videoOutput = loader.item.videoOutput
+ loader.item.mediaPlayer.play()
+ }
+ }
+ }
+}
diff --git a/tests/auto/integration/qmediaplayerbackend/fake.h b/tests/auto/integration/qmediaplayerbackend/fake.h
new file mode 100644
index 000000000..9a68741d0
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/fake.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef FAKE_H
+#define FAKE_H
+
+#include <testvideosink.h>
+
+QT_USE_NAMESPACE
+
+class TestVideoOutput : public QObject
+{
+ Q_OBJECT
+public:
+ TestVideoOutput() = default;
+
+ Q_INVOKABLE QVideoSink *videoSink() { return &m_sink; }
+
+ TestVideoSink m_sink;
+};
+
+inline void setVideoSinkAsyncFramesCounter(QVideoSink &sink, std::atomic_int &counter)
+{
+ QObject::connect(
+ &sink, &QVideoSink::videoFrameChanged, &sink, [&counter]() { ++counter; },
+ Qt::DirectConnection);
+}
+
+#endif // FAKE_H
diff --git a/tests/auto/integration/qmediaplayerbackend/fixture.h b/tests/auto/integration/qmediaplayerbackend/fixture.h
new file mode 100644
index 000000000..bf539d7ff
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/fixture.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef FIXTURE_H
+#define FIXTURE_H
+
+#include <qmediaplayer.h>
+#include <qaudiooutput.h>
+#include <qtest.h>
+#include <qsignalspy.h>
+
+#include "fake.h"
+#include "testvideosink.h"
+
+QT_USE_NAMESPACE
+
+struct Fixture : QObject
+{
+ Q_OBJECT
+public:
+ Fixture()
+ : playbackStateChanged(&player, &QMediaPlayer::playbackStateChanged),
+ errorOccurred(&player, &QMediaPlayer::errorOccurred),
+ sourceChanged(&player, &QMediaPlayer::sourceChanged),
+ mediaStatusChanged(&player, &QMediaPlayer::mediaStatusChanged),
+ positionChanged(&player, &QMediaPlayer::positionChanged),
+ durationChanged(&player, &QMediaPlayer::durationChanged),
+ playbackRateChanged(&player, &QMediaPlayer::playbackRateChanged),
+ metadataChanged(&player, &QMediaPlayer::metaDataChanged),
+ volumeChanged(&output, &QAudioOutput::volumeChanged),
+ mutedChanged(&output, &QAudioOutput::mutedChanged),
+ bufferProgressChanged(&player, &QMediaPlayer::bufferProgressChanged),
+ destroyed(&player, &QObject::destroyed)
+ {
+ setVideoSinkAsyncFramesCounter(surface, framesCount);
+
+ player.setAudioOutput(&output);
+ player.setVideoOutput(&surface);
+ }
+
+ void clearSpies()
+ {
+ playbackStateChanged.clear();
+ errorOccurred.clear();
+ sourceChanged.clear();
+ mediaStatusChanged.clear();
+ positionChanged.clear();
+ durationChanged.clear();
+ playbackRateChanged.clear();
+ metadataChanged.clear();
+ volumeChanged.clear();
+ mutedChanged.clear();
+ bufferProgressChanged.clear();
+ destroyed.clear();
+ }
+
+ QMediaPlayer player;
+ QAudioOutput output;
+ TestVideoSink surface;
+ std::atomic_int framesCount = 0;
+
+ QSignalSpy playbackStateChanged;
+ QSignalSpy errorOccurred;
+ QSignalSpy sourceChanged;
+ QSignalSpy mediaStatusChanged;
+ QSignalSpy positionChanged;
+ QSignalSpy durationChanged;
+ QSignalSpy playbackRateChanged;
+ QSignalSpy metadataChanged;
+ QSignalSpy volumeChanged;
+ QSignalSpy mutedChanged;
+ QSignalSpy bufferProgressChanged;
+ QSignalSpy destroyed;
+};
+
+// Helper to create an object that is comparable to a QSignalSpy
+using SignalList = QList<QList<QVariant>>;
+
+#endif // FIXTURE_H
diff --git a/tests/auto/integration/qmediaplayerbackend/mediaplayerstate.h b/tests/auto/integration/qmediaplayerbackend/mediaplayerstate.h
new file mode 100644
index 000000000..d9f2cc875
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/mediaplayerstate.h
@@ -0,0 +1,167 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MEDIAPLAYERSTATE_H
+#define MEDIAPLAYERSTATE_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qurl.h>
+#include <QtMultimedia/qaudiooutput.h>
+#include <QtMultimedia/qmediametadata.h>
+#include <QtMultimedia/qmediaplayer.h>
+#include <QtMultimedia/qmediatimerange.h>
+#include <QtTest/qtestcase.h>
+
+#include <optional>
+
+QT_USE_NAMESPACE
+
+/*!
+ * Helper class that simplifies testing the state of
+ * a media player against an expected state.
+ *
+ * Use the COMPARE_MEDIA_PLAYER_STATE_EQ macro to compare
+ * the media player state against the expected state.
+ *
+ * Individual properties can be ignored by comparison by
+ * assigning the std::nullopt value to the property of the
+ * expected state.
+ */
+struct MediaPlayerState
+{
+ std::optional<QList<QMediaMetaData>> audioTracks;
+ std::optional<QList<QMediaMetaData>> videoTracks;
+ std::optional<QList<QMediaMetaData>> subtitleTracks;
+ std::optional<int> activeAudioTrack;
+ std::optional<int> activeVideoTrack;
+ std::optional<int> activeSubtitleTrack;
+ std::optional<QAudioOutput*> audioOutput;
+ std::optional<QObject*> videoOutput;
+ std::optional<QVideoSink*> videoSink;
+ std::optional<QUrl> source;
+ std::optional<QIODevice const*> sourceDevice;
+ std::optional<QMediaPlayer::PlaybackState> playbackState;
+ std::optional<QMediaPlayer::MediaStatus> mediaStatus;
+ std::optional<qint64> duration;
+ std::optional<qint64> position;
+ std::optional<bool> hasAudio;
+ std::optional<bool> hasVideo;
+ std::optional<float> bufferProgress;
+ std::optional<QMediaTimeRange> bufferedTimeRange;
+ std::optional<bool> isSeekable;
+ std::optional<qreal> playbackRate;
+ std::optional<bool> isPlaying;
+ std::optional<int> loops;
+ std::optional<QMediaPlayer::Error> error;
+ std::optional<bool> isAvailable;
+ std::optional<QMediaMetaData> metaData;
+
+ /*!
+ * Read the state from an existing media player
+ */
+ explicit MediaPlayerState(const QMediaPlayer &player)
+ : audioTracks{ player.audioTracks() },
+ videoTracks{ player.videoTracks() },
+ subtitleTracks{ player.subtitleTracks() },
+ activeAudioTrack{ player.activeAudioTrack() },
+ activeVideoTrack{ player.activeVideoTrack() },
+ activeSubtitleTrack{ player.activeSubtitleTrack() },
+ audioOutput{ player.audioOutput() },
+ videoOutput{ player.videoOutput() },
+ videoSink{ player.videoSink() },
+ source{ player.source() },
+ sourceDevice{ player.sourceDevice() },
+ playbackState{ player.playbackState() },
+ mediaStatus{ player.mediaStatus() },
+ duration{ player.duration() },
+ position{ player.position() },
+ hasAudio{ player.hasAudio() },
+ hasVideo{ player.hasVideo() },
+ bufferProgress{ player.bufferProgress() },
+ bufferedTimeRange{ player.bufferedTimeRange() },
+ isSeekable{ player.isSeekable() },
+ playbackRate{ player.playbackRate() },
+ isPlaying{ player.isPlaying() },
+ loops{ player.loops() },
+ error{ player.error() },
+ isAvailable{ player.isAvailable() },
+ metaData{ player.metaData() }
+ {
+ }
+
+ /*!
+ * Creates the default state of a media player. The default state
+ * is the state the player should have when it is default constructed.
+ */
+ static MediaPlayerState defaultState()
+ {
+ MediaPlayerState state{};
+ state.audioTracks = QList<QMediaMetaData>{};
+ state.videoTracks = QList<QMediaMetaData>{};
+ state.subtitleTracks = QList<QMediaMetaData>{};
+ state.activeAudioTrack = -1;
+ state.activeVideoTrack = -1;
+ state.activeSubtitleTrack = -1;
+ state.audioOutput = nullptr;
+ state.videoOutput = nullptr;
+ state.videoSink = nullptr;
+ state.source = QUrl{};
+ state.sourceDevice = nullptr;
+ state.playbackState = QMediaPlayer::StoppedState;
+ state.mediaStatus = QMediaPlayer::NoMedia;
+ state.duration = 0;
+ state.position = 0;
+ state.hasAudio = false;
+ state.hasVideo = false;
+ state.bufferProgress = 0.0f;
+ state.bufferedTimeRange = QMediaTimeRange{};
+ state.isSeekable = false;
+ state.playbackRate = static_cast<qreal>(1);
+ state.isPlaying = false;
+ state.loops = 1;
+ state.error = QMediaPlayer::NoError;
+ state.isAvailable = true;
+ state.metaData = QMediaMetaData{};
+ return state;
+ }
+
+private:
+ MediaPlayerState() = default;
+
+};
+
+#define COMPARE_EQ_IGNORE_OPTIONAL(actual, expected) \
+ do { \
+ if ((expected).has_value()) { \
+ QCOMPARE_EQ(actual, expected); \
+ } \
+ } while (false)
+
+#define COMPARE_MEDIA_PLAYER_STATE_EQ(actual, expected) \
+ do { \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).audioTracks, (expected).audioTracks); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).videoTracks, (expected).videoTracks); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).subtitleTracks, (expected).subtitleTracks); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).activeAudioTrack, (expected).activeAudioTrack); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).activeVideoTrack, (expected).activeVideoTrack); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).activeSubtitleTrack, (expected).activeSubtitleTrack); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).source, (expected).source); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).sourceDevice, (expected).sourceDevice); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).playbackState, (expected).playbackState); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).mediaStatus, (expected).mediaStatus); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).duration, (expected).duration); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).position, (expected).position); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).hasAudio, (expected).hasAudio); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).hasVideo, (expected).hasVideo); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).bufferProgress, (expected).bufferProgress); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).bufferedTimeRange, (expected).bufferedTimeRange); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).isSeekable, (expected).isSeekable); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).playbackRate, (expected).playbackRate); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).isPlaying, (expected).isPlaying); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).loops, (expected).loops); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).error, (expected).error); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).isAvailable, (expected).isAvailable); \
+ COMPARE_EQ_IGNORE_OPTIONAL((actual).metaData, (expected).metaData); \
+ } while (false)
+
+#endif // MEDIAPLAYERSTATE_H
diff --git a/tests/auto/integration/qmediaplayerbackend/server.h b/tests/auto/integration/qmediaplayerbackend/server.h
new file mode 100644
index 000000000..a6fde1a7a
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/server.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef SERVER_H
+#define SERVER_H
+
+#include <private/qglobal_p.h>
+
+#ifdef QT_FEATURE_network
+
+#include <qstring.h>
+#include <qtcpserver.h>
+#include <qtest.h>
+#include <qurl.h>
+
+QT_USE_NAMESPACE
+
+class UnResponsiveRtspServer : public QObject
+{
+ Q_OBJECT
+public:
+ UnResponsiveRtspServer() : m_server{ new QTcpServer{ this } }
+ {
+ connect(m_server, &QTcpServer::newConnection, this, [&] { m_connected = true; });
+ }
+
+ bool listen() { return m_server->listen(QHostAddress::LocalHost); }
+
+ bool waitForConnection()
+ {
+ return QTest::qWaitFor([this] { return m_connected; });
+ }
+
+ QUrl address() const
+ {
+ return QUrl{ QString{ "rtsp://%1:%2" }
+ .arg(m_server->serverAddress().toString())
+ .arg(m_server->serverPort()) };
+ }
+
+private:
+ QTcpServer *m_server;
+ bool m_connected = false;
+};
+
+#endif // QT_FEATURE_network
+
+#endif // SERVER_H
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/3colors_with_sound_1s.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/3colors_with_sound_1s.mp4
new file mode 100644
index 000000000..96ae2e4e3
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/3colors_with_sound_1s.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/audio_video_with_jpg_thumbnail.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/audio_video_with_jpg_thumbnail.mp4
new file mode 100644
index 000000000..dfeec1546
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/audio_video_with_jpg_thumbnail.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/audio_video_with_png_thumbnail.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/audio_video_with_png_thumbnail.mp4
new file mode 100644
index 000000000..147adf777
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/audio_video_with_png_thumbnail.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/busAv1.webm b/tests/auto/integration/qmediaplayerbackend/testdata/busAv1.webm
new file mode 100644
index 000000000..048d02e8a
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/busAv1.webm
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix.mp4
new file mode 100644
index 000000000..a3661b9d2
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_180_deg_clockwise.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_180_deg_clockwise.mp4
new file mode 100644
index 000000000..9a60850d6
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_180_deg_clockwise.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_270_deg_clockwise.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_270_deg_clockwise.mp4
new file mode 100644
index 000000000..b3ecd486d
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_270_deg_clockwise.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_90_deg_clockwise.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_90_deg_clockwise.mp4
new file mode 100644
index 000000000..dc620d05f
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/color_matrix_90_deg_clockwise.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/duration_issues.webm b/tests/auto/integration/qmediaplayerbackend/testdata/duration_issues.webm
new file mode 100644
index 000000000..87b737949
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/duration_issues.webm
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3 b/tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3
index 2435f65b8..892c2a89e 100644
--- a/tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/nokia-tune.mp3
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/one_red_frame.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/one_red_frame.mp4
new file mode 100644
index 000000000..6b67a3433
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/one_red_frame.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/par_2_3.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/par_2_3.mp4
new file mode 100644
index 000000000..b0d9b3593
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/par_2_3.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/par_3_2.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/par_3_2.mp4
new file mode 100644
index 000000000..55baed13e
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/par_3_2.mp4
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
index 85d132368..4e96aceb5 100644
--- a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
+++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
@@ -1,47 +1,71 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
#include "qmediaplayer.h"
+#include "mediaplayerstate.h"
+#include "fake.h"
+#include "fixture.h"
+#include "server.h"
#include <qmediametadata.h>
#include <qaudiobuffer.h>
#include <qvideosink.h>
#include <qvideoframe.h>
#include <qaudiooutput.h>
-
-#include "../shared/mediafileselector.h"
-//TESTED_COMPONENT=src/multimedia
-
+#if QT_CONFIG(process)
+#include <qprocess.h>
+#endif
+#include <private/qglobal_p.h>
+#ifdef QT_FEATURE_network
+#include <qtcpserver.h>
+#endif
+#include <qmediatimerange.h>
+#include <private/qplatformvideosink_p.h>
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qqmlproperty.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
+#include <QtQuick/private/qquickloader_p.h>
+
+#include "mediafileselector.h"
+#include "mediabackendutils.h"
#include <QtMultimedia/private/qtmultimedia-config_p.h>
+#include "private/qquickvideooutput_p.h"
+
+#include <array>
QT_USE_NAMESPACE
+using namespace Qt::Literals;
+
+namespace {
+static qreal colorDifference(QRgb first, QRgb second)
+{
+ const auto diffVector = QVector3D(qRed(first), qGreen(first), qBlue(first))
+ - QVector3D(qRed(second), qGreen(second), qBlue(second));
+ static const auto normalizationFactor = 1. / (255 * qSqrt(3.));
+ return diffVector.length() * normalizationFactor;
+}
+
+template <typename It>
+It findSimilarColor(It it, It end, QRgb color)
+{
+ return std::min_element(it, end, [color](QRgb first, QRgb second) {
+ return colorDifference(first, color) < colorDifference(second, color);
+ });
+}
+
+template <typename Colors>
+auto findSimilarColorIndex(const Colors &colors, QRgb color)
+{
+ return std::distance(std::begin(colors),
+ findSimilarColor(std::begin(colors), std::end(colors), color));
+}
+}
+
/*
This is the backend conformance test.
@@ -53,19 +77,70 @@ class tst_QMediaPlayerBackend : public QObject
{
Q_OBJECT
public slots:
- void init();
- void cleanup();
void initTestCase();
+ void init() { m_fixture = std::make_unique<Fixture>(); }
+ void cleanup() { m_fixture = nullptr; }
private slots:
- void construction();
- void loadMedia();
- void unloadMedia();
- void loadMediaInLoadingState();
- void playPauseStop();
+ void testMediaFilesAreSupported();
+ void destructor_cancelsPreviousSetSource_whenServerDoesNotRespond();
+ void destructor_emitsOnlyQObjectDestroyedSignal_whenPlayerIsRunning();
+
+ void getters_returnExpectedValues_whenCalledWithDefaultConstructedPlayer_data() const;
+ void getters_returnExpectedValues_whenCalledWithDefaultConstructedPlayer() const;
+
+ void setSource_emitsSourceChanged_whenCalledWithInvalidFile();
+ void setSource_emitsError_whenCalledWithInvalidFile();
+ void setSource_emitsMediaStatusChange_whenCalledWithInvalidFile();
+ void setSource_doesNotEmitPlaybackStateChange_whenCalledWithInvalidFile();
+ void setSource_setsSourceMediaStatusAndError_whenCalledWithInvalidFile();
+ void setSource_silentlyCancelsPreviousCall_whenServerDoesNotRespond();
+ void setSource_changesSourceAndMediaStatus_whenCalledWithValidFile();
+ void setSource_updatesExpectedAttributes_whenMediaHasLoaded();
+ void setSource_stopsAndEntersErrorState_whenPlayerWasPlaying();
+ void setSource_loadsAudioTrack_whenCalledWithValidWavFile();
+ void setSource_resetsState_whenCalledWithEmptyUrl();
+ void setSource_loadsNewMedia_whenPreviousMediaWasFullyLoaded();
+ void setSource_loadsCorrectTracks_whenLoadingMediaInSequence();
+ void setSource_remainsInStoppedState_whenPlayerWasStopped();
+ void setSource_entersStoppedState_whenPlayerWasPlaying();
+
+ void setSourceAndPlay_setCorrectVideoSize_whenVideoHasNonStandardPixelAspectRatio_data();
+ void setSourceAndPlay_setCorrectVideoSize_whenVideoHasNonStandardPixelAspectRatio();
+
+ void pause_doesNotChangePlayerState_whenInvalidFileLoaded();
+ void pause_doesNothing_whenMediaIsNotLoaded();
+ void pause_entersPauseState_whenPlayerWasPlaying();
+
+ void play_resetsErrorState_whenCalledWithInvalidFile();
+ void play_resumesPlaying_whenValidMediaIsProvidedAfterInvalidMedia();
+ void play_doesNothing_whenMediaIsNotLoaded();
+ void play_setsPlaybackStateAndMediaStatus_whenValidFileIsLoaded();
+ void play_startsPlaybackAndChangesPosition_whenValidFileIsLoaded();
+ void play_doesNotEnterMediaLoadingState_whenResumingPlayingAfterStop();
+ void playAndSetSource_emitsExpectedSignalsAndStopsPlayback_whenSetSourceWasCalledWithEmptyUrl();
+ void play_createsFramesWithExpectedContentAndIncreasingFrameTime_whenPlayingRtspMediaStream();
+ void play_waitsForLastFrameEnd_whenPlayingVideoWithLongFrames();
+ void play_startsPlayback_withAndWithoutOutputsConnected();
+ void play_startsPlayback_withAndWithoutOutputsConnected_data();
+
+ void stop_entersStoppedState_whenPlayerWasPaused();
+ void stop_setsPositionToZero_afterPlayingToEndOfMedia();
+
+ void playbackRate_returnsOne_byDefault();
+ void setPlaybackRate_changesPlaybackRateAndEmitsSignal_data();
+ void setPlaybackRate_changesPlaybackRateAndEmitsSignal();
+
+ void setVolume_changesVolume_whenVolumeIsInRange();
+ void setVolume_clampsToRange_whenVolumeIsOutsideRange();
+ void setVolume_doesNotChangeMutedState();
+
+ void setMuted_changesMutedState_whenMutedStateChanged();
+ void setMuted_doesNotChangeVolume();
+
void processEOS();
void deleteLaterAtEOS();
- void volumeAndMuted();
+
void volumeAcrossFiles_data();
void volumeAcrossFiles();
void initialVolume();
@@ -73,513 +148,1357 @@ private slots:
void seekInStoppedState();
void subsequentPlayback();
void surfaceTest();
-// void multipleSurfaces();
void metadata();
+ void metadata_returnsMetadataWithThumbnail_whenMediaHasThumbnail_data();
+ void metadata_returnsMetadataWithThumbnail_whenMediaHasThumbnail();
void playerStateAtEOS();
void playFromBuffer();
void audioVideoAvailable();
void isSeekable();
void positionAfterSeek();
- void videoDimensions();
+ void pause_rendersVideoAtCorrectResolution_data();
+ void pause_rendersVideoAtCorrectResolution();
void position();
void multipleMediaPlayback();
+ void multiplePlaybackRateChangingStressTest();
+ void multipleSeekStressTest();
+ void setPlaybackRate_changesActualRateAndFramesRenderingTime_data();
+ void setPlaybackRate_changesActualRateAndFramesRenderingTime();
+ void durationDetectionIssues_data();
+ void durationDetectionIssues();
+ void finiteLoops();
+ void infiniteLoops();
+ void seekOnLoops();
+ void changeLoopsOnTheFly();
+ void seekAfterLoopReset();
+ void changeVideoOutputNoFramesLost();
+ void cleanSinkAndNoMoreFramesAfterStop();
+ void lazyLoadVideo();
+ void videoSinkSignals();
+ void nonAsciiFileName();
+ void setMedia_setsVideoSinkSize_beforePlaying();
+ void play_playsRotatedVideoOutput_whenVideoFileHasOrientationMetadata_data();
+ void play_playsRotatedVideoOutput_whenVideoFileHasOrientationMetadata();
private:
- QUrl selectVideoFile(const QStringList& mediaCandidates);
- bool isWavSupported();
-
- //one second local wav file
- QUrl localWavFile;
- QUrl localWavFile2;
- QUrl localVideoFile;
- QUrl localVideoFile2;
- QUrl videoDimensionTestFile;
- QUrl localCompressedSoundFile;
- QUrl localFileWithMetadata;
-
- bool m_inCISystem;
+ QUrl selectVideoFile(const QStringList &mediaCandidates);
+
+ bool canCreateRtspStream() const;
+#if QT_CONFIG(process)
+ std::unique_ptr<QProcess> createRtspStreamProcess(QString fileName, QString outputUrl);
+#endif
+ void detectVlcCommand();
+
+ // one second local wav file
+ MaybeUrl m_localWavFile = QUnexpect{};
+ MaybeUrl m_localWavFile2 = QUnexpect{};
+ MaybeUrl m_localVideoFile = QUnexpect{};
+ MaybeUrl m_localVideoFile2 = QUnexpect{};
+ MaybeUrl m_av1File = QUnexpect{};
+ MaybeUrl m_videoDimensionTestFile = QUnexpect{};
+ MaybeUrl m_localCompressedSoundFile = QUnexpect{};
+ MaybeUrl m_localFileWithMetadata = QUnexpect{};
+ MaybeUrl m_localVideoFile3ColorsWithSound = QUnexpect{};
+ MaybeUrl m_videoFileWithJpegThumbnail = QUnexpect{};
+ MaybeUrl m_videoFileWithPngThumbnail = QUnexpect{};
+ MaybeUrl m_oneRedFrameVideo = QUnexpect{};
+ MaybeUrl m_192x108_PAR_2_3_Video = QUnexpect{};
+ MaybeUrl m_192x108_PAR_3_2_Video = QUnexpect{};
+ MaybeUrl m_colorMatrixVideo = QUnexpect{};
+ MaybeUrl m_colorMatrix90degClockwiseVideo = QUnexpect{};
+ MaybeUrl m_colorMatrix180degClockwiseVideo = QUnexpect{};
+ MaybeUrl m_colorMatrix270degClockwiseVideo = QUnexpect{};
+
+ MediaFileSelector m_mediaSelector;
+
+ const std::array<QRgb, 3> m_video3Colors = { { 0xFF0000, 0x00FF00, 0x0000FF } };
+ QString m_vlcCommand;
+
+ std::unique_ptr<Fixture> m_fixture;
};
-/*
- This is a simple video surface which records all presented frames.
-*/
-class TestVideoSink : public QVideoSink
+static bool commandExists(const QString &command)
{
- Q_OBJECT
-public:
- explicit TestVideoSink(bool storeFrames = true)
- : m_storeFrames(storeFrames)
- {
- connect(this, &QVideoSink::videoFrameChanged, this, &TestVideoSink::addVideoFrame);
- }
-public Q_SLOTS:
- void addVideoFrame(const QVideoFrame &frame) {
- if (m_storeFrames)
- m_frameList.append(frame);
- ++m_totalFrames;
- }
+#if defined(Q_OS_WINDOWS)
+ static constexpr QChar separator = ';';
+#else
+ static constexpr QChar separator = ':';
+#endif
+ static const QStringList pathDirs = qEnvironmentVariable("PATH").split(separator);
+ return std::any_of(pathDirs.cbegin(), pathDirs.cend(), [&command](const QString &dir) {
+ QString fullPath = QDir(dir).filePath(command);
+ return QFile::exists(fullPath);
+ });
+}
-public:
- QList<QVideoFrame> m_frameList;
- int m_totalFrames = 0; // used instead of the list when frames are not stored
+static std::unique_ptr<QTemporaryFile> copyResourceToTemporaryFile(QString resource,
+ QString filePattern)
+{
+ QFile resourceFile(resource);
+ if (!resourceFile.open(QIODeviceBase::ReadOnly))
+ return nullptr;
-private:
- bool m_storeFrames;
-};
+ auto temporaryFile = std::make_unique<QTemporaryFile>(filePattern);
+ if (!temporaryFile->open())
+ return nullptr;
-void tst_QMediaPlayerBackend::init()
-{
+ QByteArray bytes = resourceFile.readAll();
+ QDataStream stream(temporaryFile.get());
+ stream.writeRawData(bytes.data(), bytes.length());
+
+ temporaryFile->close();
+
+ return temporaryFile;
}
-QUrl tst_QMediaPlayerBackend::selectVideoFile(const QStringList& mediaCandidates)
+void tst_QMediaPlayerBackend::detectVlcCommand()
{
- // select supported video format
- QMediaPlayer player;
- TestVideoSink *surface = new TestVideoSink;
- player.setVideoOutput(surface);
+ m_vlcCommand = qEnvironmentVariable("QT_VLC_COMMAND");
- QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error)));
+ if (!m_vlcCommand.isEmpty())
+ return;
- for (const QString &s : mediaCandidates) {
- QFileInfo videoFile(s);
- if (!videoFile.exists())
- continue;
- QUrl media = QUrl(QUrl::fromLocalFile(videoFile.absoluteFilePath()));
- player.setSource(media);
- player.pause();
+#if defined(Q_OS_WINDOWS)
+ m_vlcCommand = "vlc.exe";
+#else
+ m_vlcCommand = "vlc";
+#endif
+ if (commandExists(m_vlcCommand))
+ return;
- for (int i = 0; i < 2000 && surface->m_frameList.isEmpty() && errorSpy.isEmpty(); i+=50) {
- QTest::qWait(50);
- }
+ m_vlcCommand.clear();
- if (!surface->m_frameList.isEmpty() && errorSpy.isEmpty()) {
- return media;
- }
- errorSpy.clear();
- }
+#if defined(Q_OS_MACOS)
+ m_vlcCommand = "/Applications/VLC.app/Contents/MacOS/VLC";
+#elif defined(Q_OS_WINDOWS)
+ m_vlcCommand = "C:/Program Files/VideoLAN/VLC/vlc.exe";
+#endif
- return QUrl();
+ if (!QFile::exists(m_vlcCommand))
+ m_vlcCommand.clear();
}
-bool tst_QMediaPlayerBackend::isWavSupported()
+bool tst_QMediaPlayerBackend::canCreateRtspStream() const
{
- return !localWavFile.isEmpty();
+ return !m_vlcCommand.isEmpty();
}
void tst_QMediaPlayerBackend::initTestCase()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("SKIP initTestCase on CI, because of QTBUG-118571");
+#endif
+
QMediaPlayer player;
if (!player.isAvailable())
QSKIP("Media player service is not available");
- localWavFile = MediaFileSelector::selectMediaFile(QStringList() << "qrc:/testdata/test.wav");
- localWavFile2 = MediaFileSelector::selectMediaFile(QStringList() << "qrc:/testdata/_test.wav");
+ qRegisterMetaType<MaybeUrl>();
+
+ m_localWavFile = m_mediaSelector.select("qrc:/testdata/test.wav");
+ m_localWavFile2 = m_mediaSelector.select("qrc:/testdata/_test.wav");
+
+ m_localVideoFile =
+ m_mediaSelector.select("qrc:/testdata/colors.mp4", "qrc:/testdata/colors.ogv");
+
+ m_localVideoFile3ColorsWithSound =
+ m_mediaSelector.select("qrc:/testdata/3colors_with_sound_1s.mp4");
+
+ m_videoFileWithJpegThumbnail =
+ m_mediaSelector.select("qrc:/testdata/audio_video_with_jpg_thumbnail.mp4");
+
+ m_videoFileWithPngThumbnail =
+ m_mediaSelector.select("qrc:/testdata/audio_video_with_png_thumbnail.mp4");
+
+#ifndef Q_OS_MACOS // QTBUG-119711 Add support for AV1 decoding with the FFmpeg backend in online installer
+ m_av1File = m_mediaSelector.select("qrc:/testdata/busAv1.webm");
+#endif
+
+ m_localVideoFile2 =
+ m_mediaSelector.select("qrc:/testdata/BigBuckBunny.mp4", "qrc:/testdata/busMpeg4.mp4");
+
+ m_videoDimensionTestFile = m_mediaSelector.select("qrc:/testdata/BigBuckBunny.mp4");
+
+ m_localCompressedSoundFile =
+ m_mediaSelector.select("qrc:/testdata/nokia-tune.mp3", "qrc:/testdata/nokia-tune.mkv");
- QStringList mediaCandidates;
- mediaCandidates << "qrc:/testdata/colors.mp4";
- mediaCandidates << "qrc:/testdata/colors.ogv";
- localVideoFile = MediaFileSelector::selectMediaFile(mediaCandidates);
+ m_localFileWithMetadata = m_mediaSelector.select("qrc:/testdata/nokia-tune.mp3");
- mediaCandidates.clear();
- mediaCandidates << "qrc:/testdata/BigBuckBunny.mp4";
- mediaCandidates << "qrc:/testdata/busMpeg4.mp4";
- localVideoFile2 = MediaFileSelector::selectMediaFile(mediaCandidates);
+ m_oneRedFrameVideo = m_mediaSelector.select("qrc:/testdata/one_red_frame.mp4");
- mediaCandidates.clear();
- mediaCandidates << "qrc:/testdata/BigBuckBunny.mp4";
- videoDimensionTestFile = MediaFileSelector::selectMediaFile(mediaCandidates);
+ m_192x108_PAR_2_3_Video = m_mediaSelector.select("qrc:/testdata/par_2_3.mp4");
+ m_192x108_PAR_3_2_Video = m_mediaSelector.select("qrc:/testdata/par_3_2.mp4");
- mediaCandidates.clear();
- mediaCandidates << "qrc:/testdata/nokia-tune.mp3";
- mediaCandidates << "qrc:/testdata/nokia-tune.mkv";
- localCompressedSoundFile = MediaFileSelector::selectMediaFile(mediaCandidates);
+ m_colorMatrixVideo = m_mediaSelector.select("qrc:/testdata/color_matrix.mp4");
+ m_colorMatrix90degClockwiseVideo =
+ m_mediaSelector.select("qrc:/testdata/color_matrix_90_deg_clockwise.mp4");
+ m_colorMatrix180degClockwiseVideo =
+ m_mediaSelector.select("qrc:/testdata/color_matrix_180_deg_clockwise.mp4");
+ m_colorMatrix270degClockwiseVideo =
+ m_mediaSelector.select("qrc:/testdata/color_matrix_270_deg_clockwise.mp4");
- localFileWithMetadata =
- MediaFileSelector::selectMediaFile(QStringList() << "qrc:/testdata/nokia-tune.mp3");
+ detectVlcCommand();
+}
+
+void tst_QMediaPlayerBackend::testMediaFilesAreSupported()
+{
+ const auto mediaSelectionErrors = m_mediaSelector.dumpErrors();
+ if (!mediaSelectionErrors.isEmpty())
+ qDebug().noquote() << "Dump media selection errors:\n" << mediaSelectionErrors;
+
+ // TODO: probalbly, we should check errors anyway; TBD.
+ QCOMPARE(m_mediaSelector.failedSelectionsCount(), 0);
+}
+
+void tst_QMediaPlayerBackend::destructor_cancelsPreviousSetSource_whenServerDoesNotRespond()
+{
+#ifdef QT_FEATURE_network
+ UnResponsiveRtspServer server;
+ QVERIFY(server.listen());
+
+ auto player = std::make_unique<QMediaPlayer>();
+ player->setSource(server.address());
+
+ QVERIFY(server.waitForConnection());
- qgetenv("QT_TEST_CI").toInt(&m_inCISystem,10);
+ // Cancel connection (should be fast, but can't be reliably verified
+ // in a test. For now we just verify that we don't crash.
+ player = nullptr;
+#else
+ QSKIP("Test requires network feature");
+#endif
}
-void tst_QMediaPlayerBackend::cleanup()
+void tst_QMediaPlayerBackend::destructor_emitsOnlyQObjectDestroyedSignal_whenPlayerIsRunning()
{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+ // Arrange
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+ m_fixture->player.play();
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+
+ m_fixture->clearSpies();
+
+ // Act
+ m_fixture->player.~QMediaPlayer();
+ new (&m_fixture->player) QMediaPlayer;
+
+ // Assert
+ QCOMPARE(m_fixture->playbackStateChanged.size(), 0);
+ QCOMPARE(m_fixture->errorOccurred.size(), 0);
+ QCOMPARE(m_fixture->sourceChanged.size(), 0);
+ QCOMPARE(m_fixture->mediaStatusChanged.size(), 0);
+ QCOMPARE(m_fixture->positionChanged.size(), 0);
+ QCOMPARE(m_fixture->durationChanged.size(), 0);
+ QCOMPARE(m_fixture->metadataChanged.size(), 0);
+ QCOMPARE(m_fixture->volumeChanged.size(), 0);
+ QCOMPARE(m_fixture->mutedChanged.size(), 0);
+ QCOMPARE(m_fixture->bufferProgressChanged.size(), 0);
+ QCOMPARE(m_fixture->destroyed.size(), 1);
}
-void tst_QMediaPlayerBackend::construction()
+void tst_QMediaPlayerBackend::
+ getters_returnExpectedValues_whenCalledWithDefaultConstructedPlayer_data() const
{
- QMediaPlayer player;
- QTRY_VERIFY(player.isAvailable());
+ QTest::addColumn<bool>("hasAudioOutput");
+ QTest::addColumn<bool>("hasVideoOutput");
+ QTest::addColumn<bool>("hasVideoSink");
+
+ QTest::newRow("noOutput") << false << false << false;
+ QTest::newRow("withAudioOutput") << true << false << false;
+ QTest::newRow("withVideoOutput") << false << true << false;
+ QTest::newRow("withVideoSink") << false << false << true;
+ QTest::newRow("withAllOutputs") << true << true << true;
}
-void tst_QMediaPlayerBackend::loadMedia()
+void tst_QMediaPlayerBackend::getters_returnExpectedValues_whenCalledWithDefaultConstructedPlayer()
+ const
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ QFETCH(const bool, hasAudioOutput);
+ QFETCH(const bool, hasVideoOutput);
+ QFETCH(const bool, hasVideoSink);
+
+ QAudioOutput audioOutput;
+ TestVideoOutput videoOutput;
QMediaPlayer player;
- QAudioOutput output;
- player.setAudioOutput(&output);
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
+ if (hasAudioOutput)
+ player.setAudioOutput(&audioOutput);
- QSignalSpy stateSpy(&player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)));
- QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
- QSignalSpy mediaSpy(&player, SIGNAL(sourceChanged(QUrl)));
+ if (hasVideoOutput)
+ player.setVideoOutput(&videoOutput);
- player.setSource(localWavFile);
+ if (hasVideoSink)
+ player.setVideoSink(videoOutput.videoSink());
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
+ MediaPlayerState expectedState = MediaPlayerState::defaultState();
+ expectedState.audioOutput = hasAudioOutput ? &audioOutput : nullptr;
+ expectedState.videoOutput = (hasVideoOutput && !hasVideoSink) ? &videoOutput : nullptr;
+ expectedState.videoSink = (hasVideoSink || hasVideoOutput) ? videoOutput.videoSink() : nullptr;
+
+ const MediaPlayerState actualState{ player };
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
+}
- QVERIFY(player.mediaStatus() != QMediaPlayer::NoMedia);
- QVERIFY(player.mediaStatus() != QMediaPlayer::InvalidMedia);
- QVERIFY(player.source() == localWavFile);
+void tst_QMediaPlayerBackend::setSource_emitsSourceChanged_whenCalledWithInvalidFile()
+{
+ m_fixture->player.setSource({ "Some not existing media" });
+ QTRY_COMPARE_EQ(m_fixture->player.error(), QMediaPlayer::ResourceError);
- QCOMPARE(stateSpy.count(), 0);
- QVERIFY(statusSpy.count() > 0);
- QCOMPARE(mediaSpy.count(), 1);
- QCOMPARE(mediaSpy.last()[0].value<QUrl>(), localWavFile);
+ QCOMPARE_EQ(m_fixture->sourceChanged, SignalList({ { QUrl("Some not existing media") } }));
+}
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+void tst_QMediaPlayerBackend::setSource_emitsError_whenCalledWithInvalidFile()
+{
+ m_fixture->player.setSource({ "Some not existing media" });
+ QTRY_COMPARE_EQ(m_fixture->player.error(), QMediaPlayer::ResourceError);
- QVERIFY(player.hasAudio());
- QVERIFY(!player.hasVideo());
+ QCOMPARE_EQ(m_fixture->errorOccurred[0][0], QMediaPlayer::ResourceError);
}
-void tst_QMediaPlayerBackend::unloadMedia()
+void tst_QMediaPlayerBackend::setSource_emitsMediaStatusChange_whenCalledWithInvalidFile()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ m_fixture->player.setSource({ "Some not existing media" });
+ QTRY_COMPARE_EQ(m_fixture->player.error(), QMediaPlayer::ResourceError);
- QMediaPlayer player;
- QAudioOutput output;
- player.setAudioOutput(&output);
+ QCOMPARE_EQ(m_fixture->mediaStatusChanged,
+ SignalList({ { QMediaPlayer::LoadingMedia }, { QMediaPlayer::InvalidMedia } }));
+}
- QSignalSpy stateSpy(&player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)));
- QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
- QSignalSpy mediaSpy(&player, SIGNAL(sourceChanged(QUrl)));
- QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
- QSignalSpy durationSpy(&player, SIGNAL(durationChanged(qint64)));
+void tst_QMediaPlayerBackend::setSource_doesNotEmitPlaybackStateChange_whenCalledWithInvalidFile()
+{
+ m_fixture->player.setSource({ "Some not existing media" });
+ QTRY_COMPARE_EQ(m_fixture->player.error(), QMediaPlayer::ResourceError);
- player.setSource(localWavFile);
+ QVERIFY(m_fixture->playbackStateChanged.empty());
+}
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+void tst_QMediaPlayerBackend::setSource_setsSourceMediaStatusAndError_whenCalledWithInvalidFile()
+{
+ const QUrl invalidFile{ "Some not existing media" };
+
+ m_fixture->player.setSource(invalidFile);
+ QTRY_COMPARE_EQ(m_fixture->player.error(), QMediaPlayer::ResourceError);
+
+ MediaPlayerState expectedState = MediaPlayerState::defaultState();
+ expectedState.source = invalidFile;
+ expectedState.mediaStatus = QMediaPlayer::InvalidMedia;
+ expectedState.error = QMediaPlayer::ResourceError;
+
+ const MediaPlayerState actualState{ m_fixture->player };
+
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
+}
- QVERIFY(player.position() == 0);
+void tst_QMediaPlayerBackend::setSource_silentlyCancelsPreviousCall_whenServerDoesNotRespond()
+{
+#ifdef QT_FEATURE_network
+ CHECK_SELECTED_URL(m_localVideoFile);
+
+ UnResponsiveRtspServer server;
+
+ QVERIFY(server.listen());
+
+ m_fixture->player.setSource(server.address());
+ QVERIFY(server.waitForConnection());
+
+ m_fixture->player.setSource(*m_localVideoFile);
+
+ // Cancellation can not be reliably verified due to relatively short timeout,
+ // but we can verify that the player is in the correct state.
+ QTRY_COMPARE_EQ(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ // Cancellation is silent
+ QVERIFY(m_fixture->errorOccurred.empty());
+
+ // Media status is emitted as if only one file was loaded
+ const SignalList expectedMediaStatus = { { QMediaPlayer::LoadingMedia },
+ { QMediaPlayer::LoadedMedia } };
+ QCOMPARE_EQ(m_fixture->mediaStatusChanged, expectedMediaStatus);
+
+ // Two media source changed signals should be emitted still
+ const SignalList expectedSource = { { server.address() }, { *m_localVideoFile } };
+ QCOMPARE_EQ(m_fixture->sourceChanged, expectedSource);
+
+#else
+ QSKIP("Test requires network feature");
+#endif
+}
+
+void tst_QMediaPlayerBackend::setSource_changesSourceAndMediaStatus_whenCalledWithValidFile()
+{
+ CHECK_SELECTED_URL(m_localVideoFile);
+
+ m_fixture->player.setSource(*m_localVideoFile);
+
+ QCOMPARE_EQ(m_fixture->mediaStatusChanged, SignalList({ { QMediaPlayer::LoadingMedia } }));
+
+ MediaPlayerState expectedState = MediaPlayerState::defaultState();
+ expectedState.source = *m_localVideoFile;
+ expectedState.mediaStatus = QMediaPlayer::LoadingMedia;
+
+ if (isGStreamerPlatform()) // gstreamer synchronously identifies file streams as seekable
+ expectedState.isSeekable = true;
+
+ MediaPlayerState actualState{ m_fixture->player };
+
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
+}
+
+void tst_QMediaPlayerBackend::setSource_updatesExpectedAttributes_whenMediaHasLoaded()
+{
+ CHECK_SELECTED_URL(m_localVideoFile);
+
+ m_fixture->player.setSource(*m_localVideoFile);
+
+ QTRY_COMPARE_EQ(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ MediaPlayerState expectedState = MediaPlayerState::defaultState();
+
+ // Modify all attributes that are supposed to change with this media file
+ // All other state variables are verified to be unchanged.
+ expectedState.source = *m_localVideoFile;
+ expectedState.mediaStatus = QMediaPlayer::LoadedMedia;
+ expectedState.audioTracks = std::nullopt; // Don't compare
+ expectedState.videoTracks = std::nullopt; // Don't compare
+ expectedState.activeAudioTrack = 0;
+ expectedState.activeVideoTrack = 0;
+
+ if (isGStreamerPlatform())
+ expectedState.duration = 15019;
+ else if (isDarwinPlatform())
+ expectedState.duration = 15000;
+ else
+ expectedState.duration = 15018;
+ expectedState.hasAudio = true;
+ expectedState.hasVideo = true;
+ expectedState.isSeekable = true;
+ expectedState.metaData = std::nullopt; // Don't compare
+
+ if (isGStreamerPlatform())
+ expectedState.bufferProgress = std::nullopt; // QTBUG-124633: can change before play()
+
+ MediaPlayerState actualState{ m_fixture->player };
+
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
+}
+
+void tst_QMediaPlayerBackend::setSource_stopsAndEntersErrorState_whenPlayerWasPlaying()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+ // Arrange
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+ m_fixture->player.play();
+ QTRY_VERIFY(m_fixture->framesCount > 0);
+ QCOMPARE(m_fixture->errorOccurred.size(), 0);
+
+ // Act
+ m_fixture->player.setSource(QUrl("Some not existing media"));
+
+ // Assert
+ const int savedFramesCount = m_fixture->framesCount;
+
+ QCOMPARE(m_fixture->player.source(), QUrl("Some not existing media"));
+
+ QTRY_COMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::InvalidMedia);
+ QTRY_COMPARE(m_fixture->player.error(), QMediaPlayer::ResourceError);
+
+ QVERIFY(!m_fixture->surface.videoFrame().isValid());
+
+ QCOMPARE(m_fixture->errorOccurred.size(), 1);
+
+ QTest::qWait(20);
+ QCOMPARE(m_fixture->framesCount, savedFramesCount);
+}
+
+void tst_QMediaPlayerBackend::setSource_loadsAudioTrack_whenCalledWithValidWavFile()
+{
+ CHECK_SELECTED_URL(m_localWavFile);
+
+ m_fixture->player.setSource(*m_localWavFile);
+
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+
+ QVERIFY(m_fixture->player.mediaStatus() != QMediaPlayer::NoMedia);
+ QVERIFY(m_fixture->player.mediaStatus() != QMediaPlayer::InvalidMedia);
+ QVERIFY(m_fixture->player.source() == *m_localWavFile);
+
+ QCOMPARE(m_fixture->playbackStateChanged.size(), 0);
+ QVERIFY(m_fixture->mediaStatusChanged.size() > 0);
+ QCOMPARE(m_fixture->sourceChanged.size(), 1);
+ QCOMPARE(m_fixture->sourceChanged.last()[0].value<QUrl>(), *m_localWavFile);
+
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ QVERIFY(m_fixture->player.hasAudio());
+ QVERIFY(!m_fixture->player.hasVideo());
+}
+
+void tst_QMediaPlayerBackend::setSource_resetsState_whenCalledWithEmptyUrl()
+{
+ CHECK_SELECTED_URL(m_localWavFile);
+
+ // Load valid media and start playing
+ m_fixture->player.setSource(*m_localWavFile);
+
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ QVERIFY(m_fixture->player.position() == 0);
#ifdef Q_OS_QNX
// QNX mm-renderer only updates the duration when 'play' is triggered
- QVERIFY(player.duration() == 0);
+ QVERIFY(m_fixture->player.duration() == 0);
#else
- QVERIFY(player.duration() > 0);
+ QVERIFY(m_fixture->player.duration() > 0);
#endif
- player.play();
+ m_fixture->player.play();
- QTRY_VERIFY(player.position() > 0);
- QVERIFY(player.duration() > 0);
+ QTRY_VERIFY(m_fixture->player.position() > 0);
+ QVERIFY(m_fixture->player.duration() > 0);
- stateSpy.clear();
- statusSpy.clear();
- mediaSpy.clear();
- positionSpy.clear();
- durationSpy.clear();
+ // Set empty URL and verify that state is fully reset to default
+ m_fixture->clearSpies();
- player.setSource(QUrl());
+ m_fixture->player.setSource(QUrl());
- QVERIFY(player.position() <= 0);
- QVERIFY(player.duration() <= 0);
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
- QCOMPARE(player.source(), QUrl());
+ QVERIFY(!m_fixture->mediaStatusChanged.isEmpty());
+ QVERIFY(!m_fixture->sourceChanged.isEmpty());
+
+ const MediaPlayerState expectedState = MediaPlayerState::defaultState();
+ const MediaPlayerState actualState{ m_fixture->player };
- QVERIFY(!statusSpy.isEmpty());
- QVERIFY(!mediaSpy.isEmpty());
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
}
-void tst_QMediaPlayerBackend::loadMediaInLoadingState()
+void tst_QMediaPlayerBackend::setSource_loadsNewMedia_whenPreviousMediaWasFullyLoaded()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ CHECK_SELECTED_URL(m_localWavFile);
+ CHECK_SELECTED_URL(m_localWavFile2);
+
+ // Load media and wait for it to completely load
+ m_fixture->player.setSource(*m_localWavFile2);
+ QCOMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadingMedia);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ // Load another media file, play it, and wait for it to enter playing state
+ m_fixture->player.setSource(*m_localWavFile);
+ QCOMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadingMedia);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+ m_fixture->player.play();
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+
+ // Load first file again, and wait for it to start loading
+ m_fixture->player.setSource(*m_localWavFile2);
+ QCOMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadingMedia);
+}
- QMediaPlayer player;
- QAudioOutput output;
- player.setAudioOutput(&output);
- player.setSource(localWavFile2);
- QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadingMedia);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
- // Sets new media while old has not been finished.
- player.setSource(localWavFile);
- QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadingMedia);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
- player.play();
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+void tst_QMediaPlayerBackend::setSource_loadsCorrectTracks_whenLoadingMediaInSequence()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+ CHECK_SELECTED_URL(m_localWavFile2);
+
+ // Load audio/video file, play it, and verify that both tracks are loaded
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+ m_fixture->player.play();
+ QTRY_COMPARE_EQ(m_fixture->player.playbackState(), QMediaPlayer::PlayingState);
+ QVERIFY(m_fixture->surface.waitForFrame().isValid());
+ QVERIFY(m_fixture->player.hasAudio());
+ QVERIFY(m_fixture->player.hasVideo());
+
+ m_fixture->clearSpies();
+
+ // Load an audio file, and verify that only audio track is loaded
+ m_fixture->player.setSource(*m_localWavFile2);
+
+ QTRY_COMPARE_EQ(m_fixture->player.mediaStatus(), QMediaPlayer::MediaStatus::LoadedMedia);
- player.setSource(localWavFile2);
- QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadingMedia);
+ QCOMPARE(m_fixture->player.source(), *m_localWavFile2);
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QCOMPARE(m_fixture->playbackStateChanged.size(), 1);
+ QCOMPARE(m_fixture->errorOccurred.size(), 0);
+ QVERIFY(m_fixture->player.hasAudio());
+ QVERIFY(!m_fixture->player.hasVideo());
+ QVERIFY(!m_fixture->surface.videoFrame().isValid());
+
+ m_fixture->player.play();
+
+ // Load video only file, and verify that only video track is loaded
+ m_fixture->player.setSource(*m_localVideoFile2);
+
+ QTRY_COMPARE_EQ(m_fixture->player.mediaStatus(), QMediaPlayer::MediaStatus::LoadedMedia);
+
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QVERIFY(m_fixture->player.hasVideo());
+ QVERIFY(!m_fixture->player.hasAudio());
+ QCOMPARE(m_fixture->errorOccurred.size(), 0);
}
-void tst_QMediaPlayerBackend::playPauseStop()
+void tst_QMediaPlayerBackend::setSource_remainsInStoppedState_whenPlayerWasStopped()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ CHECK_SELECTED_URL(m_localWavFile);
+ CHECK_SELECTED_URL(m_localWavFile2);
+
+ // Arrange
+ m_fixture->player.setSource(*m_localWavFile);
+ m_fixture->player.play();
+ QTRY_VERIFY(m_fixture->player.position() > 100);
+ m_fixture->player.stop();
+ m_fixture->clearSpies();
+
+ // Act
+ m_fixture->player.setSource(*m_localWavFile2);
+
+ // Assert
+ QTRY_VERIFY(m_fixture->mediaStatusChanged.size() > 0);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+ QCOMPARE_EQ(m_fixture->mediaStatusChanged,
+ SignalList({ { QMediaPlayer::LoadingMedia }, { QMediaPlayer::LoadedMedia } }));
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QVERIFY(m_fixture->playbackStateChanged.empty());
+}
- QMediaPlayer player;
- QAudioOutput output;
- player.setAudioOutput(&output);
+void tst_QMediaPlayerBackend::setSource_entersStoppedState_whenPlayerWasPlaying()
+{
+ CHECK_SELECTED_URL(m_localWavFile);
+ CHECK_SELECTED_URL(m_localWavFile2);
+
+ // Arrange
+ m_fixture->player.setSource(*m_localWavFile2);
+ m_fixture->clearSpies();
+ m_fixture->player.play();
+ QTRY_VERIFY(m_fixture->player.position() > 100);
+
+ // Act
+ m_fixture->player.setSource(*m_localWavFile);
+
+ // Assert
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+ QTRY_COMPARE(m_fixture->mediaStatusChanged,
+ SignalList({ { QMediaPlayer::LoadedMedia },
+ { QMediaPlayer::BufferingMedia },
+ { QMediaPlayer::BufferedMedia },
+ { QMediaPlayer::LoadedMedia },
+ { QMediaPlayer::LoadingMedia },
+ { QMediaPlayer::LoadedMedia } }));
+
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QTRY_COMPARE(m_fixture->playbackStateChanged,
+ SignalList({ { QMediaPlayer::PlayingState }, { QMediaPlayer::StoppedState } }));
+
+ QTRY_VERIFY(!m_fixture->positionChanged.empty()
+ && m_fixture->positionChanged.last()[0].value<qint64>() == 0);
+
+ QCOMPARE(m_fixture->player.position(), 0);
+}
- QSignalSpy stateSpy(&player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)));
- QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
- QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
- QSignalSpy errorSpy(&player, SIGNAL(errorOccurred(QMediaPlayer::Error, const QString&)));
+void tst_QMediaPlayerBackend::
+ setSourceAndPlay_setCorrectVideoSize_whenVideoHasNonStandardPixelAspectRatio_data()
+{
+ QTest::addColumn<MaybeUrl>("url");
+ QTest::addColumn<QSize>("expectedVideoSize");
- // Check play() without a media
- player.play();
+ QTest::addRow("Horizontal expanding (par=3/2)")
+ << m_192x108_PAR_3_2_Video << QSize(192 * 3 / 2, 108);
+ QTest::addRow("Vertical expanding (par=2/3)")
+ << m_192x108_PAR_2_3_Video << QSize(192, 108 * 3 / 2);
+}
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
- QCOMPARE(player.error(), QMediaPlayer::NoError);
- QCOMPARE(player.position(), 0);
- QCOMPARE(stateSpy.count(), 0);
- QCOMPARE(statusSpy.count(), 0);
- QCOMPARE(positionSpy.count(), 0);
- QCOMPARE(errorSpy.count(), 0);
+void tst_QMediaPlayerBackend::
+ setSourceAndPlay_setCorrectVideoSize_whenVideoHasNonStandardPixelAspectRatio()
+{
+ QFETCH(MaybeUrl, url);
+ QFETCH(QSize, expectedVideoSize);
- // Check pause() without a media
- player.pause();
+ CHECK_SELECTED_URL(url);
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
- QCOMPARE(player.error(), QMediaPlayer::NoError);
- QCOMPARE(player.position(), 0);
- QCOMPARE(stateSpy.count(), 0);
- QCOMPARE(statusSpy.count(), 0);
- QCOMPARE(positionSpy.count(), 0);
- QCOMPARE(errorSpy.count(), 0);
+ m_fixture->player.setSource(*url);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+ QCOMPARE(m_fixture->player.metaData().value(QMediaMetaData::Resolution), QSize(192, 108));
- // The rest is with a valid media
+ QCOMPARE(m_fixture->surface.videoSize(), expectedVideoSize);
- player.setSource(localWavFile);
+ m_fixture->player.play();
- QCOMPARE(player.position(), qint64(0));
+ auto frame = m_fixture->surface.waitForFrame();
+ QVERIFY(frame.isValid());
+ QCOMPARE(frame.size(), expectedVideoSize);
+ QCOMPARE(frame.surfaceFormat().frameSize(), expectedVideoSize);
+ QCOMPARE(frame.surfaceFormat().viewport(), QRect(QPoint(), expectedVideoSize));
- player.play();
+#ifdef Q_OS_ANDROID
+ QSKIP("frame.toImage will return null image because of QTBUG-108446");
+#endif
- QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
+ auto image = frame.toImage();
+ QCOMPARE(frame.size(), expectedVideoSize);
+
+ // clang-format off
+
+ // Video schema:
+ //
+ // 192
+ // /---------------------\
+ // | White | |
+ // | | |
+ // |----------/ | 108
+ // | Red |
+ // | |
+ // \---------------------/
+
+ // clang-format on
+
+ // check the proper scaling
+ const std::vector<QRgb> colors = { 0xFFFFFF, 0xFF0000, 0xFF00, 0xFF, 0x0 };
+
+ const auto pixelsOffset = 4;
+ const auto halfSize = expectedVideoSize / 2;
+
+ QCOMPARE(findSimilarColorIndex(colors, image.pixel(0, 0)), 0);
+ QCOMPARE(findSimilarColorIndex(colors, image.pixel(halfSize.width() - pixelsOffset, 0)), 0);
+ QCOMPARE(findSimilarColorIndex(colors, image.pixel(0, halfSize.height() - pixelsOffset)), 0);
+ QCOMPARE(findSimilarColorIndex(colors,
+ image.pixel(halfSize.width() - pixelsOffset,
+ halfSize.height() - pixelsOffset)),
+ 0);
+
+ QCOMPARE(findSimilarColorIndex(colors, image.pixel(halfSize.width() + pixelsOffset, 0)), 1);
+ QCOMPARE(findSimilarColorIndex(colors, image.pixel(0, halfSize.height() + pixelsOffset)), 1);
+ QCOMPARE(findSimilarColorIndex(colors,
+ image.pixel(halfSize.width() + pixelsOffset,
+ halfSize.height() + pixelsOffset)),
+ 1);
+}
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+void tst_QMediaPlayerBackend::pause_doesNotChangePlayerState_whenInvalidFileLoaded()
+{
+ m_fixture->player.setSource({ "Some not existing media" });
+ QTRY_COMPARE_EQ(m_fixture->player.error(), QMediaPlayer::ResourceError);
- QCOMPARE(stateSpy.count(), 1);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::PlayingState);
- QTRY_VERIFY(statusSpy.count() > 0 &&
- statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::BufferedMedia);
+ const MediaPlayerState expectedState{ m_fixture->player };
- QTRY_VERIFY(player.position() > 100);
- QVERIFY(player.duration() > 0);
- QTRY_VERIFY(positionSpy.count() > 0);
- QTRY_VERIFY(positionSpy.last()[0].value<qint64>() > 0);
+ m_fixture->player.pause();
- stateSpy.clear();
- statusSpy.clear();
- positionSpy.clear();
+ const MediaPlayerState actualState{ m_fixture->player };
- qint64 positionBeforePause = player.position();
- player.pause();
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
+}
- QCOMPARE(player.playbackState(), QMediaPlayer::PausedState);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+void tst_QMediaPlayerBackend::pause_doesNothing_whenMediaIsNotLoaded()
+{
+ m_fixture->player.pause();
+
+ const MediaPlayerState expectedState = MediaPlayerState::defaultState();
+ const MediaPlayerState actualState{ m_fixture->player };
+
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
+
+ QVERIFY(m_fixture->playbackStateChanged.empty());
+ QVERIFY(m_fixture->mediaStatusChanged.empty());
+ QVERIFY(m_fixture->positionChanged.empty());
+ QVERIFY(m_fixture->errorOccurred.empty());
+}
+
+void tst_QMediaPlayerBackend::pause_entersPauseState_whenPlayerWasPlaying()
+{
+ CHECK_SELECTED_URL(m_localWavFile);
- QCOMPARE(stateSpy.count(), 1);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::PausedState);
+ // Arrange
+ m_fixture->player.setSource(*m_localWavFile);
+ m_fixture->player.play();
+ QTRY_VERIFY(m_fixture->player.position() > 100);
+ m_fixture->clearSpies();
+ const qint64 positionBeforePause = m_fixture->player.position();
+
+ // Act
+ m_fixture->player.pause();
+
+ // Assert
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::PausedState);
+ QCOMPARE_EQ(m_fixture->playbackStateChanged, SignalList({ { QMediaPlayer::PausedState } }));
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
QTest::qWait(500);
- QTRY_VERIFY(qAbs(player.position() - positionBeforePause) < 150);
+ QTRY_VERIFY(qAbs(m_fixture->player.position() - positionBeforePause) < 150);
+}
- stateSpy.clear();
- statusSpy.clear();
+void tst_QMediaPlayerBackend::play_resetsErrorState_whenCalledWithInvalidFile()
+{
+ m_fixture->player.setSource({ "Some not existing media" });
+ QTRY_COMPARE_EQ(m_fixture->player.error(), QMediaPlayer::ResourceError);
- player.stop();
+ MediaPlayerState expectedState{ m_fixture->player };
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+ m_fixture->player.play();
- QCOMPARE(stateSpy.count(), 1);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::StoppedState);
- //it's allowed to emit statusChanged() signal async
- QTRY_COMPARE(statusSpy.count(), 1);
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia);
+ expectedState.error = QMediaPlayer::NoError;
+ COMPARE_MEDIA_PLAYER_STATE_EQ(MediaPlayerState{ m_fixture->player }, expectedState);
- //ensure the position is reset to 0 at stop and positionChanged(0) is emitted
- QTRY_COMPARE(player.position(), qint64(0));
- QCOMPARE(positionSpy.last()[0].value<qint64>(), qint64(0));
- QVERIFY(player.duration() > 0);
+ QTest::qWait(150); // wait a bit and check position is not changed
- stateSpy.clear();
- statusSpy.clear();
- positionSpy.clear();
+ COMPARE_MEDIA_PLAYER_STATE_EQ(MediaPlayerState{ m_fixture->player }, expectedState);
+ QCOMPARE(m_fixture->surface.m_totalFrames, 0);
+}
- player.play();
+void tst_QMediaPlayerBackend::play_resumesPlaying_whenValidMediaIsProvidedAfterInvalidMedia()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+ // Arrange
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+ m_fixture->player.play();
+ QTRY_VERIFY(m_fixture->framesCount > 0);
+ m_fixture->player.setSource(QUrl("Some not existing media"));
+ QTRY_COMPARE(m_fixture->player.error(), QMediaPlayer::ResourceError);
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+
+ // Act
+ m_fixture->player.play();
+
+ // Assert
+ QTRY_VERIFY(m_fixture->framesCount > 0);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+ QCOMPARE_EQ(m_fixture->player.playbackState(), QMediaPlayer::PlayingState);
+ QCOMPARE(m_fixture->player.error(), QMediaPlayer::NoError);
+}
- QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
- QCOMPARE(stateSpy.count(), 1);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::PlayingState);
- QCOMPARE(statusSpy.count(), 1); // Should not go through Loading again when play -> stop -> play
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia);
+void tst_QMediaPlayerBackend::play_doesNothing_whenMediaIsNotLoaded()
+{
+ m_fixture->player.play();
- player.stop();
- stateSpy.clear();
- statusSpy.clear();
- positionSpy.clear();
+ const MediaPlayerState expectedState = MediaPlayerState::defaultState();
+ const MediaPlayerState actualState{ m_fixture->player };
- player.setSource(localWavFile2);
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
- QTRY_VERIFY(statusSpy.count() > 0);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia);
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(stateSpy.count(), 0);
+ QVERIFY(m_fixture->playbackStateChanged.empty());
+ QVERIFY(m_fixture->mediaStatusChanged.empty());
+ QVERIFY(m_fixture->positionChanged.empty());
+ QVERIFY(m_fixture->errorOccurred.empty());
+}
- player.play();
+void tst_QMediaPlayerBackend::play_setsPlaybackStateAndMediaStatus_whenValidFileIsLoaded()
+{
+ CHECK_SELECTED_URL(m_localVideoFile);
- QTRY_VERIFY(player.position() > 100);
+ m_fixture->player.setSource(*m_localVideoFile);
+ m_fixture->player.play();
- player.setSource(localWavFile);
+ QTRY_COMPARE_EQ(m_fixture->player.playbackState(), QMediaPlayer::PlayingState);
+ QTRY_COMPARE_EQ(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia);
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::StoppedState);
- QCOMPARE(player.position(), 0);
- QCOMPARE(positionSpy.last()[0].value<qint64>(), 0);
+ QCOMPARE(m_fixture->playbackStateChanged, SignalList({ { QMediaPlayer::PlayingState } }));
+ QTRY_COMPARE_EQ(m_fixture->mediaStatusChanged,
+ SignalList({ { QMediaPlayer::LoadingMedia },
+ { QMediaPlayer::LoadedMedia },
+ { QMediaPlayer::BufferingMedia },
+ { QMediaPlayer::BufferedMedia } }));
- stateSpy.clear();
- statusSpy.clear();
- positionSpy.clear();
+ QTRY_COMPARE_GT(m_fixture->bufferProgressChanged.size(), 0);
+ QTRY_COMPARE_NE(m_fixture->bufferProgressChanged.front().front(), 0.f);
+ QTRY_COMPARE(m_fixture->bufferProgressChanged.back().front(), 1.f);
+}
- player.play();
+void tst_QMediaPlayerBackend::play_startsPlaybackAndChangesPosition_whenValidFileIsLoaded()
+{
+ CHECK_SELECTED_URL(m_localVideoFile);
- QTRY_VERIFY(player.position() > 100);
+ m_fixture->player.setSource(*m_localVideoFile);
+ m_fixture->player.play();
- player.setSource(QUrl());
+ QTRY_VERIFY(m_fixture->player.position() > 100);
+ QTRY_VERIFY(!m_fixture->durationChanged.empty());
+ QTRY_VERIFY(!m_fixture->positionChanged.empty());
+ QTRY_VERIFY(m_fixture->positionChanged.last()[0].value<qint64>() > 100);
+}
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::NoMedia);
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::StoppedState);
- QCOMPARE(player.position(), 0);
- QCOMPARE(positionSpy.last()[0].value<qint64>(), 0);
- QCOMPARE(player.duration(), 0);
+void tst_QMediaPlayerBackend::play_doesNotEnterMediaLoadingState_whenResumingPlayingAfterStop()
+{
+ CHECK_SELECTED_URL(m_localWavFile);
+
+ // Arrange: go through a play->pause->stop sequence
+ m_fixture->player.setSource(*m_localWavFile);
+ m_fixture->player.play();
+ QTRY_VERIFY(m_fixture->player.position() > 100);
+ m_fixture->player.pause();
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+ m_fixture->player.stop();
+ m_fixture->clearSpies();
+
+ // Act
+ m_fixture->player.play();
+
+ // Assert
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::PlayingState);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+ QCOMPARE_EQ(m_fixture->playbackStateChanged, SignalList({ { QMediaPlayer::PlayingState } }));
+
+ // Note: Should not go through Loading again when play -> stop -> play
+ QCOMPARE_EQ(m_fixture->mediaStatusChanged,
+ SignalList({
+ { QMediaPlayer::BufferingMedia },
+ { QMediaPlayer::BufferedMedia },
+ }));
}
+void tst_QMediaPlayerBackend::playAndSetSource_emitsExpectedSignalsAndStopsPlayback_whenSetSourceWasCalledWithEmptyUrl()
+{
+ CHECK_SELECTED_URL(m_localWavFile2);
+
+ // Arrange
+ m_fixture->player.setSource(*m_localWavFile2);
+ m_fixture->clearSpies();
+
+ // Act
+ m_fixture->player.play();
+ QTRY_VERIFY(m_fixture->player.position() > 100);
+ m_fixture->player.setSource(QUrl());
+
+ // Assert
+ const MediaPlayerState expectedState = MediaPlayerState::defaultState();
+ const MediaPlayerState actualState{ m_fixture->player };
+ COMPARE_MEDIA_PLAYER_STATE_EQ(actualState, expectedState);
+
+ QTRY_COMPARE_EQ(m_fixture->mediaStatusChanged,
+ SignalList({ { QMediaPlayer::LoadedMedia },
+ { QMediaPlayer::BufferingMedia },
+ { QMediaPlayer::BufferedMedia },
+ { QMediaPlayer::LoadedMedia },
+ { QMediaPlayer::NoMedia } }));
+
+ QTRY_COMPARE_EQ(m_fixture->playbackStateChanged,
+ SignalList({ { QMediaPlayer::PlayingState }, { QMediaPlayer::StoppedState } }));
+
+ QTRY_VERIFY(m_fixture->positionChanged.size() > 0);
+ QCOMPARE(m_fixture->positionChanged.last()[0].value<qint64>(), 0);
+}
-void tst_QMediaPlayerBackend::processEOS()
+void tst_QMediaPlayerBackend::
+ play_createsFramesWithExpectedContentAndIncreasingFrameTime_whenPlayingRtspMediaStream()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+#if !QT_CONFIG(process)
+ QSKIP("This test requires QProcess support");
+#else
+ if (!canCreateRtspStream())
+ QSKIP("Rtsp stream cannot be created");
+
+ auto temporaryFile = copyResourceToTemporaryFile(":/testdata/colors.mp4", "colors.XXXXXX.mp4");
+ QVERIFY(temporaryFile);
+
+ const QString streamUrl = "rtsp://localhost:8083/stream";
+ auto process = createRtspStreamProcess(temporaryFile->fileName(), streamUrl);
+ QVERIFY2(process, "Cannot start rtsp process");
+
+ auto processCloser = qScopeGuard([&process]() { process->close(); });
+
+ TestVideoSink surface(false);
QMediaPlayer player;
- QAudioOutput output;
- player.setAudioOutput(&output);
- QSignalSpy stateSpy(&player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)));
- QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
- QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
+ QSignalSpy errorSpy(&player, &QMediaPlayer::errorOccurred);
+
+ player.setVideoSink(&surface);
+ // Ignore audio output to check timings accuratelly
+ // player.setAudioOutput(&output);
- player.setSource(localWavFile);
+ player.setSource(streamUrl);
player.play();
- player.setPosition(900);
- //wait up to 5 seconds for EOS
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ QTRY_COMPARE(player.playbackState(), QMediaPlayer::PlayingState);
+
+ const auto colors = { qRgb(0, 0, 0xFF), qRgb(0xFF, 0, 0), qRgb(0, 0xFE, 0) };
+ const auto colorInterval = 5000;
+
+ for (auto pos : { colorInterval / 2, colorInterval + 100 }) {
+ qDebug() << "Waiting for position:" << pos;
+
+ QTRY_COMPARE_GT(player.position(), pos);
+
+ auto frame1 = surface.waitForFrame();
+ QVERIFY(frame1.isValid());
+ QCOMPARE(frame1.size(), QSize(213, 120));
+
+ QCOMPARE_GT(frame1.startTime(), pos * 1000);
+
+ auto frameTime = frame1.startTime();
+ const auto coloIndex = frameTime / (colorInterval * 1000);
+ QCOMPARE_LT(coloIndex, 2);
+
+ const auto image1 = frame1.toImage();
+ QVERIFY(!image1.isNull());
+ QCOMPARE(findSimilarColorIndex(colors, image1.pixel(1, 1)), coloIndex);
+ QCOMPARE(findSimilarColorIndex(colors, image1.pixel(100, 100)), coloIndex);
+
+ auto frame2 = surface.waitForFrame();
+ QVERIFY(frame2.isValid());
+ QCOMPARE_GT(frame2.startTime(), frame1.startTime());
+ }
+
+ player.stop();
- QVERIFY(statusSpy.count() > 0);
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia);
QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(stateSpy.count(), 2);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::StoppedState);
+ QCOMPARE(errorSpy.size(), 0);
+#endif //QT_CONFIG(process)
+}
- //at EOS the position stays at the end of file
- QCOMPARE(player.position(), player.duration());
- QTRY_VERIFY(positionSpy.count() > 0);
- QTRY_COMPARE(positionSpy.last()[0].value<qint64>(), player.duration());
+void tst_QMediaPlayerBackend::play_waitsForLastFrameEnd_whenPlayingVideoWithLongFrames()
+{
+ CHECK_SELECTED_URL(m_oneRedFrameVideo);
- stateSpy.clear();
- statusSpy.clear();
- positionSpy.clear();
+ m_fixture->surface.setStoreFrames(true);
- player.play();
+ m_fixture->player.setSource(*m_oneRedFrameVideo);
+ m_fixture->player.play();
- //position is reset to start
- QTRY_VERIFY(player.position() < 100);
- QTRY_VERIFY(positionSpy.count() > 0);
- QCOMPARE(positionSpy.first()[0].value<qint64>(), 0);
+ QTRY_COMPARE_GT(m_fixture->surface.m_totalFrames, 0);
+ QVERIFY(m_fixture->surface.m_frameList.front().isValid());
- QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+ QElapsedTimer timer;
+ timer.start();
- QCOMPARE(stateSpy.count(), 1);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::PlayingState);
- QVERIFY(statusSpy.count() > 0);
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia);
+ QTRY_COMPARE_GT(m_fixture->surface.m_totalFrames, 1);
+ const auto elapsed = timer.elapsed();
+
+ if (!isGStreamerPlatform()) {
+ // QTBUG-124005: GStreamer timing seems to be off
+
+ // 1000 is expected
+ QCOMPARE_GT(elapsed, 900);
+ QCOMPARE_LT(elapsed, 1400);
+ }
+
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ QCOMPARE(m_fixture->surface.m_totalFrames, 2);
+ QVERIFY(!m_fixture->surface.m_frameList.back().isValid());
+}
+
+void tst_QMediaPlayerBackend::play_startsPlayback_withAndWithoutOutputsConnected()
+{
+ QFETCH(const bool, audioConnected);
+ QFETCH(const bool, videoConnected);
+
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+ if (!videoConnected && !audioConnected)
+ QSKIP_FFMPEG("FFMPEG backend playback fails when no output is connected");
+
+ // Arrange
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+ if (!audioConnected)
+ m_fixture->player.setAudioOutput(nullptr);
+
+ if (!videoConnected)
+ m_fixture->player.setVideoOutput(nullptr);
+
+ m_fixture->clearSpies();
+
+ // Act
+ m_fixture->player.play();
+
+ // Assert
+ QTRY_VERIFY(!m_fixture->mediaStatusChanged.empty()
+ && m_fixture->mediaStatusChanged.back()
+ == QList<QVariant>{ QMediaPlayer::EndOfMedia });
+
+ QTRY_COMPARE_EQ(m_fixture->playbackStateChanged,
+ SignalList({
+ { QMediaPlayer::PlayingState },
+ { QMediaPlayer::StoppedState },
+ }));
+}
+
+void tst_QMediaPlayerBackend::play_startsPlayback_withAndWithoutOutputsConnected_data()
+{
+ QTest::addColumn<bool>("videoConnected");
+ QTest::addColumn<bool>("audioConnected");
+
+ QTest::addRow("all connected") << true << true;
+ QTest::addRow("video connected") << true << false;
+ QTest::addRow("audio connected") << false << true;
+ QTest::addRow("no output connected") << false << false;
+}
+
+void tst_QMediaPlayerBackend::stop_entersStoppedState_whenPlayerWasPaused()
+{
+ CHECK_SELECTED_URL(m_localWavFile);
+
+ // Arrange
+ m_fixture->player.setSource(*m_localWavFile);
+ m_fixture->player.play();
+ QTRY_VERIFY(m_fixture->player.position() > 100);
+ m_fixture->player.pause();
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+ m_fixture->clearSpies();
+
+ // Act
+ m_fixture->player.stop();
+
+ // Assert
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ QCOMPARE(m_fixture->playbackStateChanged, SignalList({ { QMediaPlayer::StoppedState } }));
+ // it's allowed to emit statusChanged() signal async
+ QTRY_COMPARE(m_fixture->mediaStatusChanged, SignalList({ { QMediaPlayer::LoadedMedia } }));
+
+ if (!isGStreamerPlatform())
+ // QTBUG-124517: for some media types gstreamer does not emit buffer progress messages
+ QCOMPARE(m_fixture->bufferProgressChanged, SignalList({ { 0.f } }));
+
+ QTRY_COMPARE(m_fixture->player.position(), qint64(0));
+ QTRY_VERIFY(!m_fixture->positionChanged.empty());
+ QCOMPARE(m_fixture->positionChanged.last()[0].value<qint64>(), qint64(0));
+ QVERIFY(m_fixture->player.duration() > 0);
+}
+
+void tst_QMediaPlayerBackend::stop_setsPositionToZero_afterPlayingToEndOfMedia()
+{
+ // Arrange
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+ m_fixture->player.play();
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+
+ // Act
+ m_fixture->player.stop();
+
+ // Assert
+ QCOMPARE(m_fixture->player.position(), qint64(0));
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+
+ m_fixture->player.play();
+ QVERIFY(m_fixture->surface.waitForFrame().isValid());
+}
+
+
+void tst_QMediaPlayerBackend::playbackRate_returnsOne_byDefault()
+{
+ QCOMPARE_EQ(m_fixture->player.playbackRate(), static_cast<qreal>(1.0f));
+}
+
+void tst_QMediaPlayerBackend::setPlaybackRate_changesPlaybackRateAndEmitsSignal_data()
+{
+ QTest::addColumn<float>("initialPlaybackRate");
+ QTest::addColumn<float>("targetPlaybackRate");
+ QTest::addColumn<float>("expectedPlaybackRate");
+ QTest::addColumn<bool>("signalExpected");
+
+ QTest::addRow("Increase") << 1.0f << 2.0f << 2.0f << true;
+ QTest::addRow("Decrease") << 1.0f << 0.5f << 0.5f << true;
+ QTest::addRow("Keep") << 0.5f << 0.5f << 0.5f << false;
+
+ bool backendSupportsNegativePlayback =
+ isWindowsPlatform() || isDarwinPlatform() || isGStreamerPlatform();
+
+ if (backendSupportsNegativePlayback) {
+ QTest::addRow("DecreaseBelowZero") << 0.5f << -0.5f << -0.5f << true;
+ QTest::addRow("KeepDecreasingBelowZero") << -0.5f << -0.6f << -0.6f << true;
+ } else {
+ QTest::addRow("DecreaseBelowZero") << 0.5f << -0.5f << 0.0f << true;
+ QTest::addRow("KeepDecreasingBelowZero") << -0.5f << -0.6f << 0.0f << false;
+ }
+}
+
+void tst_QMediaPlayerBackend::setPlaybackRate_changesPlaybackRateAndEmitsSignal()
+{
+ QFETCH(const float, initialPlaybackRate);
+ QFETCH(const float, targetPlaybackRate);
+ QFETCH(const float, expectedPlaybackRate);
+ QFETCH(const bool, signalExpected);
+
+ // Arrange
+ m_fixture->player.setPlaybackRate(initialPlaybackRate);
+ m_fixture->clearSpies();
+
+ // Act
+ m_fixture->player.setPlaybackRate(targetPlaybackRate);
+
+ // Assert
+ if (signalExpected)
+ QCOMPARE_EQ(m_fixture->playbackRateChanged, SignalList({ { expectedPlaybackRate } }));
+ else
+ QVERIFY(m_fixture->playbackRateChanged.empty());
+
+ QCOMPARE_EQ(m_fixture->player.playbackRate(), expectedPlaybackRate);
+}
+
+void tst_QMediaPlayerBackend::setVolume_changesVolume_whenVolumeIsInRange()
+{
+ m_fixture->output.setVolume(0.0f);
+ QCOMPARE_EQ(m_fixture->output.volume(), 0.0f);
+ QCOMPARE(m_fixture->volumeChanged, SignalList({ { 0.0f } }));
+
+ m_fixture->output.setVolume(0.5f);
+ QCOMPARE_EQ(m_fixture->output.volume(), 0.5f);
+ QCOMPARE(m_fixture->volumeChanged, SignalList({ { 0.0f }, { 0.5f } }));
+
+ m_fixture->output.setVolume(1.0f);
+ QCOMPARE_EQ(m_fixture->output.volume(), 1.0f);
+ QCOMPARE(m_fixture->volumeChanged, SignalList({ { 0.0f }, { 0.5f }, { 1.0f } }));
+}
+
+void tst_QMediaPlayerBackend::setVolume_clampsToRange_whenVolumeIsOutsideRange()
+{
+ m_fixture->output.setVolume(-0.1f);
+ QCOMPARE_EQ(m_fixture->output.volume(), 0.0f);
+ QCOMPARE(m_fixture->volumeChanged, SignalList({ { 0.0f } }));
+
+ m_fixture->output.setVolume(1.1f);
+ QCOMPARE_EQ(m_fixture->output.volume(), 1.0f);
+ QCOMPARE(m_fixture->volumeChanged, SignalList({ { 0.0f }, { 1.0f } }));
+}
+
+void tst_QMediaPlayerBackend::setVolume_doesNotChangeMutedState()
+{
+ m_fixture->output.setMuted(true);
+ m_fixture->output.setVolume(0.5f);
+ QVERIFY(m_fixture->output.isMuted());
+
+ m_fixture->output.setMuted(false);
+ m_fixture->output.setVolume(0.0f);
+ QVERIFY(!m_fixture->output.isMuted());
+}
+
+void tst_QMediaPlayerBackend::setMuted_changesMutedState_whenMutedStateChanged()
+{
+ m_fixture->output.setMuted(true);
+ QVERIFY(m_fixture->output.isMuted());
+ QCOMPARE(m_fixture->mutedChanged, SignalList({ { true } }));
+
+ // No new events emitted when muted state did not change
+ m_fixture->output.setMuted(true);
+ QCOMPARE(m_fixture->mutedChanged, SignalList({ { true } }));
+
+ m_fixture->output.setMuted(false);
+ QVERIFY(!m_fixture->output.isMuted());
+ QCOMPARE(m_fixture->mutedChanged, SignalList({ { true }, { false } }));
+
+ // No new events emitted when muted state did not change
+ m_fixture->output.setMuted(false);
+ QCOMPARE(m_fixture->mutedChanged, SignalList({ { true }, { false } }));
+}
+
+void tst_QMediaPlayerBackend::setMuted_doesNotChangeVolume()
+{
+ m_fixture->output.setVolume(0.5f);
+
+ m_fixture->output.setMuted(true);
+ QCOMPARE_EQ(m_fixture->output.volume(), 0.5f);
+
+ m_fixture->output.setMuted(false);
+ QCOMPARE_EQ(m_fixture->output.volume(), 0.5f);
+}
+
+void tst_QMediaPlayerBackend::processEOS()
+{
+ CHECK_SELECTED_URL(m_localWavFile);
+
+ m_fixture->player.setSource(*m_localWavFile);
+
+ m_fixture->player.play();
+ m_fixture->player.setPosition(900);
- positionSpy.clear();
- QTRY_VERIFY(player.position() > 100);
- QTRY_VERIFY(positionSpy.count() > 0);
- QVERIFY(positionSpy.last()[0].value<qint64>() > 100);
- player.setPosition(900);
//wait up to 5 seconds for EOS
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
- QVERIFY(statusSpy.count() > 0);
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia);
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(stateSpy.count(), 2);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::StoppedState);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+
+ QVERIFY(m_fixture->mediaStatusChanged.size() > 0);
+ QCOMPARE(m_fixture->mediaStatusChanged.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia);
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QCOMPARE(m_fixture->playbackStateChanged.size(), 2);
+ QCOMPARE(m_fixture->playbackStateChanged.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::StoppedState);
+
+ //at EOS the position stays at the end of file
+ QCOMPARE(m_fixture->player.position(), m_fixture->player.duration());
+ QTRY_VERIFY(m_fixture->positionChanged.size() > 0);
+ QTRY_COMPARE(m_fixture->positionChanged.last()[0].value<qint64>(), m_fixture->player.duration());
+
+ m_fixture->playbackStateChanged.clear();
+ m_fixture->mediaStatusChanged.clear();
+ m_fixture->positionChanged.clear();
+
+ m_fixture->player.play();
+
+ //position is reset to start
+ QTRY_VERIFY(m_fixture->player.position() < 100);
+ QTRY_VERIFY(m_fixture->positionChanged.size() > 0);
+ QCOMPARE(m_fixture->positionChanged.first()[0].value<qint64>(), 0);
+
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::PlayingState);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+
+ QCOMPARE(m_fixture->playbackStateChanged.size(), 1);
+ QCOMPARE(m_fixture->playbackStateChanged.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::PlayingState);
+ QVERIFY(m_fixture->mediaStatusChanged.size() > 0);
+ QCOMPARE(m_fixture->mediaStatusChanged.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia);
+
+ m_fixture->positionChanged.clear();
+ QTRY_VERIFY(m_fixture->player.position() > 100);
+ QTRY_VERIFY(m_fixture->positionChanged.size() > 0 && m_fixture->positionChanged.last()[0].value<qint64>() > 100);
+ m_fixture->player.setPosition(900);
+ //wait up to 5 seconds for EOS
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ QVERIFY(m_fixture->mediaStatusChanged.size() > 0);
+ QCOMPARE(m_fixture->mediaStatusChanged.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia);
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QCOMPARE(m_fixture->playbackStateChanged.size(), 2);
+ QCOMPARE(m_fixture->playbackStateChanged.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::StoppedState);
+
+ if (!isGStreamerPlatform()) {
+ // QTBUG-124517: for some media types gstreamer does not emit buffer progress messages
+ QCOMPARE_GT(m_fixture->bufferProgressChanged.size(), 1);
+ QCOMPARE(m_fixture->bufferProgressChanged.back().front(), 0.f);
+ }
- //position stays at the end of file
- QCOMPARE(player.position(), player.duration());
- QTRY_VERIFY(positionSpy.count() > 0);
- QTRY_COMPARE(positionSpy.last()[0].value<qint64>(), player.duration());
+ // position stays at the end of file
+ QCOMPARE(m_fixture->player.position(), m_fixture->player.duration());
+ QTRY_VERIFY(m_fixture->positionChanged.size() > 0);
+ QTRY_COMPARE(m_fixture->positionChanged.last()[0].value<qint64>(), m_fixture->player.duration());
//after setPosition EndOfMedia status should be reset to Loaded
- stateSpy.clear();
- statusSpy.clear();
- player.setPosition(500);
+ m_fixture->playbackStateChanged.clear();
+ m_fixture->mediaStatusChanged.clear();
+ m_fixture->player.setPosition(500);
//this transition can be async, so allow backend to perform it
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
- QCOMPARE(stateSpy.count(), 0);
- QTRY_VERIFY(statusSpy.count() > 0 &&
- statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::LoadedMedia);
+ QCOMPARE(m_fixture->playbackStateChanged.size(), 0);
+ QTRY_VERIFY(m_fixture->mediaStatusChanged.size() > 0 &&
+ m_fixture->mediaStatusChanged.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::LoadedMedia);
- player.play();
- player.setPosition(900);
+ m_fixture->player.play();
+ m_fixture->player.setPosition(900);
//wait up to 5 seconds for EOS
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
- QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(player.position(), player.duration());
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QCOMPARE(m_fixture->player.position(), m_fixture->player.duration());
- stateSpy.clear();
- statusSpy.clear();
- positionSpy.clear();
+ m_fixture->playbackStateChanged.clear();
+ m_fixture->mediaStatusChanged.clear();
+ m_fixture->positionChanged.clear();
// pause() should reset position to beginning and status to Buffered
- player.pause();
+ m_fixture->player.pause();
- QTRY_COMPARE(player.position(), 0);
- QTRY_VERIFY(positionSpy.count() > 0);
- QTRY_COMPARE(positionSpy.first()[0].value<qint64>(), 0);
+ QTRY_COMPARE(m_fixture->player.position(), 0);
+ QTRY_VERIFY(m_fixture->positionChanged.size() > 0);
+ QTRY_COMPARE(m_fixture->positionChanged.first()[0].value<qint64>(), 0);
- QCOMPARE(player.playbackState(), QMediaPlayer::PausedState);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::PausedState);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
- QCOMPARE(stateSpy.count(), 1);
- QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::PausedState);
- QVERIFY(statusSpy.count() > 0);
- QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia);
+ QCOMPARE(m_fixture->playbackStateChanged.size(), 1);
+ QCOMPARE(m_fixture->playbackStateChanged.last()[0].value<QMediaPlayer::PlaybackState>(), QMediaPlayer::PausedState);
+ QVERIFY(m_fixture->mediaStatusChanged.size() > 0);
+ QCOMPARE(m_fixture->mediaStatusChanged.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia);
}
// Helper class for tst_QMediaPlayerBackend::deleteLaterAtEOS()
@@ -603,7 +1522,7 @@ private slots:
void onMediaStatusChanged(QMediaPlayer::MediaStatus status)
{
if (status == QMediaPlayer::EndOfMedia) {
- player-> deleteLater();
+ player->deleteLater();
player = nullptr;
}
}
@@ -616,78 +1535,28 @@ private:
// QTBUG-24927 - deleteLater() called to QMediaPlayer from its signal handler does not work as expected
void tst_QMediaPlayerBackend::deleteLaterAtEOS()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ CHECK_SELECTED_URL(m_localWavFile);
QPointer<QMediaPlayer> player(new QMediaPlayer);
QAudioOutput output;
player->setAudioOutput(&output);
player->setPosition(800); // don't wait as long for EOS
DeleteLaterAtEos deleter(player);
- player->setSource(localWavFile);
+ player->setSource(*m_localWavFile);
// Create an event loop for verifying deleteLater behavior instead of using
// QTRY_VERIFY or QTest::qWait. QTest::qWait makes extra effort to process
// DeferredDelete events during the wait, which interferes with this test.
QEventLoop loop;
- QTimer::singleShot(0, &deleter, SLOT(play()));
- QTimer::singleShot(5000, &loop, SLOT(quit()));
- connect(player.data(), SIGNAL(destroyed()), &loop, SLOT(quit()));
+ QTimer::singleShot(0, &deleter, &DeleteLaterAtEos::play);
+ QTimer::singleShot(5000, &loop, &QEventLoop::quit);
+ connect(player.data(), &QObject::destroyed, &loop, &QEventLoop::quit);
loop.exec();
// Verify that the player was destroyed within the event loop.
// This check will fail without the fix for QTBUG-24927.
QVERIFY(player.isNull());
}
-void tst_QMediaPlayerBackend::volumeAndMuted()
-{
- //volume and muted properties should be independent
- QMediaPlayer player;
- QAudioOutput output;
- player.setAudioOutput(&output);
- QCOMPARE(output.volume(), 1.);
- QVERIFY(!output.isMuted());
-
- player.setSource(localWavFile);
- player.pause();
-
- QCOMPARE(output.volume(), 1.);
- QVERIFY(!output.isMuted());
-
- QSignalSpy volumeSpy(&output, SIGNAL(volumeChanged(float)));
- QSignalSpy mutedSpy(&output, SIGNAL(mutedChanged(bool)));
-
- //setting volume to 0 should not trigger muted
- output.setVolume(0);
- QTRY_COMPARE(output.volume(), 0);
- QVERIFY(!output.isMuted());
- QCOMPARE(volumeSpy.count(), 1);
- QCOMPARE(volumeSpy.last()[0].toFloat(), output.volume());
- QCOMPARE(mutedSpy.count(), 0);
-
- output.setVolume(0.5);
- QTRY_COMPARE(output.volume(), 0.5);
- QVERIFY(!output.isMuted());
- QCOMPARE(volumeSpy.count(), 2);
- QCOMPARE(volumeSpy.last()[0].toFloat(), output.volume());
- QCOMPARE(mutedSpy.count(), 0);
-
- output.setMuted(true);
- QTRY_VERIFY(output.isMuted());
- QVERIFY(output.volume() > 0);
- QCOMPARE(volumeSpy.count(), 2);
- QCOMPARE(mutedSpy.count(), 1);
- QCOMPARE(mutedSpy.last()[0].toBool(), output.isMuted());
-
- output.setMuted(false);
- QTRY_VERIFY(!output.isMuted());
- QVERIFY(output.volume() > 0);
- QCOMPARE(volumeSpy.count(), 2);
- QCOMPARE(mutedSpy.count(), 2);
- QCOMPARE(mutedSpy.last()[0].toBool(), output.isMuted());
-
-}
-
void tst_QMediaPlayerBackend::volumeAcrossFiles_data()
{
QTest::addColumn<int>("volume");
@@ -703,12 +1572,15 @@ void tst_QMediaPlayerBackend::volumeAcrossFiles_data()
void tst_QMediaPlayerBackend::volumeAcrossFiles()
{
+ CHECK_SELECTED_URL(m_localWavFile);
+
QFETCH(int, volume);
QFETCH(bool, muted);
float vol = volume/100.;
- QMediaPlayer player;
QAudioOutput output;
+ QMediaPlayer player;
+
player.setAudioOutput(&output);
//volume and muted should not be preserved between player instances
@@ -721,7 +1593,7 @@ void tst_QMediaPlayerBackend::volumeAcrossFiles()
QTRY_COMPARE(output.volume(), vol);
QTRY_COMPARE(output.isMuted(), muted);
- player.setSource(localWavFile);
+ player.setSource(*m_localWavFile);
QCOMPARE(output.volume(), vol);
QCOMPARE(output.isMuted(), muted);
@@ -737,7 +1609,7 @@ void tst_QMediaPlayerBackend::volumeAcrossFiles()
QTRY_COMPARE(output.volume(), vol);
QCOMPARE(output.isMuted(), muted);
- player.setSource(localWavFile);
+ player.setSource(*m_localWavFile);
player.pause();
QTRY_COMPARE(output.volume(), vol);
@@ -746,15 +1618,14 @@ void tst_QMediaPlayerBackend::volumeAcrossFiles()
void tst_QMediaPlayerBackend::initialVolume()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ CHECK_SELECTED_URL(m_localWavFile);
{
- QMediaPlayer player;
QAudioOutput output;
+ QMediaPlayer player;
player.setAudioOutput(&output);
output.setVolume(1);
- player.setSource(localWavFile);
+ player.setSource(*m_localWavFile);
QCOMPARE(output.volume(), 1);
player.play();
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
@@ -762,10 +1633,10 @@ void tst_QMediaPlayerBackend::initialVolume()
}
{
- QMediaPlayer player;
QAudioOutput output;
+ QMediaPlayer player;
player.setAudioOutput(&output);
- player.setSource(localWavFile);
+ player.setSource(*m_localWavFile);
QCOMPARE(output.volume(), 1);
player.play();
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
@@ -775,22 +1646,25 @@ void tst_QMediaPlayerBackend::initialVolume()
void tst_QMediaPlayerBackend::seekPauseSeek()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+#ifdef Q_OS_ANDROID
+ QSKIP("frame.toImage will return null image because of QTBUG-108446");
+#endif
+ CHECK_SELECTED_URL(m_localVideoFile);
- QMediaPlayer player;
+ TestVideoSink surface(true);
QAudioOutput output;
+ QMediaPlayer player;
+
player.setAudioOutput(&output);
- QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
+ QSignalSpy positionSpy(&player, &QMediaPlayer::positionChanged);
- TestVideoSink *surface = new TestVideoSink;
- player.setVideoOutput(surface);
+ player.setVideoOutput(&surface);
- player.setSource(localVideoFile);
+ player.setSource(*m_localVideoFile);
QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
- QVERIFY(surface->m_frameList.isEmpty()); // frame must not appear until we call pause() or play()
+ QVERIFY(surface.m_frameList.isEmpty()); // frame must not appear until we call pause() or play()
positionSpy.clear();
qint64 position = 7000;
@@ -799,21 +1673,22 @@ void tst_QMediaPlayerBackend::seekPauseSeek()
QTRY_COMPARE(player.position(), position);
QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
QTest::qWait(250); // wait a bit to ensure the frame is not rendered
- QVERIFY(surface->m_frameList.isEmpty()); // still no frame, we must call pause() or play() to see a frame
+ QVERIFY(surface.m_frameList
+ .isEmpty()); // still no frame, we must call pause() or play() to see a frame
player.pause();
QTRY_COMPARE(player.playbackState(), QMediaPlayer::PausedState); // it might take some time for the operation to be completed
- QTRY_VERIFY(!surface->m_frameList.isEmpty()); // we must see a frame at position 7000 here
+ QTRY_VERIFY(!surface.m_frameList.isEmpty()); // we must see a frame at position 7000 here
// Make sure that the frame has a timestamp before testing - not all backends provides this
- if (!surface->m_frameList.back().isValid() || surface->m_frameList.back().startTime() < 0)
+ if (!surface.m_frameList.back().isValid() || surface.m_frameList.back().startTime() < 0)
QSKIP("No timestamp");
{
- QVideoFrame frame = surface->m_frameList.back();
+ QVideoFrame frame = surface.m_frameList.back();
const qint64 elapsed = (frame.startTime() / 1000) - position; // frame.startTime() is microsecond, position is milliseconds.
QVERIFY2(qAbs(elapsed) < (qint64)500, QByteArray::number(elapsed).constData());
- QCOMPARE(frame.width(), 160);
+ QCOMPARE(frame.width(), 213);
QCOMPARE(frame.height(), 120);
// create QImage for QVideoFrame to verify RGB pixel colors
@@ -824,19 +1699,19 @@ void tst_QMediaPlayerBackend::seekPauseSeek()
QVERIFY(qBlue(image.pixel(0, 0)) < 20);
}
- surface->m_frameList.clear();
+ surface.m_frameList.clear();
positionSpy.clear();
position = 12000;
player.setPosition(position);
QTRY_VERIFY(!positionSpy.isEmpty() && qAbs(player.position() - position) < (qint64)500);
QCOMPARE(player.playbackState(), QMediaPlayer::PausedState);
- QTRY_VERIFY(!surface->m_frameList.isEmpty());
+ QTRY_VERIFY(!surface.m_frameList.isEmpty());
{
- QVideoFrame frame = surface->m_frameList.back();
+ QVideoFrame frame = surface.m_frameList.back();
const qint64 elapsed = (frame.startTime() / 1000) - position;
QVERIFY2(qAbs(elapsed) < (qint64)500, QByteArray::number(elapsed).constData());
- QCOMPARE(frame.width(), 160);
+ QCOMPARE(frame.width(), 213);
QCOMPARE(frame.height(), 120);
QImage image = frame.toImage();
@@ -849,19 +1724,19 @@ void tst_QMediaPlayerBackend::seekPauseSeek()
void tst_QMediaPlayerBackend::seekInStoppedState()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ CHECK_SELECTED_URL(m_localVideoFile);
- QMediaPlayer player;
+ TestVideoSink surface(false);
QAudioOutput output;
+ QMediaPlayer player;
+
player.setAudioOutput(&output);
- TestVideoSink surface(false);
player.setVideoOutput(&surface);
- QSignalSpy stateSpy(&player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)));
- QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
+ QSignalSpy stateSpy(&player, &QMediaPlayer::playbackStateChanged);
+ QSignalSpy positionSpy(&player, &QMediaPlayer::positionChanged);
- player.setSource(localVideoFile);
+ player.setSource(*m_localVideoFile);
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
QCOMPARE(player.position(), 0);
@@ -874,11 +1749,11 @@ void tst_QMediaPlayerBackend::seekInStoppedState()
player.setPosition(position);
QTRY_VERIFY(qAbs(player.position() - position) < qint64(200));
- QTRY_VERIFY(positionSpy.count() > 0);
+ QTRY_VERIFY(positionSpy.size() > 0);
QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(200));
QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(stateSpy.count(), 0);
+ QCOMPARE(stateSpy.size(), 0);
QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
@@ -888,12 +1763,11 @@ void tst_QMediaPlayerBackend::seekInStoppedState()
QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
QTRY_VERIFY(player.position() > position);
- QCOMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
QTest::qWait(100);
// Check that it never played from the beginning
QVERIFY(player.position() > position);
- for (int i = 0; i < positionSpy.count(); ++i)
+ for (int i = 0; i < positionSpy.size(); ++i)
QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 200));
// ------
@@ -910,11 +1784,11 @@ void tst_QMediaPlayerBackend::seekInStoppedState()
player.setPosition(position);
QTRY_VERIFY(qAbs(player.position() - position) < qint64(200));
- QTRY_VERIFY(positionSpy.count() > 0);
+ QTRY_VERIFY(positionSpy.size() > 0);
QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(200));
QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(stateSpy.count(), 0);
+ QCOMPARE(stateSpy.size(), 0);
QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
@@ -923,13 +1797,12 @@ void tst_QMediaPlayerBackend::seekInStoppedState()
player.play();
QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
- QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
QVERIFY(qAbs(player.position() - position) < qint64(200));
QTest::qWait(500);
// Check that it never played from the beginning
QVERIFY(player.position() > (position - 200));
- for (int i = 0; i < positionSpy.count(); ++i)
+ for (int i = 0; i < positionSpy.size(); ++i)
QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 200));
// ------
@@ -946,11 +1819,11 @@ void tst_QMediaPlayerBackend::seekInStoppedState()
player.setPosition(position);
QTRY_VERIFY(qAbs(player.position() - position) < qint64(200));
- QTRY_VERIFY(positionSpy.count() > 0);
+ QTRY_VERIFY(positionSpy.size() > 0);
QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(200));
QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(stateSpy.count(), 0);
+ QCOMPARE(stateSpy.size(), 0);
QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
player.play();
@@ -963,19 +1836,18 @@ void tst_QMediaPlayerBackend::seekInStoppedState()
QTest::qWait(500);
// Check that it never played from the beginning
QVERIFY(player.position() > (position - 200));
- for (int i = 0; i < positionSpy.count(); ++i)
+ for (int i = 0; i < positionSpy.size(); ++i)
QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 200));
}
void tst_QMediaPlayerBackend::subsequentPlayback()
{
- if (localCompressedSoundFile.isEmpty())
- QSKIP("Sound format is not supported");
+ CHECK_SELECTED_URL(m_localCompressedSoundFile);
- QMediaPlayer player;
QAudioOutput output;
+ QMediaPlayer player;
player.setAudioOutput(&output);
- player.setSource(localCompressedSoundFile);
+ player.setSource(*m_localCompressedSoundFile);
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
QTRY_VERIFY(player.isSeekable());
player.setPosition(5000);
@@ -991,7 +1863,7 @@ void tst_QMediaPlayerBackend::subsequentPlayback()
player.play();
QTRY_COMPARE(player.playbackState(), QMediaPlayer::PlayingState);
- QTRY_VERIFY(player.position() > 1000);
+ QTRY_COMPARE_GT(player.position(), 1000);
player.pause();
QCOMPARE(player.playbackState(), QMediaPlayer::PausedState);
// make sure position does not "jump" closer to the end of the file
@@ -1001,26 +1873,26 @@ void tst_QMediaPlayerBackend::subsequentPlayback()
QTRY_COMPARE(player.position(), qint64(0));
player.play();
QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
- QTRY_VERIFY(player.position() > 1000);
+ QTRY_COMPARE_GT(player.position(), 1000);
player.pause();
QCOMPARE(player.playbackState(), QMediaPlayer::PausedState);
- QVERIFY(player.position() > 1000);
+ QCOMPARE_GT(player.position(), 1000);
}
void tst_QMediaPlayerBackend::multipleMediaPlayback()
{
- if (localVideoFile.isEmpty() || localVideoFile2.isEmpty())
- QSKIP("Video format is not supported");
+ CHECK_SELECTED_URL(m_localVideoFile);
+ CHECK_SELECTED_URL(m_localVideoFile2);
+ QAudioOutput output;
TestVideoSink surface(false);
QMediaPlayer player;
- QAudioOutput output;
player.setVideoOutput(&surface);
player.setAudioOutput(&output);
- player.setSource(localVideoFile);
+ player.setSource(*m_localVideoFile);
- QCOMPARE(player.source(), localVideoFile);
+ QCOMPARE(player.source(), *m_localVideoFile);
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
player.setPosition(0);
@@ -1028,14 +1900,15 @@ void tst_QMediaPlayerBackend::multipleMediaPlayback()
QCOMPARE(player.error(), QMediaPlayer::NoError);
QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
+ QVERIFY(player.isSeekable());
QTRY_VERIFY(player.position() > 0);
- QCOMPARE(player.source(), localVideoFile);
+ QCOMPARE(player.source(), *m_localVideoFile);
player.stop();
- player.setSource(localVideoFile2);
+ player.setSource(*m_localVideoFile2);
- QCOMPARE(player.source(), localVideoFile2);
+ QCOMPARE(player.source(), *m_localVideoFile2);
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
QTRY_VERIFY(player.isSeekable());
@@ -1045,95 +1918,389 @@ void tst_QMediaPlayerBackend::multipleMediaPlayback()
QCOMPARE(player.error(), QMediaPlayer::NoError);
QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
QTRY_VERIFY(player.position() > 0);
- QCOMPARE(player.source(), localVideoFile2);
+ QCOMPARE(player.source(), *m_localVideoFile2);
player.stop();
QTRY_COMPARE(player.playbackState(), QMediaPlayer::StoppedState);
}
-void tst_QMediaPlayerBackend::surfaceTest()
+void tst_QMediaPlayerBackend::multiplePlaybackRateChangingStressTest()
{
- // 25 fps video file
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("SKIP on macOS CI since multiple fake drawing on macOS CI platform causes UB. To be "
+ "investigated.");
+#endif
TestVideoSink surface(false);
+ QAudioOutput output;
QMediaPlayer player;
+
+ player.setAudioOutput(&output);
+ player.setVideoOutput(&surface);
+
+ player.setSource(*m_localVideoFile3ColorsWithSound);
+
+ player.play();
+
+ surface.waitForFrame();
+
+ QSignalSpy spy(&player, &QMediaPlayer::playbackStateChanged);
+
+ constexpr qint64 expectedVideoDuration = 3000;
+ constexpr int waitingInterval = 200;
+ constexpr qint64 maxDuration = expectedVideoDuration + 2000;
+ constexpr qint64 minDuration = expectedVideoDuration - 100;
+ constexpr qint64 maxFrameDelay = 2000;
+
+ surface.m_elapsedTimer.start();
+
+ qint64 duration = 0;
+
+ for (int i = 0; !spy.wait(waitingInterval); ++i) {
+ duration += waitingInterval * player.playbackRate();
+
+ player.setPlaybackRate(0.5 * (i % 4 + 1));
+
+ QCOMPARE_LE(duration, maxDuration);
+
+ QVERIFY2(surface.m_elapsedTimer.elapsed() < maxFrameDelay,
+ "If the delay is more than 2s, we consider the video playing is hanging.");
+
+ /* Some debug code for windows. Use the code instead of the check above to debug the bug.
+ * https://bugreports.qt.io/browse/QTBUG-105940.
+ * TODO: fix hanging on windows and remove.
+ if ( surface.m_elapsedTimer.elapsed() > maxFrameDelay ) {
+ qDebug() << "pause/play";
+ player.pause();
+ player.play();
+ surface.m_elapsedTimer.restart();
+ spy.clear();
+ }*/
+ }
+
+ duration += waitingInterval * player.playbackRate();
+
+ QCOMPARE_GT(duration, minDuration);
+
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(spy.at(0).size(), 1);
+ QCOMPARE(spy.at(0).at(0).value<QMediaPlayer::PlaybackState>(), QMediaPlayer::StoppedState);
+
+ QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
+ QCOMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+}
+
+void tst_QMediaPlayerBackend::multipleSeekStressTest()
+{
+#ifdef Q_OS_ANDROID
+ QSKIP("frame.toImage will return null image because of QTBUG-108446");
+#endif
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+ TestVideoSink surface(false);
QAudioOutput output;
+ QMediaPlayer player;
+
player.setAudioOutput(&output);
player.setVideoOutput(&surface);
- player.setSource(localVideoFile);
+
+ player.setSource(*m_localVideoFile3ColorsWithSound);
+
player.play();
- QTRY_VERIFY(player.position() >= 1000);
- QVERIFY2(surface.m_totalFrames >= 25, qPrintable(QString("Expected >= 25, got %1").arg(surface.m_totalFrames)));
+
+ auto waitAndCheckFrame = [&](qint64 pos, QString checkInfo) {
+ auto errorPrintingGuard = qScopeGuard([&]() {
+ qDebug() << "Error:" << checkInfo;
+ qDebug() << "Position:" << pos;
+ });
+
+ auto frame = surface.waitForFrame();
+ QVERIFY(frame.isValid());
+
+ const auto trackTime = pos * 1000;
+
+ // in theory, previous frame might be received, in this case we wait for a new one that is
+ // expected to be relevant
+ if (frame.endTime() < trackTime || frame.startTime() > trackTime) {
+ frame = surface.waitForFrame();
+ QVERIFY(frame.isValid());
+ }
+
+ QCOMPARE_GE(frame.startTime(), trackTime - 200'000);
+ QCOMPARE_LE(frame.endTime(), trackTime + 200'000);
+
+ auto frameImage = frame.toImage();
+ const auto actualColor = frameImage.pixel(1, 1);
+
+ const auto actualColorIndex = findSimilarColorIndex(m_video3Colors, actualColor);
+
+ const auto expectedColorIndex = pos / 1000;
+
+ QCOMPARE(actualColorIndex, expectedColorIndex);
+
+ errorPrintingGuard.dismiss();
+ };
+
+ auto seekAndCheck = [&](qint64 pos) {
+ QSignalSpy positionSpy(&player, &QMediaPlayer::positionChanged);
+ player.setPosition(pos);
+
+ QTRY_VERIFY(positionSpy.size() >= 1);
+ int setPosition = positionSpy.first().first().toInt();
+ QCOMPARE_GT(setPosition, pos - 100);
+ QCOMPARE_LT(setPosition, pos + 100);
+ };
+
+ constexpr qint64 posInterval = 10;
+
+ {
+ for (qint64 pos = posInterval; pos <= 2200; pos += posInterval)
+ seekAndCheck(pos);
+
+ waitAndCheckFrame(2200, "emulate fast moving of a seek slider forward");
+
+ QCOMPARE_NE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
+ }
+
+ {
+ for (qint64 pos = 2100; pos >= 800; pos -= posInterval)
+ seekAndCheck(pos);
+
+ waitAndCheckFrame(800, "emulate fast moving of a seek slider backward");
+
+ QCOMPARE_NE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ QCOMPARE(player.playbackState(), QMediaPlayer::PlayingState);
+ }
+
+ {
+ player.pause();
+
+ for (qint64 pos = 500; pos <= 1100; pos += posInterval)
+ seekAndCheck(pos);
+
+ waitAndCheckFrame(1100, "emulate fast moving of a seek slider forward on paused state");
+
+ QCOMPARE_NE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ QCOMPARE(player.playbackState(), QMediaPlayer::PausedState);
+ }
+}
+
+void tst_QMediaPlayerBackend::setPlaybackRate_changesActualRateAndFramesRenderingTime_data()
+{
+ QTest::addColumn<bool>("withAudio");
+ QTest::addColumn<int>("positionDeviationMs");
+
+ QTest::newRow("Without audio") << false << 150;
+
+ // set greater positionDeviationMs for case with audio due to possible synchronization.
+ QTest::newRow("With audio") << true << 200;
}
-#if 0
-void tst_QMediaPlayerBackend::multipleSurfaces()
+void tst_QMediaPlayerBackend::setPlaybackRate_changesActualRateAndFramesRenderingTime()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ QFETCH(bool, withAudio);
+ QFETCH(int, positionDeviationMs);
+
+#ifdef Q_OS_ANDROID
+ QSKIP("frame.toImage will return null image because of QTBUG-108446");
+#endif
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("SKIP on macOS CI since multiple fake drawing on macOS CI platform causes UB. To be "
+ "investigated: QTBUG-111744");
+#endif
+ m_fixture->player.setAudioOutput(
+ withAudio ? &m_fixture->output
+ : nullptr); // TODO: mock audio output and check sound by frequency
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+
+ auto checkColorAndPosition = [&](qint64 expectedPosition, QString errorTag) {
+ constexpr qint64 intervalTime = 1000;
+
+ const int colorIndex = expectedPosition / intervalTime;
+ const auto expectedColor = m_video3Colors[colorIndex];
+ const auto actualPosition = m_fixture->player.position();
+
+ auto frame = m_fixture->surface.videoFrame();
+ auto image = frame.toImage();
+ QVERIFY(!image.isNull());
+
+ const auto actualColor = image.pixel(1, 1);
+
+ auto errorPrintingGuard = qScopeGuard([&]() {
+ qDebug() << "Error Tag:" << errorTag;
+ qDebug() << " Actual Color:" << QColor(actualColor)
+ << " Expected Color:" << QColor(expectedColor);
+ qDebug() << " Most probable actual color index:"
+ << findSimilarColorIndex(m_video3Colors, actualColor)
+ << "Expected color index:" << colorIndex;
+ qDebug() << " Actual position:" << actualPosition;
+ qDebug() << " Frame start time:" << frame.startTime();
+ });
+
+ // TODO: investigate why frames sometimes are not delivered in time on windows
+ constexpr qreal maxColorDifference = 0.18;
+ QVERIFY(m_fixture->player.isPlaying());
+ QCOMPARE_LE(colorDifference(actualColor, expectedColor), maxColorDifference);
+ QCOMPARE_GT(actualPosition, expectedPosition - positionDeviationMs);
+ QCOMPARE_LT(actualPosition, expectedPosition + positionDeviationMs);
+
+ const auto framePosition = frame.startTime() / 1000;
+
+ QCOMPARE_GT(framePosition, expectedPosition - positionDeviationMs);
+ QCOMPARE_LT(framePosition, expectedPosition + positionDeviationMs);
+ QCOMPARE_LT(qAbs(framePosition - actualPosition), positionDeviationMs);
+
+ errorPrintingGuard.dismiss();
+ };
+
+ m_fixture->player.play();
+
+ m_fixture->surface.waitForFrame();
+
+ auto waitUntil = [&](qint64 targetPosition) {
+ const auto position = m_fixture->player.position();
+
+ const auto waitingIntervalMs =
+ static_cast<int>((targetPosition - position) / m_fixture->player.playbackRate());
+
+ if (targetPosition > position)
+ QTest::qWait(waitingIntervalMs);
- QVideoSink surface1;
- QVideoSink surface2;
+ qDebug() << "Test waiting:" << waitingIntervalMs << "ms, Position:" << position << "=>"
+ << m_fixture->player.position() << "Expected target position:" << targetPosition
+ << "playbackRate:" << m_fixture->player.playbackRate();
+ };
+ waitUntil(400);
+ checkColorAndPosition(400, "Check default playback rate");
+
+ m_fixture->player.setPlaybackRate(2.);
+
+ waitUntil(1400);
+ checkColorAndPosition(1400, "Check 2.0 playback rate");
+
+ m_fixture->player.setPlaybackRate(0.5);
+
+ waitUntil(1800);
+ checkColorAndPosition(1800, "Check 0.5 playback rate");
+
+ m_fixture->player.setPlaybackRate(0.321);
+
+ m_fixture->player.stop();
+}
+
+void tst_QMediaPlayerBackend::surfaceTest()
+{
+ CHECK_SELECTED_URL(m_localVideoFile);
+ // 25 fps video file
+
+ QAudioOutput output;
+ TestVideoSink surface(false);
QMediaPlayer player;
- player.setVideoOutput(QList<QVideoSink *>() << &surface1 << &surface2);
- player.setSource(localVideoFile);
+ player.setAudioOutput(&output);
+ player.setVideoOutput(&surface);
+ player.setSource(*m_localVideoFile);
player.play();
QTRY_VERIFY(player.position() >= 1000);
-// QVERIFY2(surface1.m_totalFrames >= 25, qPrintable(QString("Expected >= 25, got %1").arg(surface1.m_totalFrames)));
-// QVERIFY2(surface2.m_totalFrames >= 25, qPrintable(QString("Expected >= 25, got %1").arg(surface2.m_totalFrames)));
-// QCOMPARE(surface1.m_totalFrames, surface2.m_totalFrames);
+ QVERIFY2(surface.m_totalFrames >= 25, qPrintable(QStringLiteral("Expected >= 25, got %1").arg(surface.m_totalFrames)));
}
-#endif
void tst_QMediaPlayerBackend::metadata()
{
- if (localFileWithMetadata.isEmpty())
- QSKIP("No supported media file");
+ CHECK_SELECTED_URL(m_localFileWithMetadata);
- QMediaPlayer player;
- QAudioOutput output;
- player.setAudioOutput(&output);
+ m_fixture->player.setSource(*m_localFileWithMetadata);
- QSignalSpy metadataChangedSpy(&player, SIGNAL(metaDataChanged()));
+ QTRY_VERIFY(m_fixture->metadataChanged.size() > 0);
- player.setSource(localFileWithMetadata);
+ const QMediaMetaData metadata = m_fixture->player.metaData();
+ QCOMPARE(metadata.value(QMediaMetaData::Title).toString(), QStringLiteral("Nokia Tune"));
+ QCOMPARE(metadata.value(QMediaMetaData::ContributingArtist).toString(), QStringLiteral("TestArtist"));
+ QCOMPARE(metadata.value(QMediaMetaData::AlbumTitle).toString(), QStringLiteral("TestAlbum"));
+ QCOMPARE(metadata.value(QMediaMetaData::Duration), QVariant(7704));
+ QVERIFY(!metadata.value(QMediaMetaData::ThumbnailImage).value<QImage>().isNull());
+ m_fixture->clearSpies();
- QTRY_VERIFY(metadataChangedSpy.count() > 0);
+ m_fixture->player.setSource(QUrl());
- QCOMPARE(player.metaData().value(QMediaMetaData::Title).toString(), QStringLiteral("Nokia Tune"));
- QCOMPARE(player.metaData().value(QMediaMetaData::ContributingArtist).toString(), QStringLiteral("TestArtist"));
- QCOMPARE(player.metaData().value(QMediaMetaData::AlbumTitle).toString(), QStringLiteral("TestAlbum"));
+ QCOMPARE(m_fixture->metadataChanged.size(), 1);
+ QVERIFY(m_fixture->player.metaData().isEmpty());
+}
- metadataChangedSpy.clear();
+void tst_QMediaPlayerBackend::metadata_returnsMetadataWithThumbnail_whenMediaHasThumbnail_data()
+{
+ QTest::addColumn<MaybeUrl>("mediaUrl");
+ QTest::addColumn<bool>("hasThumbnail");
+ QTest::addColumn<QSize>("expectedSize");
+ QTest::addColumn<QColor>("expectedColor");
+
+ QTest::addRow("jpeg thumbnail") << m_videoFileWithJpegThumbnail << true << QSize{ 20, 28 } << QColor(35, 177, 77);
+ QTest::addRow("png thumbnail") << m_videoFileWithPngThumbnail << true << QSize{ 20, 28 } << QColor(35, 177, 77);
+ QTest::addRow("no thumbnail") << m_localVideoFile3ColorsWithSound << false << QSize{ 0, 0 } << QColor(0, 0, 0);
+}
- player.setSource(QUrl());
+void tst_QMediaPlayerBackend::metadata_returnsMetadataWithThumbnail_whenMediaHasThumbnail()
+{
+ // QTBUG-124380: gstreamer reports CoverArtImage instead of ThumbnailImage
+ QMediaMetaData::Key key =
+ isGStreamerPlatform() ? QMediaMetaData::CoverArtImage : QMediaMetaData::ThumbnailImage;
+
+ // Arrange
+ QFETCH(const MaybeUrl, mediaUrl);
+ QFETCH(const bool, hasThumbnail);
+ QFETCH(const QSize, expectedSize);
+ QFETCH(const QColor, expectedColor);
+
+ CHECK_SELECTED_URL(mediaUrl);
+
+ m_fixture->player.setSource(*mediaUrl);
+ QTRY_VERIFY(!m_fixture->metadataChanged.empty());
+
+ // Act
+ const QMediaMetaData metadata = m_fixture->player.metaData();
+ const QImage thumbnail = metadata.value(key).value<QImage>();
+
+ // Assert
+ QCOMPARE_EQ(!thumbnail.isNull(), hasThumbnail);
+ QCOMPARE_EQ(thumbnail.size(), expectedSize);
- QCOMPARE(metadataChangedSpy.count(), 1);
- QVERIFY(player.metaData().isEmpty());
+ if (hasThumbnail) {
+ const QPoint center{ expectedSize.width() / 2, expectedSize.height() / 2 };
+ const auto centerColor = thumbnail.pixelColor(center);
+
+ constexpr int maxChannelDiff = 5;
+ QCOMPARE_LT(std::abs(centerColor.red() - expectedColor.red()), maxChannelDiff);
+ QCOMPARE_LT(std::abs(centerColor.green() - expectedColor.green()), maxChannelDiff);
+ QCOMPARE_LT(std::abs(centerColor.blue() - expectedColor.blue()), maxChannelDiff);
+ }
}
void tst_QMediaPlayerBackend::playerStateAtEOS()
{
- if (!isWavSupported())
- QSKIP("Sound format is not supported");
+ CHECK_SELECTED_URL(m_localWavFile);
- QMediaPlayer player;
QAudioOutput output;
+ QMediaPlayer player;
player.setAudioOutput(&output);
bool endOfMediaReceived = false;
- connect(&player, &QMediaPlayer::mediaStatusChanged, [&](QMediaPlayer::MediaStatus status) {
+ connect(&player, &QMediaPlayer::mediaStatusChanged,
+ this, [&](QMediaPlayer::MediaStatus status) {
if (status == QMediaPlayer::EndOfMedia) {
QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState);
endOfMediaReceived = true;
}
});
- player.setSource(localWavFile);
+ player.setSource(*m_localWavFile);
player.play();
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
@@ -1142,74 +2309,64 @@ void tst_QMediaPlayerBackend::playerStateAtEOS()
void tst_QMediaPlayerBackend::playFromBuffer()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ CHECK_SELECTED_URL(m_localVideoFile);
TestVideoSink surface(false);
QMediaPlayer player;
player.setVideoOutput(&surface);
- QFile file(localVideoFile.toLocalFile());
- if (!file.open(QIODevice::ReadOnly))
- QSKIP("Could not open file");
- player.setSourceDevice(&file, localVideoFile);
+ QFile file(u":"_s + m_localVideoFile->toEncoded(QUrl::RemoveScheme));
+ QVERIFY(file.open(QIODevice::ReadOnly));
+
+ player.setSourceDevice(&file, *m_localVideoFile);
player.play();
QTRY_VERIFY(player.position() >= 1000);
- QVERIFY2(surface.m_totalFrames >= 25, qPrintable(QString("Expected >= 25, got %1").arg(surface.m_totalFrames)));
+ QVERIFY2(surface.m_totalFrames >= 25, qPrintable(QStringLiteral("Expected >= 25, got %1").arg(surface.m_totalFrames)));
}
void tst_QMediaPlayerBackend::audioVideoAvailable()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ CHECK_SELECTED_URL(m_localVideoFile);
TestVideoSink surface(false);
QAudioOutput output;
QMediaPlayer player;
- QSignalSpy hasVideoSpy(&player, SIGNAL(hasVideoChanged(bool)));
- QSignalSpy hasAudioSpy(&player, SIGNAL(hasAudioChanged(bool)));
+ QSignalSpy hasVideoSpy(&player, &QMediaPlayer::hasVideoChanged);
+ QSignalSpy hasAudioSpy(&player, &QMediaPlayer::hasAudioChanged);
player.setVideoOutput(&surface);
player.setAudioOutput(&output);
- player.setSource(localVideoFile);
+ player.setSource(*m_localVideoFile);
QTRY_VERIFY(player.hasVideo());
QTRY_VERIFY(player.hasAudio());
- QCOMPARE(hasVideoSpy.count(), 1);
- QCOMPARE(hasAudioSpy.count(), 1);
+ QCOMPARE(hasVideoSpy.size(), 1);
+ QCOMPARE(hasAudioSpy.size(), 1);
player.setSource(QUrl());
QTRY_VERIFY(!player.hasVideo());
QTRY_VERIFY(!player.hasAudio());
- QCOMPARE(hasVideoSpy.count(), 2);
- QCOMPARE(hasAudioSpy.count(), 2);
+ QCOMPARE(hasVideoSpy.size(), 2);
+ QCOMPARE(hasAudioSpy.size(), 2);
}
void tst_QMediaPlayerBackend::isSeekable()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ CHECK_SELECTED_URL(m_localVideoFile);
TestVideoSink surface(false);
QMediaPlayer player;
player.setVideoOutput(&surface);
-#ifdef Q_OS_ANDROID
- QEXPECT_FAIL("", "On Android isSeekable() is always set to true due to QTBUG-96952", Continue);
-#endif
QVERIFY(!player.isSeekable());
- player.setSource(localVideoFile);
+ player.setSource(*m_localVideoFile);
QTRY_VERIFY(player.isSeekable());
}
void tst_QMediaPlayerBackend::positionAfterSeek()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ CHECK_SELECTED_URL(m_localVideoFile);
TestVideoSink surface(false);
QMediaPlayer player;
player.setVideoOutput(&surface);
-#ifdef Q_OS_ANDROID
- QEXPECT_FAIL("", "On Android isSeekable() is always set to true due to QTBUG-96952", Continue);
-#endif
QVERIFY(!player.isSeekable());
- player.setSource(localVideoFile);
+ player.setSource(*m_localVideoFile);
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
player.pause();
player.setPosition(500);
@@ -1224,41 +2381,55 @@ void tst_QMediaPlayerBackend::positionAfterSeek()
QTRY_VERIFY(player.position() < 700);
}
-void tst_QMediaPlayerBackend::videoDimensions()
+void tst_QMediaPlayerBackend::pause_rendersVideoAtCorrectResolution_data()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ QTest::addColumn<MaybeUrl>("mediaFile");
+ QTest::addColumn<int>("width");
+ QTest::addColumn<int>("height");
+ QTest::addRow("mp4") << m_videoDimensionTestFile << 540 << 320;
+ QTest::addRow("av1") << m_av1File << 160 * 143 / 80 << 160;
+}
+
+void tst_QMediaPlayerBackend::pause_rendersVideoAtCorrectResolution()
+{
+ QFETCH(const MaybeUrl, mediaFile);
+ QFETCH(const int, width);
+ QFETCH(const int, height);
+ CHECK_SELECTED_URL(mediaFile);
+
+ // Arrange
TestVideoSink surface(true);
QMediaPlayer player;
player.setVideoOutput(&surface);
-#ifdef Q_OS_ANDROID
- QEXPECT_FAIL("", "On Android isSeekable() is always set to true due to QTBUG-96952", Continue);
-#endif
QVERIFY(!player.isSeekable());
- player.setSource(videoDimensionTestFile);
+ player.setSource(*mediaFile);
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ // Act
player.pause();
+
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QEXPECT_FAIL("av1", "QTBUG-119711: AV1 decoding requires HW support", Abort);
+
QTRY_COMPARE(surface.m_totalFrames, 1);
- QCOMPARE(surface.m_frameList.last().width(), 540);
- QCOMPARE(surface.videoSize().width(), 540);
- QCOMPARE(surface.m_frameList.last().height(), 320);
- QCOMPARE(surface.videoSize().height(), 320);
+
+ // Assert
+ QCOMPARE(surface.m_frameList.last().width(), width);
+ QCOMPARE(surface.videoSize().width(), width);
+ QCOMPARE(surface.m_frameList.last().height(), height);
+ QCOMPARE(surface.videoSize().height(), height);
}
void tst_QMediaPlayerBackend::position()
{
- if (localVideoFile.isEmpty())
- QSKIP("No supported video file");
+ CHECK_SELECTED_URL(m_localVideoFile);
TestVideoSink surface(true);
QMediaPlayer player;
player.setVideoOutput(&surface);
-#ifdef Q_OS_ANDROID
- QEXPECT_FAIL("", "On Android isSeekable() is always set to true due to QTBUG-96952", Continue);
-#endif
QVERIFY(!player.isSeekable());
- player.setSource(localVideoFile);
+ player.setSource(*m_localVideoFile);
QTRY_VERIFY(player.isSeekable());
player.play();
@@ -1276,6 +2447,648 @@ void tst_QMediaPlayerBackend::position()
QVERIFY(player.position() < 550);
}
+void tst_QMediaPlayerBackend::durationDetectionIssues_data()
+{
+ QTest::addColumn<QString>("mediaFile");
+ QTest::addColumn<qint64>("expectedDuration");
+ QTest::addColumn<int>("expectedVideoTrackCount");
+ QTest::addColumn<qint64>("expectedVideoTrackDuration");
+ QTest::addColumn<int>("expectedAudioTrackCount");
+ QTest::addColumn<QVariant>("expectedAudioTrackDuration");
+
+ // clang-format off
+
+ QTest::newRow("stream-duration-in-metadata")
+ << QString{ "qrc:/testdata/duration_issues.webm" }
+ << 400ll // Total media duration
+ << 1 // Number of video tracks in file
+ << 400ll // Video stream duration
+ << 0 // Number of audio tracks in file
+ << QVariant{}; // Audio stream duration (unused)
+
+ QTest::newRow("no-stream-duration-in-metadata")
+ << QString{ "qrc:/testdata/nokia-tune.mkv" }
+ << 7531ll // Total media duration
+ << 0 // Number of video tracks in file
+ << 0ll // Video stream duration (unused)
+ << 1 // Number of audio tracks in file
+ << QVariant{}; // Audio stream duration (not present on file)
+
+ // clang-format on
+}
+
+void tst_QMediaPlayerBackend::durationDetectionIssues()
+{
+ QFETCH(QString, mediaFile);
+ QFETCH(qint64, expectedDuration);
+ QFETCH(int, expectedVideoTrackCount);
+ QFETCH(qint64, expectedVideoTrackDuration);
+ QFETCH(int, expectedAudioTrackCount);
+ QFETCH(QVariant, expectedAudioTrackDuration);
+
+ // ffmpeg detects stream an incorrect stream duration, so we take
+ // the correct duration from the metadata
+
+ TestVideoSink surface(false);
+ QAudioOutput output;
+ QMediaPlayer player;
+
+ QSignalSpy durationSpy(&player, &QMediaPlayer::durationChanged);
+
+ player.setVideoOutput(&surface);
+ player.setAudioOutput(&output);
+ player.setSource(mediaFile);
+
+ QTRY_COMPARE_EQ(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ // Duration event received
+ QCOMPARE(durationSpy.size(), 1);
+ QCOMPARE(durationSpy.front().front(), expectedDuration);
+
+ // Duration property
+ QCOMPARE(player.duration(), expectedDuration);
+ QCOMPARE(player.metaData().value(QMediaMetaData::Duration), expectedDuration);
+
+ // Track duration properties
+ const auto videoTracks = player.videoTracks();
+ QCOMPARE(videoTracks.size(), expectedVideoTrackCount);
+
+ if (expectedVideoTrackCount != 0)
+ QCOMPARE(videoTracks.front().value(QMediaMetaData::Duration), expectedVideoTrackDuration);
+
+ const auto audioTracks = player.audioTracks();
+ QCOMPARE(audioTracks.size(), expectedAudioTrackCount);
+
+ if (expectedAudioTrackCount != 0)
+ QCOMPARE(audioTracks.front().value(QMediaMetaData::Duration), expectedAudioTrackDuration);
+}
+
+struct LoopIteration {
+ qint64 startPos;
+ qint64 endPos;
+ qint64 posCount;
+};
+// Creates a vector of LoopIterations, containing start- and end position
+// and the number of position changes per video loop iteration.
+static std::vector<LoopIteration> loopIterations(const QSignalSpy &positionSpy)
+{
+ std::vector<LoopIteration> result;
+ // Loops through all positions emitted by QMediaPlayer::positionChanged
+ for (auto &params : positionSpy) {
+ const auto pos = params.front().value<qint64>();
+
+ // Adds new LoopIteration struct to result if position is lower than previous position
+ if (result.empty() || pos < result.back().endPos) {
+ result.push_back(LoopIteration{pos, pos, 1});
+ }
+ // Updates end position of the current LoopIteration if position is higher than previous position
+ else {
+ result.back().posCount++;
+ result.back().endPos = pos;
+ }
+ }
+ return result;
+}
+
+void tst_QMediaPlayerBackend::finiteLoops()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("The test accidently gets crashed on macOS CI, not reproduced locally. To be "
+ "investigated: QTBUG-111744");
+#endif
+
+ QCOMPARE(m_fixture->player.loops(), 1);
+ m_fixture->player.setLoops(3);
+ QCOMPARE(m_fixture->player.loops(), 3);
+
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+ m_fixture->player.setPlaybackRate(5);
+ QCOMPARE(m_fixture->player.loops(), 3);
+
+ m_fixture->player.play();
+ m_fixture->surface.waitForFrame();
+
+ // check pause doesn't affect looping
+ {
+ QTest::qWait(static_cast<int>(m_fixture->player.duration() * 3
+ * 0.6 /*relative pos*/ / m_fixture->player.playbackRate()));
+ m_fixture->player.pause();
+ m_fixture->player.play();
+ }
+
+ QTRY_COMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+
+ // Check for expected number of loop iterations and startPos, endPos and posCount per iteration
+ std::vector<LoopIteration> iterations = loopIterations(m_fixture->positionChanged);
+ QCOMPARE(iterations.size(), 3u);
+ QCOMPARE_GT(iterations[0].startPos, 0);
+ QCOMPARE(iterations[0].endPos, m_fixture->player.duration());
+ QCOMPARE_GT(iterations[0].posCount, 10);
+ QCOMPARE(iterations[1].startPos, 0);
+ QCOMPARE(iterations[1].endPos, m_fixture->player.duration());
+ QCOMPARE_GT(iterations[1].posCount, 10);
+ QCOMPARE(iterations[2].startPos, 0);
+ QCOMPARE(iterations[2].endPos, m_fixture->player.duration());
+ QCOMPARE_GT(iterations[2].posCount, 10);
+
+ QCOMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+
+ // Check that loop counter is reset when playback is restarted.
+ {
+ m_fixture->positionChanged.clear();
+ m_fixture->player.play();
+ m_fixture->player.setPlaybackRate(10);
+ m_fixture->surface.waitForFrame();
+
+ QTRY_COMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QCOMPARE(loopIterations(m_fixture->positionChanged).size(), 3u);
+ QCOMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ }
+}
+
+void tst_QMediaPlayerBackend::infiniteLoops()
+{
+ CHECK_SELECTED_URL(m_localVideoFile2);
+
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("The test accidently gets crashed on macOS CI, not reproduced locally. To be "
+ "investigated: QTBUG-111744");
+#endif
+
+ m_fixture->player.setLoops(QMediaPlayer::Infinite);
+ QCOMPARE(m_fixture->player.loops(), QMediaPlayer::Infinite);
+
+ // select some small file
+ m_fixture->player.setSource(*m_localVideoFile2);
+ m_fixture->player.setPlaybackRate(20);
+
+ m_fixture->player.play();
+ m_fixture->surface.waitForFrame();
+
+ for (int i = 0; i < 2; ++i) {
+ m_fixture->positionChanged.clear();
+
+ QTest::qWait(
+ std::max(static_cast<int>(m_fixture->player.duration()
+ / m_fixture->player.playbackRate() * 4),
+ 300 /*ensure some minimum waiting time to reduce threading flakiness*/));
+ QVERIFY(m_fixture->player.mediaStatus() == QMediaPlayer::BufferingMedia
+ || m_fixture->player.mediaStatus() == QMediaPlayer::BufferedMedia);
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::PlayingState);
+
+ const auto iterations = loopIterations(m_fixture->positionChanged);
+ QVERIFY(!iterations.empty());
+ QCOMPARE(iterations.front().endPos, m_fixture->player.duration());
+ }
+
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+
+ m_fixture->player.stop(); // QMediaPlayer::stop stops whether or not looping is infinite
+ QCOMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+
+ QCOMPARE(m_fixture->mediaStatusChanged,
+ SignalList({ { QMediaPlayer::LoadingMedia },
+ { QMediaPlayer::LoadedMedia },
+ { QMediaPlayer::BufferingMedia },
+ { QMediaPlayer::BufferedMedia },
+ { QMediaPlayer::LoadedMedia } }));
+}
+
+void tst_QMediaPlayerBackend::seekOnLoops()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("The test accidently gets crashed on macOS CI, not reproduced locally. To be "
+ "investigated: QTBUG-111744");
+#endif
+
+ m_fixture->player.setLoops(3);
+ m_fixture->player.setPlaybackRate(2);
+
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+
+ m_fixture->player.play();
+ m_fixture->surface.waitForFrame();
+
+ // seek in the 1st loop
+ m_fixture->player.setPosition(m_fixture->player.duration() * 4 / 5);
+
+ // wait for the 2nd loop and seek
+ m_fixture->surface.waitForFrame();
+ QTRY_VERIFY(m_fixture->player.position() < m_fixture->player.duration() / 2);
+ m_fixture->player.setPosition(m_fixture->player.duration() * 8 / 9);
+
+ // wait for the 3rd loop and seek
+ m_fixture->surface.waitForFrame();
+ QTRY_VERIFY(m_fixture->player.position() < m_fixture->player.duration() / 2);
+ m_fixture->player.setPosition(m_fixture->player.duration() * 4 / 5);
+
+ QTRY_COMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+
+ auto iterations = loopIterations(m_fixture->positionChanged);
+
+ QCOMPARE(iterations.size(), 3u);
+ QCOMPARE_GT(iterations[0].startPos, 0);
+ QCOMPARE(iterations[0].endPos, m_fixture->player.duration());
+ QCOMPARE_GT(iterations[0].posCount, 2);
+ QCOMPARE(iterations[1].startPos, 0);
+ QCOMPARE(iterations[1].endPos, m_fixture->player.duration());
+ QCOMPARE_GT(iterations[1].posCount, 2);
+ QCOMPARE(iterations[2].startPos, 0);
+ QCOMPARE(iterations[2].endPos, m_fixture->player.duration());
+ QCOMPARE_GT(iterations[2].posCount, 2);
+
+ QCOMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+}
+
+void tst_QMediaPlayerBackend::changeLoopsOnTheFly()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("The test accidently gets crashed on macOS CI, not reproduced locally. To be "
+ "investigated: QTBUG-111744");
+#endif
+
+ m_fixture->player.setLoops(4);
+ m_fixture->player.setPlaybackRate(5);
+
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+
+ m_fixture->player.play();
+ m_fixture->surface.waitForFrame();
+
+ m_fixture->player.setPosition(m_fixture->player.duration() * 4 / 5);
+
+ // wait for the 2nd loop
+ m_fixture->surface.waitForFrame();
+ QTRY_VERIFY(m_fixture->player.position() < m_fixture->player.duration() / 2);
+ m_fixture->player.setPosition(m_fixture->player.duration() * 8 / 9);
+
+ m_fixture->player.setLoops(1);
+
+ QTRY_COMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QCOMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+
+ auto iterations = loopIterations(m_fixture->positionChanged);
+ QCOMPARE(iterations.size(), 2u);
+
+ QCOMPARE(iterations[1].startPos, 0);
+ QCOMPARE(iterations[1].endPos, m_fixture->player.duration());
+ QCOMPARE_GT(iterations[1].posCount, 2);
+}
+
+void tst_QMediaPlayerBackend::seekAfterLoopReset()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("The test accidently gets crashed on macOS CI, not reproduced locally. To be "
+ "investigated: QTBUG-111744");
+#endif
+
+ m_fixture->surface.setStoreFrames(false);
+
+ m_fixture->player.setLoops(QMediaPlayer::Infinite);
+ m_fixture->player.setPlaybackRate(2);
+
+ m_fixture->player.setSource(*m_localVideoFile3ColorsWithSound);
+
+ m_fixture->player.play();
+ m_fixture->surface.waitForFrame();
+
+ // seek in the 1st loop
+ m_fixture->player.setPosition(m_fixture->player.duration() * 4 / 5);
+
+ // wait for the 2nd loop
+ m_fixture->surface.waitForFrame();
+ QTRY_VERIFY(m_fixture->player.position() < m_fixture->player.duration() / 2);
+
+ // reset loops and seek
+ m_fixture->player.setLoops(1);
+ m_fixture->player.setPosition(m_fixture->player.duration() * 8 / 9);
+
+ QTRY_COMPARE(m_fixture->player.playbackState(), QMediaPlayer::StoppedState);
+ QCOMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia);
+}
+
+void tst_QMediaPlayerBackend::changeVideoOutputNoFramesLost()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+ QVideoSink sinks[4];
+ std::atomic_int framesCount[4] = {
+ 0,
+ };
+ for (int i = 0; i < 4; ++i)
+ setVideoSinkAsyncFramesCounter(sinks[i], framesCount[i]);
+
+ QMediaPlayer player;
+
+ player.setPlaybackRate(10);
+
+ player.setVideoOutput(&sinks[0]);
+ player.setSource(*m_localVideoFile3ColorsWithSound);
+ player.play();
+ QTRY_VERIFY(!player.isPlaying());
+
+ player.setPlaybackRate(4);
+ player.setVideoOutput(&sinks[1]);
+ player.play();
+
+ QTRY_VERIFY(framesCount[1] >= framesCount[0] / 4);
+ player.setVideoOutput(&sinks[2]);
+ const int savedFrameNumber1 = framesCount[1];
+
+ QTRY_VERIFY(framesCount[2] >= (framesCount[0] - savedFrameNumber1) / 2);
+ player.setVideoOutput(&sinks[3]);
+ const int savedFrameNumber2 = framesCount[2];
+
+ QTRY_VERIFY(!player.isPlaying());
+
+ // check if no frames sent to old sinks
+ QCOMPARE(framesCount[1], savedFrameNumber1);
+ QCOMPARE(framesCount[2], savedFrameNumber2);
+
+ // no frames lost
+ QCOMPARE(framesCount[1] + framesCount[2] + framesCount[3], framesCount[0]);
+}
+
+void tst_QMediaPlayerBackend::cleanSinkAndNoMoreFramesAfterStop()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+ QVideoSink sink;
+ std::atomic_int framesCount = 0;
+ setVideoSinkAsyncFramesCounter(sink, framesCount);
+ QMediaPlayer player;
+
+ player.setPlaybackRate(10);
+ player.setVideoOutput(&sink);
+
+ player.setSource(*m_localVideoFile3ColorsWithSound);
+
+ // Run a few time to have more chances to detect race conditions
+ for (int i = 0; i < 8; ++i) {
+ player.play();
+ QTRY_VERIFY(framesCount > 0);
+ QVERIFY(sink.videoFrame().isValid());
+
+ player.stop();
+
+ if (isGStreamerPlatform())
+ // QTBUG-124005: stop() is asynchronous in gstreamer
+ QTRY_VERIFY(!sink.videoFrame().isValid());
+ else
+ QVERIFY(!sink.videoFrame().isValid());
+
+ QCOMPARE_NE(framesCount, 0);
+ framesCount = 0;
+
+ QTest::qWait(30);
+
+ // check if nothing changed after short waiting
+ QCOMPARE(framesCount, 0);
+ }
+}
+
+void tst_QMediaPlayerBackend::lazyLoadVideo()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(QUrl("qrc:/LazyLoad.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QQuickItem *rootItem = qobject_cast<QQuickItem *>(root.get());
+ QVERIFY(rootItem);
+
+ QQuickView view;
+ rootItem->setParentItem(view.contentItem());
+ view.resize(600, 800);
+ view.show();
+
+ QQuickLoader *loader = qobject_cast<QQuickLoader *>(rootItem->findChild<QQuickItem *>("loader"));
+ QVERIFY(loader);
+ QCOMPARE(QQmlProperty::read(loader, "active").toBool(), false);
+ loader->setProperty("active", true);
+ QCOMPARE(QQmlProperty::read(loader, "active").toBool(), true);
+
+ QQuickItem *videoPlayer = qobject_cast<QQuickItem *>(loader->findChild<QQuickItem *>("videoPlayer"));
+ QVERIFY(videoPlayer);
+
+ QTRY_COMPARE_EQ(QQmlProperty::read(videoPlayer, "playbackState").value<QMediaPlayer::PlaybackState>(), QMediaPlayer::PlayingState);
+ QCOMPARE(QQmlProperty::read(videoPlayer, "error").value<QMediaPlayer::Error>(), QMediaPlayer::NoError);
+
+ QVideoSink *videoSink = QQmlProperty::read(videoPlayer, "videoSink").value<QVideoSink *>();
+ QVERIFY(videoSink);
+
+ QSignalSpy spy(videoSink, &QVideoSink::videoFrameChanged);
+ QVERIFY(spy.wait());
+
+ QVideoFrame frame = spy.at(0).at(0).value<QVideoFrame>();
+ QVERIFY(frame.isValid());
+}
+
+void tst_QMediaPlayerBackend::videoSinkSignals()
+{
+ std::atomic<int> videoFrameCounter = 0;
+ std::atomic<int> videoSizeCounter = 0;
+
+ // TODO: come up with custom frames source,
+ // create the test target tst_QVideoSinkBackend,
+ // and move the test there
+
+ CHECK_SELECTED_URL(m_localVideoFile2);
+
+ QVideoSink sink;
+ QMediaPlayer player;
+ player.setVideoSink(&sink);
+
+ player.setSource(*m_localVideoFile2);
+
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::MediaStatus::LoadedMedia);
+
+ sink.platformVideoSink()->setNativeSize({}); // reset size to be able to check the size update
+
+ connect(&sink, &QVideoSink::videoFrameChanged, this, [&](const QVideoFrame &frame) {
+ QCOMPARE(sink.videoFrame(), frame);
+ QCOMPARE(sink.videoSize(), frame.size());
+ ++videoFrameCounter;
+ }, Qt::DirectConnection);
+
+ connect(&sink, &QVideoSink::videoSizeChanged, this, [&]() {
+ QCOMPARE(sink.videoSize(), sink.videoFrame().size());
+ if (sink.videoSize().isValid()) // filter end frame
+ ++videoSizeCounter;
+ }, Qt::DirectConnection);
+
+ player.play();
+
+ QTRY_COMPARE_GE(videoFrameCounter, 2);
+ QCOMPARE(videoSizeCounter, 1);
+}
+
+void tst_QMediaPlayerBackend::nonAsciiFileName()
+{
+ CHECK_SELECTED_URL(m_localWavFile);
+
+ auto temporaryFile =
+ copyResourceToTemporaryFile(":/testdata/test.wav", "äöüØøÆ中文.XXXXXX.wav");
+ QVERIFY(temporaryFile);
+
+ m_fixture->player.setSource(temporaryFile->fileName());
+ m_fixture->player.play();
+
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::BufferedMedia);
+
+ QCOMPARE(m_fixture->errorOccurred.size(), 0);
+}
+
+void tst_QMediaPlayerBackend::setMedia_setsVideoSinkSize_beforePlaying()
+{
+ CHECK_SELECTED_URL(m_localVideoFile3ColorsWithSound);
+
+ QVideoSink sink1;
+ QVideoSink sink2;
+ QMediaPlayer player;
+
+ QSignalSpy spy1(&sink1, &QVideoSink::videoSizeChanged);
+ QSignalSpy spy2(&sink2, &QVideoSink::videoSizeChanged);
+
+ player.setVideoOutput(&sink1);
+ QCOMPARE(sink1.videoSize(), QSize());
+
+ player.setSource(*m_localVideoFile3ColorsWithSound);
+
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::MediaStatus::LoadedMedia);
+
+ QCOMPARE(sink1.videoSize(), QSize(684, 384));
+
+ player.setVideoOutput(&sink2);
+ QCOMPARE(sink2.videoSize(), QSize(684, 384));
+
+ QCOMPARE(spy1.size(), 1);
+ QCOMPARE(spy2.size(), 1);
+}
+
+#if QT_CONFIG(process)
+std::unique_ptr<QProcess> tst_QMediaPlayerBackend::createRtspStreamProcess(QString fileName,
+ QString outputUrl)
+{
+ Q_ASSERT(!m_vlcCommand.isEmpty());
+
+ auto process = std::make_unique<QProcess>();
+#if defined(Q_OS_WINDOWS)
+ fileName.replace('/', '\\');
+#endif
+
+ // clang-format off
+ QStringList vlcParams =
+ {
+ "-vvv", fileName,
+ "--sout", QLatin1String("#rtp{sdp=%1}").arg(outputUrl),
+ "--intf", "dummy"
+ };
+ // clang-format on
+
+ process->start(m_vlcCommand, vlcParams);
+ if (!process->waitForStarted())
+ return nullptr;
+
+ // rtsp stream might be with started some delay after the vlc process starts.
+ // Ideally, we should wait for open connections, it requires some extra work + QNetwork dependency.
+ QTest::qWait(500);
+
+ return process;
+}
+#endif //QT_CONFIG(process)
+
+void tst_QMediaPlayerBackend::play_playsRotatedVideoOutput_whenVideoFileHasOrientationMetadata_data()
+{
+ QTest::addColumn<MaybeUrl>("fileURL");
+ QTest::addColumn<QRgb>("expectedColor");
+ QTest::addColumn<QtVideo::Rotation>("expectedRotationAngle");
+ QTest::addColumn<QSize>("videoSize");
+
+ // clang-format off
+ QTest::addRow("without rotation") << m_colorMatrixVideo
+ << QRgb(0xff0000)
+ << QtVideo::Rotation::None
+ << QSize(960, 540);
+
+ QTest::addRow("90 deg clockwise") << m_colorMatrix90degClockwiseVideo
+ << QRgb(0x0000FF)
+ << QtVideo::Rotation::Clockwise90
+ << QSize(540, 960);
+
+ QTest::addRow("180 deg clockwise") << m_colorMatrix180degClockwiseVideo
+ << QRgb(0xFFFF00)
+ << QtVideo::Rotation::Clockwise180
+ << QSize(960, 540);
+
+ QTest::addRow("270 deg clockwise") << m_colorMatrix270degClockwiseVideo
+ << QRgb(0x00FF00)
+ << QtVideo::Rotation::Clockwise270
+ << QSize(540, 960);
+ // clang-format on
+}
+
+void tst_QMediaPlayerBackend::play_playsRotatedVideoOutput_whenVideoFileHasOrientationMetadata()
+{
+ // This test uses 4 video files with a 2x2 color matrix consisting of
+ // red (upper left), blue (lower left), yellow (lower right) and green (upper right).
+ // The files are identical, except that three of them contain
+ // orientation (rotation) metadata specifying that they should be
+ // viewed with a 90, 180 and 270 degree clockwise rotation respectively.
+
+ // Fetch path and expected color of upper left area of each file
+ QFETCH(const MaybeUrl, fileURL);
+ QFETCH(const QRgb, expectedColor);
+ QFETCH(const QtVideo::Rotation, expectedRotationAngle);
+ QFETCH(const QSize, videoSize);
+
+ CHECK_SELECTED_URL(fileURL);
+
+ // Load video file
+ m_fixture->player.setSource(*fileURL);
+ QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ // Compare videoSize of the output video sink with the expected value before starting playing
+ QCOMPARE(m_fixture->surface.videoSize(), videoSize);
+
+ // Compare orientation metadata of QMediaPlayer with expected value
+ const auto metaData = m_fixture->player.metaData();
+ const auto playerOrientation = metaData.value(QMediaMetaData::Orientation).value<QtVideo::Rotation>();
+ QCOMPARE(playerOrientation, expectedRotationAngle);
+
+ // Compare orientation metadata of active video stream with expected value
+ const int activeVideoTrack = m_fixture->player.activeVideoTrack();
+ const auto videoTrackMetaData = m_fixture->player.videoTracks().at(activeVideoTrack);
+ const auto videoTrackOrientation = videoTrackMetaData.value(QMediaMetaData::Orientation).value<QtVideo::Rotation>();
+ QCOMPARE(videoTrackOrientation, expectedRotationAngle);
+
+ // Play video file, sample upper left area, compare with expected color
+ m_fixture->player.play();
+ QTRY_COMPARE(m_fixture->player.playbackState(), QMediaPlayer::PlayingState);
+ QVideoFrame videoFrame = m_fixture->surface.waitForFrame();
+ QVERIFY(videoFrame.isValid());
+ QCOMPARE(videoFrame.rotation(), expectedRotationAngle);
+ QImage image = videoFrame.toImage();
+ QVERIFY(!image.isNull());
+ QRgb upperLeftColor = image.pixel(5, 5);
+ QCOMPARE_LT(colorDifference(upperLeftColor, expectedColor), 0.004);
+
+ // Compare videoSize of the output video sink with the expected value after getting a frame
+ QCOMPARE(m_fixture->surface.videoSize(), videoSize);
+}
+
QTEST_MAIN(tst_QMediaPlayerBackend)
#include "tst_qmediaplayerbackend.moc"
diff --git a/tests/auto/integration/qml/CMakeLists.txt b/tests/auto/integration/qml/CMakeLists.txt
index 35837cbad..1b7a1f686 100644
--- a/tests/auto/integration/qml/CMakeLists.txt
+++ b/tests/auto/integration/qml/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qml.pro.
#####################################################################
@@ -9,7 +12,7 @@ qt_internal_add_test(tst_qml
QMLTEST
SOURCES
tst_qml.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
)
diff --git a/tests/auto/integration/qml/soundeffect/tst_soundeffect.qml b/tests/auto/integration/qml/soundeffect/tst_soundeffect.qml
index 7ebbe2e73..0b5867bce 100644
--- a/tests/auto/integration/qml/soundeffect/tst_soundeffect.qml
+++ b/tests/auto/integration/qml/soundeffect/tst_soundeffect.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtMultimedia
diff --git a/tests/auto/integration/qml/tst_qml.cpp b/tests/auto/integration/qml/tst_qml.cpp
index 668285a31..2bdca1037 100644
--- a/tests/auto/integration/qml/tst_qml.cpp
+++ b/tests/auto/integration/qml/tst_qml.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(qml)
diff --git a/tests/auto/integration/qquickvideooutput/CMakeLists.txt b/tests/auto/integration/qquickvideooutput/CMakeLists.txt
index 145dd8875..909416140 100644
--- a/tests/auto/integration/qquickvideooutput/CMakeLists.txt
+++ b/tests/auto/integration/qquickvideooutput/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qdeclarativevideooutput.pro.
#####################################################################
@@ -9,7 +12,7 @@ qt_internal_add_test(tst_qquickvideooutput
tst_qquickvideooutput.cpp
INCLUDE_DIRECTORIES
../../../../src/imports/multimedia
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
Qt::MultimediaQuickPrivate
diff --git a/tests/auto/integration/qquickvideooutput/tst_qquickvideooutput.cpp b/tests/auto/integration/qquickvideooutput/tst_qquickvideooutput.cpp
index 519fc6944..a7b6f286c 100644
--- a/tests/auto/integration/qquickvideooutput/tst_qquickvideooutput.cpp
+++ b/tests/auto/integration/qquickvideooutput/tst_qquickvideooutput.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//TESTED_COMPONENT=plugins/declarative/multimedia
@@ -124,19 +99,19 @@ void tst_QQuickVideoOutput::fillMode()
// Default is preserveaspectfit
QCOMPARE(videoOutput->property("fillMode").value<QQuickVideoOutput::FillMode>(), QQuickVideoOutput::PreserveAspectFit);
- QCOMPARE(propSpy.count(), 0);
+ QCOMPARE(propSpy.size(), 0);
videoOutput->setProperty("fillMode", QVariant(int(QQuickVideoOutput::PreserveAspectCrop)));
QCOMPARE(videoOutput->property("fillMode").value<QQuickVideoOutput::FillMode>(), QQuickVideoOutput::PreserveAspectCrop);
- QCOMPARE(propSpy.count(), 1);
+ QCOMPARE(propSpy.size(), 1);
videoOutput->setProperty("fillMode", QVariant(int(QQuickVideoOutput::Stretch)));
QCOMPARE(videoOutput->property("fillMode").value<QQuickVideoOutput::FillMode>(), QQuickVideoOutput::Stretch);
- QCOMPARE(propSpy.count(), 2);
+ QCOMPARE(propSpy.size(), 2);
videoOutput->setProperty("fillMode", QVariant(int(QQuickVideoOutput::Stretch)));
QCOMPARE(videoOutput->property("fillMode").value<QQuickVideoOutput::FillMode>(), QQuickVideoOutput::Stretch);
- QCOMPARE(propSpy.count(), 2);
+ QCOMPARE(propSpy.size(), 2);
delete videoOutput;
}
@@ -153,43 +128,43 @@ void tst_QQuickVideoOutput::orientation()
// Default orientation is 0
QCOMPARE(videoOutput->property("orientation").toInt(), 0);
- QCOMPARE(propSpy.count(), 0);
+ QCOMPARE(propSpy.size(), 0);
videoOutput->setProperty("orientation", QVariant(90));
QCOMPARE(videoOutput->property("orientation").toInt(), 90);
- QCOMPARE(propSpy.count(), 1);
+ QCOMPARE(propSpy.size(), 1);
videoOutput->setProperty("orientation", QVariant(180));
QCOMPARE(videoOutput->property("orientation").toInt(), 180);
- QCOMPARE(propSpy.count(), 2);
+ QCOMPARE(propSpy.size(), 2);
videoOutput->setProperty("orientation", QVariant(270));
QCOMPARE(videoOutput->property("orientation").toInt(), 270);
- QCOMPARE(propSpy.count(), 3);
+ QCOMPARE(propSpy.size(), 3);
videoOutput->setProperty("orientation", QVariant(360));
QCOMPARE(videoOutput->property("orientation").toInt(), 360);
- QCOMPARE(propSpy.count(), 4);
+ QCOMPARE(propSpy.size(), 4);
// More than 360 should be fine
videoOutput->setProperty("orientation", QVariant(540));
QCOMPARE(videoOutput->property("orientation").toInt(), 540);
- QCOMPARE(propSpy.count(), 5);
+ QCOMPARE(propSpy.size(), 5);
// Negative should be fine
videoOutput->setProperty("orientation", QVariant(-180));
QCOMPARE(videoOutput->property("orientation").toInt(), -180);
- QCOMPARE(propSpy.count(), 6);
+ QCOMPARE(propSpy.size(), 6);
// Same value should not reemit
videoOutput->setProperty("orientation", QVariant(-180));
QCOMPARE(videoOutput->property("orientation").toInt(), -180);
- QCOMPARE(propSpy.count(), 6);
+ QCOMPARE(propSpy.size(), 6);
// Non multiples of 90 should not work
videoOutput->setProperty("orientation", QVariant(-1));
QCOMPARE(videoOutput->property("orientation").toInt(), -180);
- QCOMPARE(propSpy.count(), 6);
+ QCOMPARE(propSpy.size(), 6);
delete videoOutput;
}
@@ -317,11 +292,11 @@ void tst_QQuickVideoOutput::sourceRect()
presentDummyFrame(holder.videoSink(), QSize(200,100));
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
- QCOMPARE(propSpy.count(), 1);
+ QCOMPARE(propSpy.size(), 1);
// Another frame shouldn't cause a source rect change
presentDummyFrame(holder.videoSink(), QSize(200,100));
- QCOMPARE(propSpy.count(), 1);
+ QCOMPARE(propSpy.size(), 1);
QCOMPARE(videoOutput->property("sourceRect").toRectF(), QRectF(0, 0, 200, 100));
// Changing orientation and stretch modes should not affect this
diff --git a/tests/auto/integration/qquickvideooutput_window/CMakeLists.txt b/tests/auto/integration/qquickvideooutput_window/CMakeLists.txt
index 8d9bb078d..e7b624c70 100644
--- a/tests/auto/integration/qquickvideooutput_window/CMakeLists.txt
+++ b/tests/auto/integration/qquickvideooutput_window/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qdeclarativevideooutput_window.pro.
#####################################################################
@@ -9,7 +12,7 @@ qt_internal_add_test(tst_qquickvideooutput_window
tst_qquickvideooutput_window.cpp
INCLUDE_DIRECTORIES
../../../../src/imports/multimedia
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
Qt::MultimediaQuickPrivate
diff --git a/tests/auto/integration/qquickvideooutput_window/tst_qquickvideooutput_window.cpp b/tests/auto/integration/qquickvideooutput_window/tst_qquickvideooutput_window.cpp
index 5942010e6..68f1771f9 100644
--- a/tests/auto/integration/qquickvideooutput_window/tst_qquickvideooutput_window.cpp
+++ b/tests/auto/integration/qquickvideooutput_window/tst_qquickvideooutput_window.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Research In Motion
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Research In Motion
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
//TESTED_COMPONENT=plugins/declarative/multimedia
diff --git a/tests/auto/integration/qscreencapturebackend/BLACKLIST b/tests/auto/integration/qscreencapturebackend/BLACKLIST
new file mode 100644
index 000000000..bd6bb0e01
--- /dev/null
+++ b/tests/auto/integration/qscreencapturebackend/BLACKLIST
@@ -0,0 +1,11 @@
+macos ci
+windows ci
+
+#QTBUG-122577
+opensuse-15.5 ci
+
+#QTBUG-112827 on Android
+#QTBUG-111190, v4l2m2m issues
+[capture_capturesToFile_whenConnectedToMediaRecorder]
+linux ci
+android ci
diff --git a/tests/auto/integration/qscreencapturebackend/CMakeLists.txt b/tests/auto/integration/qscreencapturebackend/CMakeLists.txt
new file mode 100644
index 000000000..9b40642a0
--- /dev/null
+++ b/tests/auto/integration/qscreencapturebackend/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qscreencapturebackend Test:
+#####################################################################
+
+qt_internal_add_test(tst_qscreencapturebackend
+ SOURCES
+ tst_qscreencapturebackend.cpp
+ LIBRARIES
+ Qt::Multimedia
+ Qt::Gui
+ Qt::Widgets
+)
+
+
diff --git a/tests/auto/integration/qscreencapturebackend/tst_qscreencapturebackend.cpp b/tests/auto/integration/qscreencapturebackend/tst_qscreencapturebackend.cpp
new file mode 100644
index 000000000..522d9bcdd
--- /dev/null
+++ b/tests/auto/integration/qscreencapturebackend/tst_qscreencapturebackend.cpp
@@ -0,0 +1,505 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+
+#include <qvideosink.h>
+#include <qvideoframe.h>
+#include <qmediacapturesession.h>
+#include <qpainter.h>
+#include <qscreencapture.h>
+#include <qsignalspy.h>
+#include <qmediarecorder.h>
+#include <qmediaplayer.h>
+
+#include <vector>
+
+QT_USE_NAMESPACE
+
+/*
+ This is the backend conformance test.
+
+ Since it relies on platform media framework it may be less stable.
+ Note, some of screen capture backend is not implemented or has bugs.
+ That's why some of the tests could get failed.
+ TODO: fix and platform implementations and make it stable.
+*/
+
+class QTestWidget : public QWidget
+{
+public:
+ QTestWidget(QColor firstColor, QColor secondColor)
+ : m_firstColor(firstColor), m_secondColor(secondColor)
+ {
+ }
+
+ static std::unique_ptr<QTestWidget> createAndShow(Qt::WindowFlags flags, const QRect &geometry,
+ QScreen *screen = nullptr,
+ QColor firstColor = QColor(0xFF, 0, 0),
+ QColor secondColor = QColor(0, 0, 0xFF))
+ {
+ auto widget = std::make_unique<QTestWidget>(firstColor, secondColor);
+
+ widget->setWindowTitle("Test QScreenCapture");
+ widget->setScreen(screen ? screen : QApplication::primaryScreen());
+ widget->setWindowFlags(flags);
+ widget->setGeometry(geometry);
+ widget->show();
+
+ return widget;
+ }
+
+ void setColors(QColor firstColor, QColor secondColor)
+ {
+ m_firstColor = firstColor;
+ m_secondColor = secondColor;
+ this->repaint();
+ }
+
+protected:
+ void paintEvent(QPaintEvent * /*event*/) override
+ {
+ QPainter p(this);
+ p.setPen(Qt::NoPen);
+
+ p.setBrush(m_firstColor);
+ auto rect = this->rect();
+ p.drawRect(rect);
+
+ if (m_firstColor != m_secondColor) {
+ rect.adjust(40, 50, -60, -70);
+ p.setBrush(m_secondColor);
+ p.drawRect(rect);
+ }
+ }
+
+private:
+ QColor m_firstColor;
+ QColor m_secondColor;
+};
+
+class TestVideoSink : public QVideoSink
+{
+ Q_OBJECT
+public:
+ TestVideoSink()
+ {
+ connect(this, &QVideoSink::videoFrameChanged, this, &TestVideoSink::videoFrameChangedSync);
+ }
+
+ void setStoreImagesEnabled(bool storeImages = true) {
+ if (storeImages)
+ connect(this, &QVideoSink::videoFrameChanged, this, &TestVideoSink::storeImage, Qt::UniqueConnection);
+ else
+ disconnect(this, &QVideoSink::videoFrameChanged, this, &TestVideoSink::storeImage);
+ }
+
+ const std::vector<QImage> &images() const { return m_images; }
+
+ QVideoFrame waitForFrame()
+ {
+ QSignalSpy spy(this, &TestVideoSink::videoFrameChangedSync);
+ return spy.wait() ? spy.at(0).at(0).value<QVideoFrame>() : QVideoFrame{};
+ }
+
+signals:
+ void videoFrameChangedSync(QVideoFrame frame);
+
+private:
+ void storeImage(const QVideoFrame &frame) {
+ auto image = frame.toImage();
+ image.detach();
+ m_images.push_back(std::move(image));
+ }
+
+private:
+ std::vector<QImage> m_images;
+};
+
+class tst_QScreenCaptureBackend : public QObject
+{
+ Q_OBJECT
+
+ void removeWhileCapture(std::function<void(QScreenCapture &)> scModifier,
+ std::function<void()> deleter);
+
+ void capture(QTestWidget &widget, const QPoint &drawingOffset, const QSize &expectedSize,
+ std::function<void(QScreenCapture &)> scModifier);
+
+private slots:
+ void initTestCase();
+ void setActive_startsAndStopsCapture();
+ void setScreen_selectsScreen_whenCalledWithWidgetsScreen();
+ void constructor_selectsPrimaryScreenAsDefault();
+ void setScreen_selectsSecondaryScreen_whenCalledWithSecondaryScreen();
+
+ void capture_capturesToFile_whenConnectedToMediaRecorder();
+ void removeScreenWhileCapture(); // Keep the test last defined. TODO: find a way to restore
+ // application screens.
+};
+
+void tst_QScreenCaptureBackend::setActive_startsAndStopsCapture()
+{
+#ifdef Q_OS_ANDROID
+ // Should be removed after fixing QTBUG-112855
+ auto widget = QTestWidget::createAndShow(Qt::Window | Qt::FramelessWindowHint,
+ QRect{ 200, 100, 430, 351 });
+ QVERIFY(QTest::qWaitForWindowExposed(widget.get()));
+ QTest::qWait(100);
+#endif
+ TestVideoSink sink;
+ QScreenCapture sc;
+
+ QSignalSpy errorsSpy(&sc, &QScreenCapture::errorOccurred);
+ QSignalSpy activeStateSpy(&sc, &QScreenCapture::activeChanged);
+
+ QMediaCaptureSession session;
+
+ session.setScreenCapture(&sc);
+ session.setVideoSink(&sink);
+
+ QCOMPARE(activeStateSpy.size(), 0);
+ QVERIFY(!sc.isActive());
+
+ // set active true
+ {
+ sc.setActive(true);
+
+ QVERIFY(sc.isActive());
+ QCOMPARE(activeStateSpy.size(), 1);
+ QCOMPARE(activeStateSpy.front().front().toBool(), true);
+ QCOMPARE(errorsSpy.size(), 0);
+ }
+
+ // wait a bit
+ {
+ activeStateSpy.clear();
+ QTest::qWait(50);
+
+ QCOMPARE(activeStateSpy.size(), 0);
+ }
+
+ // set active false
+ {
+ sc.setActive(false);
+
+ sink.setStoreImagesEnabled(true);
+
+ QVERIFY(!sc.isActive());
+ QCOMPARE(sink.images().size(), 0u);
+ QCOMPARE(activeStateSpy.size(), 1);
+ QCOMPARE(activeStateSpy.front().front().toBool(), false);
+ QCOMPARE(errorsSpy.size(), 0);
+ }
+
+ // set active false again
+ {
+ activeStateSpy.clear();
+
+ sc.setActive(false);
+
+ QVERIFY(!sc.isActive());
+ QCOMPARE(activeStateSpy.size(), 0);
+ QCOMPARE(errorsSpy.size(), 0);
+ }
+}
+
+void tst_QScreenCaptureBackend::capture(QTestWidget &widget, const QPoint &drawingOffset,
+ const QSize &expectedSize,
+ std::function<void(QScreenCapture &)> scModifier)
+{
+ TestVideoSink sink;
+ QScreenCapture sc;
+
+ QSignalSpy errorsSpy(&sc, &QScreenCapture::errorOccurred);
+
+ if (scModifier)
+ scModifier(sc);
+
+ QMediaCaptureSession session;
+
+ session.setScreenCapture(&sc);
+ session.setVideoSink(&sink);
+
+ const auto pixelRatio = widget.devicePixelRatio();
+
+ sc.setActive(true);
+
+ QVERIFY(sc.isActive());
+
+ // In some cases, on Linux the window seems to be of a wrong color after appearance,
+ // the delay helps.
+ // TODO: remove the delay
+ QTest::qWait(300);
+
+ // Let's wait for the first frame to address a potential initialization delay.
+ // In practice, the delay varies between the platform and may randomly get increased.
+ {
+ const auto firstFrame = sink.waitForFrame();
+ QVERIFY(firstFrame.isValid());
+ }
+
+ sink.setStoreImagesEnabled();
+
+ const int delay = 200;
+
+ QTest::qWait(delay);
+ const auto expectedFramesCount =
+ delay / static_cast<int>(1000 / std::min(widget.screen()->refreshRate(), 60.));
+ const int framesCount = static_cast<int>(sink.images().size());
+ QCOMPARE_LE(framesCount, expectedFramesCount + 2);
+ QCOMPARE_GE(framesCount, 1);
+
+ for (const auto &image : sink.images()) {
+ auto pixelColor = [&drawingOffset, pixelRatio, &image](int x, int y) {
+ return image.pixelColor((QPoint(x, y) + drawingOffset) * pixelRatio).toRgb();
+ };
+ const int capturedWidth = qRound(image.size().width() / pixelRatio);
+ const int capturedHeight = qRound(image.size().height() / pixelRatio);
+ QCOMPARE(QSize(capturedWidth, capturedHeight), expectedSize);
+ QCOMPARE(pixelColor(0, 0), QColor(0xFF, 0, 0));
+
+ QCOMPARE(pixelColor(39, 50), QColor(0xFF, 0, 0));
+ QCOMPARE(pixelColor(40, 49), QColor(0xFF, 0, 0));
+
+ QCOMPARE(pixelColor(40, 50), QColor(0, 0, 0xFF));
+ }
+
+ QCOMPARE(errorsSpy.size(), 0);
+}
+
+void tst_QScreenCaptureBackend::removeWhileCapture(
+ std::function<void(QScreenCapture &)> scModifier, std::function<void()> deleter)
+{
+ QVideoSink sink;
+ QScreenCapture sc;
+
+ QSignalSpy errorsSpy(&sc, &QScreenCapture::errorOccurred);
+
+ QMediaCaptureSession session;
+
+ if (scModifier)
+ scModifier(sc);
+
+ session.setScreenCapture(&sc);
+ session.setVideoSink(&sink);
+
+ sc.setActive(true);
+
+ QTest::qWait(300);
+
+ QCOMPARE(errorsSpy.size(), 0);
+
+ if (deleter)
+ deleter();
+
+ QTest::qWait(100);
+
+ QSignalSpy framesSpy(&sink, &QVideoSink::videoFrameChanged);
+
+ QTest::qWait(100);
+
+ QCOMPARE(errorsSpy.size(), 1);
+ QCOMPARE(errorsSpy.front().front().value<QScreenCapture::Error>(),
+ QScreenCapture::CaptureFailed);
+ QVERIFY2(!errorsSpy.front().back().value<QString>().isEmpty(),
+ "Expected not empty error description");
+
+ QVERIFY2(framesSpy.empty(), "No frames expected after screen removal");
+}
+
+void tst_QScreenCaptureBackend::initTestCase()
+{
+#ifdef Q_OS_ANDROID
+ QSKIP("grabWindow() no longer supported on Android adding child windows support: QTBUG-118849");
+#endif
+#if defined(Q_OS_LINUX)
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci" &&
+ qEnvironmentVariable("XDG_SESSION_TYPE").toLower() != "x11")
+ QSKIP("Skip on wayland; to be fixed");
+#endif
+
+ if (!QApplication::primaryScreen())
+ QSKIP("No screens found");
+
+ QScreenCapture sc;
+ if (sc.error() == QScreenCapture::CapturingNotSupported)
+ QSKIP("Screen capturing not supported");
+}
+
+void tst_QScreenCaptureBackend::setScreen_selectsScreen_whenCalledWithWidgetsScreen()
+{
+ auto widget = QTestWidget::createAndShow(Qt::Window | Qt::FramelessWindowHint
+ | Qt::WindowStaysOnTopHint
+#ifdef Q_OS_ANDROID
+ | Qt::Popup
+#endif
+ ,
+ QRect{ 200, 100, 430, 351 });
+ QVERIFY(QTest::qWaitForWindowExposed(widget.get()));
+
+ capture(*widget, { 200, 100 }, widget->screen()->size(),
+ [&widget](QScreenCapture &sc) { sc.setScreen(widget->screen()); });
+}
+
+void tst_QScreenCaptureBackend::constructor_selectsPrimaryScreenAsDefault()
+{
+ auto widget = QTestWidget::createAndShow(Qt::Window | Qt::FramelessWindowHint
+ | Qt::WindowStaysOnTopHint
+#ifdef Q_OS_ANDROID
+ | Qt::Popup
+#endif
+ ,
+ QRect{ 200, 100, 430, 351 });
+ QVERIFY(QTest::qWaitForWindowExposed(widget.get()));
+
+ capture(*widget, { 200, 100 }, QApplication::primaryScreen()->size(), nullptr);
+}
+
+void tst_QScreenCaptureBackend::setScreen_selectsSecondaryScreen_whenCalledWithSecondaryScreen()
+{
+ const auto screens = QApplication::screens();
+ if (screens.size() < 2)
+ QSKIP("2 or more screens required");
+
+ auto topLeft = screens.back()->geometry().topLeft().x();
+
+ auto widgetOnSecondaryScreen = QTestWidget::createAndShow(
+ Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint,
+ QRect{ topLeft + 200, 100, 430, 351 }, screens.back());
+ QVERIFY(QTest::qWaitForWindowExposed(widgetOnSecondaryScreen.get()));
+
+ auto widgetOnPrimaryScreen = QTestWidget::createAndShow(
+ Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint,
+ QRect{ 200, 100, 430, 351 }, screens.front(), QColor(0, 0, 0), QColor(0, 0, 0));
+ QVERIFY(QTest::qWaitForWindowExposed(widgetOnPrimaryScreen.get()));
+ capture(*widgetOnSecondaryScreen, { 200, 100 }, screens.back()->size(),
+ [&screens](QScreenCapture &sc) { sc.setScreen(screens.back()); });
+}
+
+void tst_QScreenCaptureBackend::capture_capturesToFile_whenConnectedToMediaRecorder()
+{
+#ifdef Q_OS_LINUX
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("QTBUG-116671: SKIP on linux CI to avoid crashes in ffmpeg. To be fixed.");
+#endif
+
+ // Create widget with blue color
+ auto widget = QTestWidget::createAndShow(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint,
+ QRect{ 200, 100, 430, 351 });
+ widget->setColors(QColor(0, 0, 0xFF), QColor(0, 0, 0xFF));
+
+ QScreenCapture sc;
+ QSignalSpy errorsSpy(&sc, &QScreenCapture::errorOccurred);
+ QMediaCaptureSession session;
+ QMediaRecorder recorder;
+ session.setScreenCapture(&sc);
+ session.setRecorder(&recorder);
+ auto screen = QApplication::primaryScreen();
+ QSize screenSize = screen->geometry().size();
+ QSize videoResolution = QSize(1920, 1080);
+ recorder.setVideoResolution(videoResolution);
+ recorder.setQuality(QMediaRecorder::VeryHighQuality);
+
+ // Insert metadata
+ QMediaMetaData metaData;
+ metaData.insert(QMediaMetaData::Author, QStringLiteral("Author"));
+ metaData.insert(QMediaMetaData::Date, QDateTime::currentDateTime());
+ recorder.setMetaData(metaData);
+
+ sc.setActive(true);
+
+ QTest::qWait(1000); // wait a bit for SC threading activating
+
+ {
+ QSignalSpy recorderStateChanged(&recorder, &QMediaRecorder::recorderStateChanged);
+
+ recorder.record();
+
+ QTRY_VERIFY(!recorderStateChanged.empty());
+ QCOMPARE(recorder.recorderState(), QMediaRecorder::RecordingState);
+ }
+
+ QTest::qWait(1000);
+ widget->setColors(QColor(0, 0xFF, 0), QColor(0, 0xFF, 0)); // Change widget color
+ QTest::qWait(1000);
+
+ {
+ QSignalSpy recorderStateChanged(&recorder, &QMediaRecorder::recorderStateChanged);
+
+ recorder.stop();
+
+ QTRY_VERIFY(!recorderStateChanged.empty());
+ QCOMPARE(recorder.recorderState(), QMediaRecorder::StoppedState);
+ }
+
+ QString fileName = recorder.actualLocation().toLocalFile();
+ QVERIFY(!fileName.isEmpty());
+ QVERIFY(QFileInfo(fileName).size() > 0);
+
+ TestVideoSink sink;
+ QMediaPlayer player;
+ player.setSource(fileName);
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+ QCOMPARE_EQ(player.metaData().value(QMediaMetaData::Resolution).toSize(),
+ QSize(videoResolution));
+ QCOMPARE_GT(player.duration(), 350);
+ QCOMPARE_LT(player.duration(), 3000);
+
+ // Convert video frames to QImages
+ player.setVideoSink(&sink);
+ sink.setStoreImagesEnabled();
+ player.setPlaybackRate(10);
+ player.play();
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ const size_t framesCount = sink.images().size();
+
+ // Find pixel point at center of widget
+ int x = 415 * videoResolution.width() / screenSize.width();
+ int y = 275 * videoResolution.height() / screenSize.height();
+ auto point = QPoint(x, y);
+
+ // Verify color of first fourth of the video frames
+ for (size_t i = 0; i <= static_cast<size_t>(framesCount * 0.25); i++) {
+ QImage image = sink.images().at(i);
+ QVERIFY(!image.isNull());
+ QRgb rgb = image.pixel(point);
+// qDebug() << QStringLiteral("RGB: %1, %2, %3").arg(qRed(rgb)).arg(qGreen(rgb)).arg(qBlue(rgb));
+
+ // RGB values should be 0, 0, 255. Compensating for inaccurate video encoding.
+ QVERIFY(qRed(rgb) <= 60);
+ QVERIFY(qGreen(rgb) <= 60);
+ QVERIFY(qBlue(rgb) >= 200);
+ }
+
+ // Verify color of last fourth of the video frames
+ for (size_t i = static_cast<size_t>(framesCount * 0.75); i < framesCount - 1; i++) {
+ QImage image = sink.images().at(i);
+ QVERIFY(!image.isNull());
+ QRgb rgb = image.pixel(point);
+// qDebug() << QStringLiteral("RGB: %1, %2, %3").arg(qRed(rgb)).arg(qGreen(rgb)).arg(qBlue(rgb));
+
+ // RGB values should be 0, 255, 0. Compensating for inaccurate video encoding.
+ QVERIFY(qRed(rgb) <= 60);
+ QVERIFY(qGreen(rgb) >= 200);
+ QVERIFY(qBlue(rgb) <= 60);
+ }
+
+ QFile(fileName).remove();
+}
+
+void tst_QScreenCaptureBackend::removeScreenWhileCapture()
+{
+ QSKIP("TODO: find a reliable way to emulate it");
+
+ removeWhileCapture([](QScreenCapture &sc) { sc.setScreen(QApplication::primaryScreen()); },
+ []() {
+ // It's something that doesn't look safe but it performs required flow
+ // and allows to test the corener case.
+ delete QApplication::primaryScreen();
+ });
+}
+
+QTEST_MAIN(tst_QScreenCaptureBackend)
+
+#include "tst_qscreencapturebackend.moc"
diff --git a/tests/auto/integration/qsoundeffect/CMakeLists.txt b/tests/auto/integration/qsoundeffect/CMakeLists.txt
index 36340f9c1..d3760e816 100644
--- a/tests/auto/integration/qsoundeffect/CMakeLists.txt
+++ b/tests/auto/integration/qsoundeffect/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qsoundeffect.pro.
#####################################################################
@@ -10,7 +13,7 @@ list(APPEND test_data "test.wav")
qt_internal_add_test(tst_qsoundeffect
SOURCES
tst_qsoundeffect.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
TESTDATA ${test_data}
diff --git a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp
index 21cda97b2..24a6365a8 100644
--- a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp
+++ b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/qlocale.h>
@@ -116,13 +89,13 @@ void tst_QSoundEffect::initTestCase()
void tst_QSoundEffect::testSource()
{
- QSignalSpy readSignal(sound, SIGNAL(sourceChanged()));
+ QSignalSpy readSignal(sound, &QSoundEffect::sourceChanged);
sound->setSource(url);
sound->setVolume(0.1f);
QCOMPARE(sound->source(),url);
- QCOMPARE(readSignal.count(),1);
+ QCOMPARE(readSignal.size(),1);
QTestEventLoop::instance().enterLoop(1);
sound->play();
@@ -135,24 +108,24 @@ void tst_QSoundEffect::testLooping()
sound->setSource(url);
QTRY_COMPARE(sound->status(), QSoundEffect::Ready);
- QSignalSpy readSignal_Count(sound, SIGNAL(loopCountChanged()));
- QSignalSpy readSignal_Remaining(sound, SIGNAL(loopsRemainingChanged()));
+ QSignalSpy readSignal_Count(sound, &QSoundEffect::loopCountChanged);
+ QSignalSpy readSignal_Remaining(sound, &QSoundEffect::loopsRemainingChanged);
sound->setLoopCount(3);
sound->setVolume(0.1f);
QCOMPARE(sound->loopCount(), 3);
- QCOMPARE(readSignal_Count.count(), 1);
+ QCOMPARE(readSignal_Count.size(), 1);
QCOMPARE(sound->loopsRemaining(), 0);
- QCOMPARE(readSignal_Remaining.count(), 0);
+ QCOMPARE(readSignal_Remaining.size(), 0);
sound->play();
- QVERIFY(readSignal_Remaining.count() > 0);
+ QVERIFY(readSignal_Remaining.size() > 0);
// test.wav is about 200ms, wait until it has finished playing 3 times
QTestEventLoop::instance().enterLoop(3);
QTRY_COMPARE(sound->loopsRemaining(), 0);
- QVERIFY(readSignal_Remaining.count() == 4);
+ QVERIFY(readSignal_Remaining.size() == 4);
QTRY_VERIFY(!sound->isPlaying());
// QTBUG-36643 (setting the loop count while playing should work)
@@ -162,29 +135,29 @@ void tst_QSoundEffect::testLooping()
sound->setLoopCount(10);
QCOMPARE(sound->loopCount(), 10);
- QCOMPARE(readSignal_Count.count(), 1);
+ QCOMPARE(readSignal_Count.size(), 1);
QCOMPARE(sound->loopsRemaining(), 0);
- QCOMPARE(readSignal_Remaining.count(), 0);
+ QCOMPARE(readSignal_Remaining.size(), 0);
sound->play();
- QVERIFY(readSignal_Remaining.count() > 0);
+ QVERIFY(readSignal_Remaining.size() > 0);
// wait for the sound to be played several times
QTRY_VERIFY(sound->loopsRemaining() <= 7);
- QVERIFY(readSignal_Remaining.count() >= 3);
+ QVERIFY(readSignal_Remaining.size() >= 3);
readSignal_Count.clear();
readSignal_Remaining.clear();
// change the loop count while playing
sound->setLoopCount(3);
QCOMPARE(sound->loopCount(), 3);
- QCOMPARE(readSignal_Count.count(), 1);
+ QCOMPARE(readSignal_Count.size(), 1);
QCOMPARE(sound->loopsRemaining(), 3);
- QCOMPARE(readSignal_Remaining.count(), 1);
+ QCOMPARE(readSignal_Remaining.size(), 1);
// wait for all the loops to be completed
QTRY_COMPARE(sound->loopsRemaining(), 0);
- QTRY_VERIFY(readSignal_Remaining.count() == 4);
+ QTRY_VERIFY(readSignal_Remaining.size() == 4);
QTRY_VERIFY(!sound->isPlaying());
}
@@ -194,13 +167,13 @@ void tst_QSoundEffect::testLooping()
sound->setLoopCount(QSoundEffect::Infinite);
QCOMPARE(sound->loopCount(), int(QSoundEffect::Infinite));
- QCOMPARE(readSignal_Count.count(), 1);
+ QCOMPARE(readSignal_Count.size(), 1);
QCOMPARE(sound->loopsRemaining(), 0);
- QCOMPARE(readSignal_Remaining.count(), 0);
+ QCOMPARE(readSignal_Remaining.size(), 0);
sound->play();
QTRY_COMPARE(sound->loopsRemaining(), int(QSoundEffect::Infinite));
- QCOMPARE(readSignal_Remaining.count(), 1);
+ QCOMPARE(readSignal_Remaining.size(), 1);
QTest::qWait(500);
QVERIFY(sound->isPlaying());
@@ -210,34 +183,34 @@ void tst_QSoundEffect::testLooping()
// Setting the loop count to 0 should play it one last time
sound->setLoopCount(0);
QCOMPARE(sound->loopCount(), 1);
- QCOMPARE(readSignal_Count.count(), 1);
+ QCOMPARE(readSignal_Count.size(), 1);
QCOMPARE(sound->loopsRemaining(), 1);
- QCOMPARE(readSignal_Remaining.count(), 1);
+ QCOMPARE(readSignal_Remaining.size(), 1);
QTRY_COMPARE(sound->loopsRemaining(), 0);
- QTRY_VERIFY(readSignal_Remaining.count() >= 2);
+ QTRY_VERIFY(readSignal_Remaining.size() >= 2);
QTRY_VERIFY(!sound->isPlaying());
}
}
void tst_QSoundEffect::testVolume()
{
- QSignalSpy readSignal(sound, SIGNAL(volumeChanged()));
+ QSignalSpy readSignal(sound, &QSoundEffect::volumeChanged);
sound->setVolume(0.5);
QCOMPARE(sound->volume(),0.5);
- QTRY_COMPARE(readSignal.count(),1);
+ QTRY_COMPARE(readSignal.size(),1);
}
void tst_QSoundEffect::testMuting()
{
- QSignalSpy readSignal(sound, SIGNAL(mutedChanged()));
+ QSignalSpy readSignal(sound, &QSoundEffect::mutedChanged);
sound->setMuted(true);
QCOMPARE(sound->isMuted(),true);
- QTRY_COMPARE(readSignal.count(),1);
+ QTRY_COMPARE(readSignal.size(),1);
}
void tst_QSoundEffect::testPlaying()
@@ -402,12 +375,12 @@ void tst_QSoundEffect::testSupportedMimeTypes()
void tst_QSoundEffect::testCorruptFile()
{
for (int i = 0; i < 10; i++) {
- QSignalSpy statusSpy(sound, SIGNAL(statusChanged()));
+ QSignalSpy statusSpy(sound, &QSoundEffect::statusChanged);
sound->setSource(urlCorrupted);
QVERIFY(!sound->isPlaying());
QVERIFY(sound->status() == QSoundEffect::Loading || sound->status() == QSoundEffect::Error);
QTRY_COMPARE(sound->status(), QSoundEffect::Error);
- QCOMPARE(statusSpy.count(), 2);
+ QCOMPARE(statusSpy.size(), 2);
sound->play();
QVERIFY(!sound->isPlaying());
diff --git a/tests/auto/integration/qvideoframebackend/CMakeLists.txt b/tests/auto/integration/qvideoframebackend/CMakeLists.txt
new file mode 100644
index 000000000..8c129fa97
--- /dev/null
+++ b/tests/auto/integration/qvideoframebackend/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qvideoframebackend Test:
+#####################################################################
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ testdata/*)
+list(APPEND testdata_resource_files ${test_data_glob})
+
+qt_internal_add_test(tst_qvideoframebackend
+ SOURCES
+ ../shared/mediafileselector.h
+ ../shared/testvideosink.h
+ tst_qvideoframebackend.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::MultimediaPrivate
+ TESTDATA ${testdata_resource_files}
+ INCLUDE_DIRECTORIES
+ ../shared/
+)
+
+qt_internal_add_resource(tst_qvideoframebackend "testdata"
+ PREFIX
+ "/"
+ FILES
+ ${testdata_resource_files}
+)
diff --git a/tests/auto/integration/qvideoframebackend/testdata/colors.mp4 b/tests/auto/integration/qvideoframebackend/testdata/colors.mp4
new file mode 100644
index 000000000..30ddda8b0
--- /dev/null
+++ b/tests/auto/integration/qvideoframebackend/testdata/colors.mp4
Binary files differ
diff --git a/tests/auto/integration/qvideoframebackend/testdata/one_red_frame.mp4 b/tests/auto/integration/qvideoframebackend/testdata/one_red_frame.mp4
new file mode 100644
index 000000000..6b67a3433
--- /dev/null
+++ b/tests/auto/integration/qvideoframebackend/testdata/one_red_frame.mp4
Binary files differ
diff --git a/tests/auto/integration/qvideoframebackend/tst_qvideoframebackend.cpp b/tests/auto/integration/qvideoframebackend/tst_qvideoframebackend.cpp
new file mode 100644
index 000000000..72751d889
--- /dev/null
+++ b/tests/auto/integration/qvideoframebackend/tst_qvideoframebackend.cpp
@@ -0,0 +1,258 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <qmediaplayer.h>
+#include <qvideoframe.h>
+#include <qdebug.h>
+
+#include "mediafileselector.h"
+#include "testvideosink.h"
+#include "private/qvideotexturehelper_p.h"
+#include "private/qvideowindow_p.h"
+#include <thread>
+
+
+QT_USE_NAMESPACE
+
+class tst_QVideoFrameBackend : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void initTestCase();
+ void init() { }
+ void cleanup() { }
+
+private slots:
+ void testMediaFilesAreSupported();
+
+ void toImage_retainsThePreviousMappedState_data();
+ void toImage_retainsThePreviousMappedState();
+
+ void toImage_rendersUpdatedFrame_afterMappingInWriteModeAndModifying_data();
+ void toImage_rendersUpdatedFrame_afterMappingInWriteModeAndModifying();
+
+ void toImage_returnsImage_whenCalledFromSeparateThreadAndWhileRenderingToWindow();
+
+private:
+ QVideoFrame createDefaultFrame() const;
+
+ QVideoFrame createMediaPlayerFrame() const;
+
+ using FrameCreator = decltype(&tst_QVideoFrameBackend::createDefaultFrame);
+
+ template <typename F>
+ void addMediaPlayerFrameTestData(F &&f);
+
+private:
+ MaybeUrl m_oneRedFrameVideo = QUnexpect{};
+ MaybeUrl m_colorsVideo = QUnexpect{};
+ MediaFileSelector m_mediaSelector;
+};
+
+QVideoFrame tst_QVideoFrameBackend::createDefaultFrame() const
+{
+ return QVideoFrame(QVideoFrameFormat(QSize(10, 20), QVideoFrameFormat::Format_ARGB8888));
+}
+
+QVideoFrame tst_QVideoFrameBackend::createMediaPlayerFrame() const
+{
+ if (!m_oneRedFrameVideo)
+ return {};
+
+ TestVideoSink sink;
+ QMediaPlayer player;
+
+ player.setVideoOutput(&sink);
+ player.setSource(*m_oneRedFrameVideo);
+
+ player.play();
+
+ return sink.waitForFrame();
+}
+
+template <typename F>
+void tst_QVideoFrameBackend::addMediaPlayerFrameTestData(F &&f)
+{
+ if (!m_oneRedFrameVideo) {
+ qWarning() << "Skipping test data with mediaplayer as the source cannot be open."
+ "\nSee the test case 'testMediaFilesAreSupported' for details";
+ return;
+ }
+
+ f();
+}
+
+void tst_QVideoFrameBackend::initTestCase()
+{
+#ifdef Q_OS_ANDROID
+ qWarning() << "Skip media selection, QTBUG-118571";
+ return;
+#endif
+
+ m_oneRedFrameVideo = m_mediaSelector.select("qrc:/testdata/one_red_frame.mp4");
+ m_colorsVideo = m_mediaSelector.select("qrc:/testdata/colors.mp4");
+}
+
+void tst_QVideoFrameBackend::testMediaFilesAreSupported()
+{
+#ifdef Q_OS_ANDROID
+ QSKIP("Skip test cases with mediaPlayerFrame on Android CI, because of QTBUG-118571");
+#endif
+
+ QCOMPARE(m_mediaSelector.dumpErrors(), "");
+}
+
+void tst_QVideoFrameBackend::toImage_retainsThePreviousMappedState_data()
+{
+ QTest::addColumn<FrameCreator>("frameCreator");
+ QTest::addColumn<QVideoFrame::MapMode>("initialMapMode");
+
+ // clang-format off
+ QTest::addRow("defaulFrame.notMapped") << &tst_QVideoFrameBackend::createDefaultFrame
+ << QVideoFrame::NotMapped;
+ QTest::addRow("defaulFrame.readOnly") << &tst_QVideoFrameBackend::createDefaultFrame
+ << QVideoFrame::ReadOnly;
+
+ addMediaPlayerFrameTestData([]()
+ {
+ QTest::addRow("mediaPlayerFrame.notMapped")
+ << &tst_QVideoFrameBackend::createMediaPlayerFrame
+ << QVideoFrame::NotMapped;
+ QTest::addRow("mediaPlayerFrame.readOnly")
+ << &tst_QVideoFrameBackend::createMediaPlayerFrame
+ << QVideoFrame::ReadOnly;
+ });
+
+ // clang-format on
+}
+
+void tst_QVideoFrameBackend::toImage_retainsThePreviousMappedState()
+{
+ QFETCH(const FrameCreator, frameCreator);
+ QFETCH(const QVideoFrame::MapMode, initialMapMode);
+ const bool initiallyMapped = initialMapMode != QVideoFrame::NotMapped;
+
+ QVideoFrame frame = std::invoke(frameCreator, this);
+ QVERIFY(frame.isValid());
+
+ frame.map(initialMapMode);
+ QCOMPARE(frame.mapMode(), initialMapMode);
+
+ QImage image = frame.toImage();
+ QVERIFY(!image.isNull());
+
+ QCOMPARE(frame.mapMode(), initialMapMode);
+ QCOMPARE(frame.isMapped(), initiallyMapped);
+}
+
+void tst_QVideoFrameBackend::toImage_rendersUpdatedFrame_afterMappingInWriteModeAndModifying_data()
+{
+ QTest::addColumn<FrameCreator>("frameCreator");
+ QTest::addColumn<QVideoFrame::MapMode>("mapMode");
+
+ // clang-format off
+ QTest::addRow("defaulFrame.writeOnly") << &tst_QVideoFrameBackend::createDefaultFrame
+ << QVideoFrame::WriteOnly;
+ QTest::addRow("defaulFrame.readWrite") << &tst_QVideoFrameBackend::createDefaultFrame
+ << QVideoFrame::ReadWrite;
+
+ addMediaPlayerFrameTestData([]()
+ {
+ QTest::addRow("mediaPlayerFrame.writeOnly")
+ << &tst_QVideoFrameBackend::createMediaPlayerFrame
+ << QVideoFrame::WriteOnly;
+ QTest::addRow("mediaPlayerFrame.readWrite")
+ << &tst_QVideoFrameBackend::createMediaPlayerFrame
+ << QVideoFrame::ReadWrite;
+ });
+ // clang-format on
+}
+
+void tst_QVideoFrameBackend::toImage_rendersUpdatedFrame_afterMappingInWriteModeAndModifying()
+{
+ QFETCH(const FrameCreator, frameCreator);
+ QFETCH(const QVideoFrame::MapMode, mapMode);
+
+ // Arrange
+
+ QVideoFrame frame = std::invoke(frameCreator, this);
+ QVERIFY(frame.isValid());
+
+ QImage originalImage = frame.toImage();
+ QVERIFY(!originalImage.isNull());
+
+ // Act: map the frame in write mode and change the top level pixel
+ frame.map(mapMode);
+ QVERIFY(frame.isWritable());
+
+ QCOMPARE_NE(frame.pixelFormat(), QVideoFrameFormat::Format_Invalid);
+
+ const QVideoTextureHelper::TextureDescription *textureDescription =
+ QVideoTextureHelper::textureDescription(frame.pixelFormat());
+ QVERIFY(textureDescription);
+
+ uchar *firstPlaneBits = frame.bits(0);
+ QVERIFY(firstPlaneBits);
+
+ for (int i = 0; i < textureDescription->strideFactor; ++i)
+ firstPlaneBits[i] = ~firstPlaneBits[i];
+
+ frame.unmap();
+
+ // get an image from modified frame
+ QImage modifiedImage = frame.toImage();
+
+ // Assert
+
+ QVERIFY(!frame.isMapped());
+ QCOMPARE_NE(originalImage.pixel(0, 0), modifiedImage.pixel(0, 0));
+ QCOMPARE(originalImage.pixel(1, 0), modifiedImage.pixel(1, 0));
+ QCOMPARE(originalImage.pixel(1, 1), modifiedImage.pixel(1, 1));
+}
+
+void tst_QVideoFrameBackend::toImage_returnsImage_whenCalledFromSeparateThreadAndWhileRenderingToWindow()
+{
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci") {
+#ifdef Q_OS_MACOS
+ QSKIP("SKIP on macOS because of crash and error \"Failed to create QWindow::MetalSurface. Metal is not supported by any of the GPUs in this system.\"");
+#elif defined(Q_OS_ANDROID)
+ QSKIP("SKIP initTestCase on CI, because of QTBUG-118571");
+#endif
+ }
+ // Arrange
+ QVideoWindow window;
+ window.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QMediaPlayer player;
+ player.setVideoOutput(&window);
+
+ const QVideoSink *sink = window.videoSink();
+ std::vector<QImage> images;
+
+ // act
+ connect(sink, &QVideoSink::videoFrameChanged, sink, [&](const QVideoFrame &frame) {
+
+ // Run toImage on separate thread to exercise special code path
+ QImage image;
+ auto t = std::thread([&] { image = frame.toImage(); });
+ t.join();
+
+ if (!image.isNull())
+ images.push_back(image);
+ });
+
+ // Arrange some more
+ player.setSource(*m_colorsVideo);
+ player.setLoops(10);
+ player.play();
+
+ // assert
+ QTRY_COMPARE_GE_WITH_TIMEOUT(images.size(), 10u, std::chrono::seconds(60) );
+}
+
+QTEST_MAIN(tst_QVideoFrameBackend)
+#include "tst_qvideoframebackend.moc"
diff --git a/tests/auto/integration/qwindowcapturebackend/BLACKLIST b/tests/auto/integration/qwindowcapturebackend/BLACKLIST
new file mode 100644
index 000000000..bc176cf98
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/BLACKLIST
@@ -0,0 +1,13 @@
+macos ci
+
+#QTBUG-112827 on Android
+#QTBUG-111190, v4l2m2m issues
+[recorder_encodesFrames_toValidMediaFile]
+linux ci
+android ci
+
+#QTBUG-112827 on Android
+#QTBUG-111190, v4l2m2m issues
+[recorder_encodesFrames_toValidMediaFile_whenWindowResizes]
+linux ci
+android ci
diff --git a/tests/auto/integration/qwindowcapturebackend/CMakeLists.txt b/tests/auto/integration/qwindowcapturebackend/CMakeLists.txt
new file mode 100644
index 000000000..8f633a1da
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_qwindowcapturebackend
+ SOURCES
+ tst_qwindowcapturebackend.cpp
+ widget.h
+ widget.cpp
+ grabber.h
+ grabber.cpp
+ fixture.h
+ fixture.cpp
+ LIBRARIES
+ Qt::Multimedia
+ Qt::Gui
+ Qt::Widgets
+ Qt::MultimediaWidgets
+)
+
+
diff --git a/tests/auto/integration/qwindowcapturebackend/fixture.cpp b/tests/auto/integration/qwindowcapturebackend/fixture.cpp
new file mode 100644
index 000000000..ee130e294
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/fixture.cpp
@@ -0,0 +1,234 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "fixture.h"
+
+#include <qmediaplayer.h>
+#include <qvideowidget.h>
+#include <qsystemsemaphore.h>
+#include <quuid.h>
+
+DisableCursor::DisableCursor()
+{
+ QCursor cursor(Qt::BlankCursor);
+ QApplication::setOverrideCursor(cursor);
+}
+
+DisableCursor::~DisableCursor()
+{
+ QGuiApplication::restoreOverrideCursor();
+}
+
+WindowCaptureFixture::WindowCaptureFixture()
+{
+ m_session.setWindowCapture(&m_capture);
+ m_session.setVideoSink(&m_grabber);
+}
+
+QString WindowCaptureFixture::getResultsPath(const QString &fileName)
+{
+ const QString sep = QStringLiteral("--");
+
+ QString stem = QCoreApplication::applicationName();
+ if (const char *currentTest = QTest::currentTestFunction())
+ stem += sep + QString::fromLatin1(currentTest);
+
+ if (const char *currentTag = QTest::currentDataTag())
+ stem += sep + QString::fromLatin1(currentTag);
+
+ stem += sep + fileName;
+
+ const QDir resultsDir = qEnvironmentVariable("COIN_CTEST_RESULTSDIR", QDir::tempPath());
+
+ return resultsDir.filePath(stem);
+}
+
+bool WindowCaptureFixture::compareImages(QImage actual, const QImage &expected,
+ const QString &fileSuffix)
+{
+ // Convert to same format so that we can compare images
+ actual = actual.convertToFormat(expected.format());
+
+ if (actual == expected)
+ return true;
+
+ qWarning() << "Image comparison failed.";
+ qWarning() << "Actual image:";
+ qWarning() << actual;
+ qWarning() << "Expected image:";
+ qWarning() << expected;
+
+ const QString actualName = getResultsPath(QStringLiteral("actual%1.png").arg(fileSuffix));
+ if (!actual.save(actualName))
+ qWarning() << "Failed to save actual file to " << actualName;
+
+ const QString expectedName = getResultsPath(QStringLiteral("expected%1.png").arg(fileSuffix));
+ if (!expected.save(expectedName))
+ qWarning() << "Failed to save expected file to " << expectedName;
+
+ return false;
+}
+
+bool WindowCaptureWithWidgetFixture::start(QSize size)
+{
+ // In case of window capture failure, signal the grabber so we can stop
+ // waiting for frames that will never come.
+ connect(&m_capture, &QWindowCapture::errorOccurred, &m_grabber, &FrameGrabber::stop);
+
+ m_widget.setSize(size);
+
+ m_widget.show();
+
+ // Make sure window is in a state that allows it to be found by QWindowCapture.
+ // Not necessary on Windows, but seems to be necessary on some platforms.
+ if (!QTest::qWaitForWindowExposed(&m_widget, static_cast<int>(s_testTimeout.count()))) {
+ qWarning() << "Failed to display widget within timeout";
+ return false;
+ }
+
+ m_captureWindow = findCaptureWindow(m_widget.windowTitle());
+
+ if (!m_captureWindow.isValid())
+ return false;
+
+ m_capture.setWindow(m_captureWindow);
+ m_capture.setActive(true);
+
+ return true;
+}
+
+QVideoFrame WindowCaptureWithWidgetFixture::waitForFrame(qint64 noOlderThanTime)
+{
+ const std::vector<QVideoFrame> frames = m_grabber.waitAndTakeFrames(1u, noOlderThanTime);
+ if (frames.empty())
+ return QVideoFrame{};
+
+ return frames.back();
+}
+
+QCapturableWindow WindowCaptureWithWidgetFixture::findCaptureWindow(const QString &windowTitle)
+{
+ QList<QCapturableWindow> allWindows = QWindowCapture::capturableWindows();
+
+ const auto window = std::find_if(allWindows.begin(), allWindows.end(),
+ [windowTitle](const QCapturableWindow &win) {
+ return win.description() == windowTitle;
+ });
+
+ // Extra debug output to help understanding if test widget window could not be found
+ if (window == allWindows.end()) {
+ qDebug() << "Could not find window" << windowTitle << ". Existing capturable windows:";
+ std::for_each(allWindows.begin(), allWindows.end(), [](const QCapturableWindow &win) {
+ qDebug() << " " << win.description();
+ });
+ return QCapturableWindow{};
+ }
+
+ return *window;
+}
+
+void WindowCaptureWithWidgetAndRecorderFixture::start(QSize size, bool togglePattern)
+{
+ if (togglePattern) {
+ // Drive animation
+ connect(&m_grabber, &FrameGrabber::videoFrameChanged, &m_widget,
+ &TestWidget::togglePattern);
+ }
+
+ connect(&m_recorder, &QMediaRecorder::recorderStateChanged, this,
+ &WindowCaptureWithWidgetAndRecorderFixture::recorderStateChanged);
+
+ m_session.setRecorder(&m_recorder);
+ m_recorder.setQuality(QMediaRecorder::HighQuality);
+ m_recorder.setOutputLocation(QUrl::fromLocalFile(m_mediaFile));
+ m_recorder.setVideoResolution(size);
+
+ WindowCaptureWithWidgetFixture::start(size);
+
+ m_recorder.record();
+}
+
+bool WindowCaptureWithWidgetAndRecorderFixture::stop()
+{
+ m_recorder.stop();
+
+ const auto recorderStopped = [this] { return m_recorderState == QMediaRecorder::StoppedState; };
+
+ return QTest::qWaitFor(recorderStopped, s_testTimeout);
+}
+
+bool WindowCaptureWithWidgetAndRecorderFixture::testVideoFilePlayback(const QString &fileName)
+{
+ QVideoWidget widget;
+
+ QMediaPlayer player;
+
+ bool playing = true;
+ connect(&player, &QMediaPlayer::playbackStateChanged, this,
+ [&](QMediaPlayer::PlaybackState state) {
+ if (state == QMediaPlayer::StoppedState)
+ playing = false;
+ });
+
+ QMediaPlayer::Error error = QMediaPlayer::NoError;
+ connect(&player, &QMediaPlayer::errorOccurred, this,
+ [&](QMediaPlayer::Error e, const QString &errorString) {
+ error = e;
+ qWarning() << errorString;
+ });
+
+ player.setSource(QUrl{ fileName });
+ player.setVideoOutput(&widget);
+ widget.show();
+ player.play();
+
+ const bool completed = QTest::qWaitFor(
+ [&] { return !playing || error != QMediaPlayer::NoError; }, s_testTimeout);
+
+ return completed && error == QMediaPlayer::NoError;
+}
+
+void WindowCaptureWithWidgetAndRecorderFixture::recorderStateChanged(
+ QMediaRecorder::RecorderState state)
+{
+ m_recorderState = state;
+}
+
+bool WindowCaptureWithWidgetInOtherProcessFixture::start()
+{
+ // In case of window capture failure, signal the grabber so we can stop
+ // waiting for frames that will never come.
+ connect(&m_capture, &QWindowCapture::errorOccurred, &m_grabber, &FrameGrabber::stop);
+
+ // Create a new window title that is also used as a semaphore key with less than 30 characters
+ const QString windowTitle = QString::number(qHash(QUuid::createUuid().toString()));
+
+ QSystemSemaphore windowVisible{ QNativeIpcKey{ windowTitle } };
+
+ // Start another instance of the test executable and ask it to show a
+ // its test widget.
+ m_windowProcess.setArguments({ "--show", windowTitle });
+ m_windowProcess.setProgram(QApplication::applicationFilePath());
+ m_windowProcess.start();
+
+ // Make sure window is in a state that allows it to be found by QWindowCapture.
+ // We do this by waiting for the process to release the semaphore once its window is visible
+ windowVisible.acquire();
+
+ m_captureWindow = findCaptureWindow(windowTitle);
+
+ if (!m_captureWindow.isValid())
+ return false;
+
+ // Start capturing the out-of-process window
+ m_capture.setWindow(m_captureWindow);
+ m_capture.setActive(true);
+
+ // Show in-process widget used to create a reference image
+ m_widget.show();
+
+ return true;
+}
+
+
+#include "moc_fixture.cpp"
diff --git a/tests/auto/integration/qwindowcapturebackend/fixture.h b/tests/auto/integration/qwindowcapturebackend/fixture.h
new file mode 100644
index 000000000..2f72c3468
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/fixture.h
@@ -0,0 +1,147 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef WINDOW_CAPTURE_FIXTURE_H
+#define WINDOW_CAPTURE_FIXTURE_H
+
+#include "grabber.h"
+#include "widget.h"
+
+#include <chrono>
+#include <qmediacapturesession.h>
+#include <qmediarecorder.h>
+#include <qobject.h>
+#include <qsignalspy.h>
+#include <qtest.h>
+#include <qvideoframe.h>
+#include <qwindowcapture.h>
+#include <qprocess.h>
+
+QT_USE_NAMESPACE
+
+constexpr inline std::chrono::milliseconds s_testTimeout = std::chrono::seconds(60);
+
+/*!
+ Utility used to hide application cursor for image comparison tests.
+ On Windows, the mouse cursor is captured as part of the window capture.
+ This and can cause differences when comparing captured images with images
+ from QWindow::grab() which is used as a reference.
+*/
+struct DisableCursor final
+{
+ DisableCursor();
+ ~DisableCursor();
+
+ DisableCursor(const DisableCursor &) = delete;
+ DisableCursor &operator=(const DisableCursor &) = delete;
+};
+
+/*!
+ Fixture class that orchestrates setup/teardown of window capturing
+*/
+class WindowCaptureFixture : public QObject
+{
+ Q_OBJECT
+
+public:
+ WindowCaptureFixture();
+
+ /*!
+ Compare two images, ignoring format.
+ If images differ, diagnostic output is logged and images are saved to file.
+ */
+ static bool compareImages(QImage actual, const QImage &expected,
+ const QString &fileSuffix = "");
+
+ QMediaCaptureSession m_session;
+ QWindowCapture m_capture;
+ FrameGrabber m_grabber;
+
+ QSignalSpy m_errors{ &m_capture, &QWindowCapture::errorOccurred };
+ QSignalSpy m_activations{ &m_capture, &QWindowCapture::activeChanged };
+
+private:
+ /*!
+ Calculate a result path based upon a single filename.
+ On CI, the file will be located in COIN_CTEST_RESULTSDIR, and on developer
+ computers, the file will be located in TEMP.
+
+ The file name is on the form "testCase_testFunction_[dataTag_]fileName"
+ */
+ static QString getResultsPath(const QString &fileName);
+};
+
+/*!
+ Fixture class that extends window capture fixture with a capturable widget
+*/
+class WindowCaptureWithWidgetFixture : public WindowCaptureFixture
+{
+ Q_OBJECT
+
+public:
+ /*!
+ Starts capturing and returns true if successful.
+
+ Two phase initialization is used to be able to detect
+ failure to find widget window as a capturable window.
+ */
+ bool start(QSize size = { 60, 40 });
+
+ /*!
+ Waits until the a captured frame is received and returns it
+ */
+ QVideoFrame waitForFrame(qint64 noOlderThanTime = 0);
+
+ DisableCursor m_cursorDisabled; // Avoid mouse cursor causing image differences
+ TestWidget m_widget;
+ QCapturableWindow m_captureWindow;
+
+protected:
+ static QCapturableWindow findCaptureWindow(const QString &windowTitle);
+};
+
+class WindowCaptureWithWidgetInOtherProcessFixture : public WindowCaptureWithWidgetFixture
+{
+ Q_OBJECT
+
+public:
+ ~WindowCaptureWithWidgetInOtherProcessFixture() { m_windowProcess.close(); }
+
+ /*!
+ Create widget in separate process and start capturing its content
+ */
+ bool start();
+
+ QProcess m_windowProcess;
+};
+
+class WindowCaptureWithWidgetAndRecorderFixture : public WindowCaptureWithWidgetFixture
+{
+ Q_OBJECT
+
+public:
+ void start(QSize size = { 60, 40 }, bool togglePattern = true);
+
+ /*!
+ Stop recording.
+
+ Since recorder finalizes the file asynchronously, even after destructors are called,
+ we need to explicitly wait for the stopped state before ending the test. If we don't
+ do this, the media file can not be deleted by the QTemporaryDir at destruction.
+ */
+ bool stop();
+
+ bool testVideoFilePlayback(const QString& fileName);
+
+public slots:
+ void recorderStateChanged(QMediaRecorder::RecorderState state);
+
+public:
+ QTemporaryDir m_tempDir;
+ const QString m_mediaFile = m_tempDir.filePath("test.mp4");
+ QMediaRecorder m_recorder;
+ QMediaRecorder::RecorderState m_recorderState = QMediaRecorder::StoppedState;
+ QSignalSpy m_recorderErrors{ &m_recorder, &QMediaRecorder::errorOccurred };
+};
+
+#endif
diff --git a/tests/auto/integration/qwindowcapturebackend/grabber.cpp b/tests/auto/integration/qwindowcapturebackend/grabber.cpp
new file mode 100644
index 000000000..a7b72aeef
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/grabber.cpp
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "grabber.h"
+#include "fixture.h"
+
+#include <qtest.h>
+#include <qvideoframe.h>
+
+FrameGrabber::FrameGrabber()
+{
+ const auto copyFrame = [this](const QVideoFrame &frame) { m_frames.push_back(frame); };
+
+ connect(this, &QVideoSink::videoFrameChanged, this, copyFrame, Qt::DirectConnection);
+}
+
+const std::vector<QVideoFrame> &FrameGrabber::getFrames() const
+{
+ return m_frames;
+}
+
+std::vector<QVideoFrame> FrameGrabber::waitAndTakeFrames(size_t minCount, qint64 noOlderThanTime)
+{
+ m_frames.clear();
+
+ const auto enoughFramesOrStopped = [this, minCount, noOlderThanTime]() -> bool {
+ if (m_stopped)
+ return true; // Stop waiting
+
+ if (noOlderThanTime > 0) {
+ // Reject frames older than noOlderThanTime
+ const auto newEnd = std::remove_if(m_frames.begin(), m_frames.end(),
+ [noOlderThanTime](const QVideoFrame &frame) {
+ return frame.startTime() <= noOlderThanTime;
+ });
+ m_frames.erase(newEnd, m_frames.end());
+ }
+
+ return m_frames.size() >= minCount;
+ };
+
+ if (!QTest::qWaitFor(enoughFramesOrStopped, s_testTimeout))
+ return {};
+
+ if (m_stopped)
+ return {};
+
+ return std::exchange(m_frames, {});
+}
+
+bool FrameGrabber::isStopped() const
+{
+ return m_stopped;
+}
+
+void FrameGrabber::stop()
+{
+ qWarning() << "Stopping grabber";
+ m_stopped = true;
+}
+
+#include "moc_grabber.cpp"
diff --git a/tests/auto/integration/qwindowcapturebackend/grabber.h b/tests/auto/integration/qwindowcapturebackend/grabber.h
new file mode 100644
index 000000000..e997ff954
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/grabber.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef WINDOW_CAPTURE_GRABBER_H
+#define WINDOW_CAPTURE_GRABBER_H
+
+#include <qvideosink.h>
+#include <vector>
+
+QT_USE_NAMESPACE
+
+/*!
+ The FrameGrabber stores frames that arrive from the window capture,
+ and is used to inspect captured frames in the tests.
+*/
+class FrameGrabber : public QVideoSink
+{
+ Q_OBJECT
+
+public:
+ FrameGrabber();
+
+ const std::vector<QVideoFrame> &getFrames() const;
+
+ /*!
+ Wait for at least \a minCount frames that are no older than noOlderThanTime.
+
+ Returns empty if not enough frames arrived, or if grabber was stopped before global timeout
+ elapsed.
+ */
+ std::vector<QVideoFrame> waitAndTakeFrames(size_t minCount, qint64 noOlderThanTime = 0);
+
+ bool isStopped() const;
+
+public slots:
+ void stop();
+
+private:
+ std::vector<QVideoFrame> m_frames;
+ bool m_stopped = false;
+};
+
+#endif
diff --git a/tests/auto/integration/qwindowcapturebackend/tst_qwindowcapturebackend.cpp b/tests/auto/integration/qwindowcapturebackend/tst_qwindowcapturebackend.cpp
new file mode 100644
index 000000000..6809f81a8
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/tst_qwindowcapturebackend.cpp
@@ -0,0 +1,278 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// TESTED_COMPONENT=src/multimedia
+
+#include "fixture.h"
+#include "widget.h"
+
+#include <qmediarecorder.h>
+#include <qpainter.h>
+#include <qsignalspy.h>
+#include <qtest.h>
+#include <qwindowcapture.h>
+#include <qcommandlineparser.h>
+
+#include <chrono>
+#include <vector>
+
+using std::chrono::duration_cast;
+using std::chrono::high_resolution_clock;
+using std::chrono::microseconds;
+
+QT_USE_NAMESPACE
+
+class tst_QWindowCaptureBackend : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ static void initTestCase()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("Feature does not work on Android");
+#endif
+#if defined(Q_OS_LINUX)
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci"
+ && qEnvironmentVariable("XDG_SESSION_TYPE").toLower() != "x11")
+ QSKIP("Skip on wayland; to be fixed");
+#elif defined(Q_OS_MACOS)
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("QTBUG-116285: Skip on macOS CI because of permissions issues");
+#endif
+
+ const QWindowCapture capture;
+ if (capture.error() == QWindowCapture::CapturingNotSupported)
+ QSKIP("Screen capturing not supported");
+ }
+
+ void isActive_returnsFalse_whenNotStarted()
+ {
+ const WindowCaptureFixture fixture;
+ QVERIFY(!fixture.m_capture.isActive());
+ }
+
+ void setActive_failsAndEmitEerrorOccurred_whenNoWindowSelected()
+ {
+ WindowCaptureFixture fixture;
+
+ fixture.m_capture.setActive(true);
+
+ QVERIFY(!fixture.m_capture.isActive());
+ QVERIFY(!fixture.m_errors.empty());
+ }
+
+ void setActive_startsWindowCapture_whenCalledWithTrue()
+ {
+ WindowCaptureWithWidgetFixture fixture;
+ QVERIFY(fixture.start());
+
+ // Ensure that we have received a frame
+ QVERIFY(fixture.waitForFrame().isValid());
+
+ QCOMPARE(fixture.m_activations.size(), 1);
+ QVERIFY(fixture.m_errors.empty());
+ }
+
+ void capturedImage_equals_imageFromGrab_data()
+ {
+ QTest::addColumn<QSize>("windowSize");
+ QTest::newRow("single-pixel-window") << QSize{1, 1};
+ QTest::newRow("small-window") << QSize{60, 40};
+ QTest::newRow("odd-width-window") << QSize{ 61, 40 };
+ QTest::newRow("odd-height-window") << QSize{ 60, 41 };
+ QTest::newRow("big-window") << QApplication::primaryScreen()->size();
+ }
+
+ void capturedImage_equals_imageFromGrab()
+ {
+ QFETCH(QSize, windowSize);
+
+ WindowCaptureWithWidgetFixture fixture;
+ QVERIFY(fixture.start(windowSize));
+
+ const QImage expected = fixture.m_widget.grabImage();
+ const QImage actual = fixture.waitForFrame().toImage();
+
+ QVERIFY(fixture.compareImages(actual, expected));
+ }
+
+ void capturedImage_changes_whenWindowContentChanges()
+ {
+ WindowCaptureWithWidgetFixture fixture;
+ QVERIFY(fixture.start());
+
+ const auto startTime = high_resolution_clock::now();
+
+ const QVideoFrame colorFrame = fixture.waitForFrame();
+ QVERIFY(colorFrame.isValid());
+
+ fixture.m_widget.setDisplayPattern(TestWidget::Grid);
+
+ // Ignore all frames that were grabbed since the colored frame,
+ // to ensure that we get a frame after we changed display pattern
+ const high_resolution_clock::duration delay = high_resolution_clock::now() - startTime;
+ const QVideoFrame gridFrame = fixture.waitForFrame(
+ colorFrame.endTime() + duration_cast<microseconds>(delay).count());
+
+ QVERIFY(gridFrame.isValid());
+
+ // Make sure that the gridFrame has a different content than the colorFrame
+ QCOMPARE(gridFrame.size(), colorFrame.size());
+ QCOMPARE_NE(gridFrame.toImage(), colorFrame.toImage());
+
+ const QImage actualGridImage = fixture.m_widget.grabImage();
+ QVERIFY(fixture.compareImages(gridFrame.toImage(), actualGridImage));
+ }
+
+ void sequenceOfCapturedImages_compareEqual_whenWindowContentIsUnchanged()
+ {
+ WindowCaptureWithWidgetFixture fixture;
+ QVERIFY(fixture.start());
+
+ const std::vector<QVideoFrame> frames = fixture.m_grabber.waitAndTakeFrames(10);
+ QVERIFY(!frames.empty());
+
+ QImage firstFrame = frames.front().toImage();
+ QVERIFY(!firstFrame.isNull());
+
+ qsizetype index = 0;
+ for (const auto &frame : std::as_const(frames)){
+ QVERIFY(fixture.compareImages(frame.toImage(), firstFrame, QString::number(index)));
+ ++index;
+ }
+ }
+
+ void recorder_encodesFrames_toValidMediaFile_data()
+ {
+ QTest::addColumn<QSize>("windowSize");
+ //QTest::newRow("empty-window") << QSize{ 0, 0 }; TODO: Crash
+ //QTest::newRow("single-pixel-window") << QSize{ 1, 1 }; TODO: Crash
+ QTest::newRow("small-window") << QSize{ 60, 40 };
+ QTest::newRow("odd-width-window") << QSize{ 61, 40 };
+ QTest::newRow("odd-height-window") << QSize{ 60, 41 };
+ QTest::newRow("big-window") << QSize{ 800, 600 };
+ }
+
+ void recorder_encodesFrames_toValidMediaFile()
+ {
+#ifdef Q_OS_LINUX
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("QTBUG-116671: SKIP on linux CI to avoid crashes in ffmpeg. To be fixed.");
+#endif
+ QFETCH(QSize, windowSize);
+
+ WindowCaptureWithWidgetAndRecorderFixture fixture;
+ fixture.start(windowSize);
+
+ // Wait on grabber to ensure that video recorder also get some frames
+ fixture.m_grabber.waitAndTakeFrames(60);
+
+ // Wait for recorder finalization
+ fixture.stop();
+
+ QVERIFY(fixture.m_recorderErrors.empty());
+ QVERIFY(QFile{ fixture.m_mediaFile }.exists());
+ QVERIFY(fixture.testVideoFilePlayback(fixture.m_mediaFile));
+ }
+
+ void recorder_encodesFrames_toValidMediaFile_whenWindowResizes_data()
+ {
+ QTest::addColumn<int>("increment");
+ QTest::newRow("shrink") << -1;
+ QTest::newRow("grow") << 1;
+ }
+
+ void recorder_encodesFrames_toValidMediaFile_whenWindowResizes()
+ {
+#ifdef Q_OS_LINUX
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("QTBUG-116671: SKIP on linux CI to avoid crashes in ffmpeg. To be fixed.");
+#endif
+ QFETCH(int, increment);
+
+ QSize windowSize = { 200, 150 };
+ WindowCaptureWithWidgetAndRecorderFixture fixture;
+ fixture.start(windowSize, /*toggle pattern*/ false);
+
+ for (qsizetype i = 0; i < 20; ++i) {
+ windowSize.setWidth(windowSize.width() + increment);
+ windowSize.setHeight(windowSize.height() + increment);
+ fixture.m_widget.setSize(windowSize);
+
+ // Wait on grabber to ensure that video recorder also get some frames
+ fixture.m_grabber.waitAndTakeFrames(1);
+ }
+
+ // Wait for recorder finalization
+ fixture.stop();
+
+ QVERIFY(fixture.m_recorderErrors.empty());
+ QVERIFY(QFile{ fixture.m_mediaFile }.exists());
+ QVERIFY(fixture.testVideoFilePlayback(fixture.m_mediaFile));
+ }
+
+ void windowCapture_capturesWindowsInOtherProcesses()
+ {
+ WindowCaptureWithWidgetInOtherProcessFixture fixture;
+ QVERIFY(fixture.start());
+
+ // Get reference image from our in-process widget
+ const QImage expected = fixture.m_widget.grabImage();
+
+ // Get actual image grabbed from out-of-process widget
+ const QImage actual = fixture.waitForFrame().toImage();
+
+ QVERIFY(fixture.compareImages(actual, expected));
+ }
+
+ /*
+ This test is not a requirement per se, but we want all platforms
+ to behave the same. A reasonable alternative could have been to
+ treat closed window as a regular 'Stop' capture (not an error).
+ */
+ void windowCapture_stopsWithError_whenProcessCloses()
+ {
+ WindowCaptureWithWidgetInOtherProcessFixture fixture;
+ QVERIFY(fixture.start());
+
+ // Get capturing started
+ fixture.m_grabber.waitAndTakeFrames(3);
+
+ // Closing the process waits for it to exit
+ fixture.m_windowProcess.close();
+
+ const bool captureFailed =
+ QTest::qWaitFor([&] { return !fixture.m_errors.empty(); }, s_testTimeout);
+
+ QVERIFY(captureFailed);
+ }
+};
+
+int main(int argc, char *argv[])
+{
+ QCommandLineParser cmd;
+ const QCommandLineOption showTestWidget{ QStringList{ "show" },
+ "Creates a test widget with given title",
+ "windowTitle" };
+ cmd.addOption(showTestWidget);
+ cmd.parse({ argv, argv + argc });
+
+ if (cmd.isSet(showTestWidget)) {
+ QApplication app{ argc, argv };
+ const QString windowTitle = cmd.value(showTestWidget);
+ const bool result = showCaptureWindow(windowTitle);
+ return result ? 0 : 1;
+ }
+
+ // If no special arguments are set, enter the regular QTest main routine
+ TESTLIB_SELFCOVERAGE_START("tst_QWindowCaptureatioBackend")
+ QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<tst_QWindowCaptureBackend>();
+ QApplication app(argc, argv);
+ app.setAttribute(Qt::AA_Use96Dpi, true);
+ tst_QWindowCaptureBackend tc;
+ QTEST_SET_MAIN_SOURCE_PATH return QTest::qExec(&tc, argc, argv);
+
+}
+
+#include "tst_qwindowcapturebackend.moc"
diff --git a/tests/auto/integration/qwindowcapturebackend/widget.cpp b/tests/auto/integration/qwindowcapturebackend/widget.cpp
new file mode 100644
index 000000000..b17487149
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/widget.cpp
@@ -0,0 +1,125 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "widget.h"
+#include "fixture.h"
+
+#include <qapplication.h>
+#include <qsystemsemaphore.h>
+#include <qtest.h>
+
+
+TestWidget::TestWidget(const QString &uuid, QScreen *screen)
+{
+ // Give each window a unique title so that we can uniquely identify it
+ setWindowTitle(uuid);
+
+ setScreen(screen ? screen : QApplication::primaryScreen());
+
+ // Use frameless hint because on Windows UWP platform, the window titlebar is captured,
+ // but the reference image acquired by 'QWindow::grab()' does not include titlebar.
+ // This allows us to do pixel-perfect matching of captured content.
+ setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
+ setFixedSize(60, 40);
+}
+
+void TestWidget::setDisplayPattern(Pattern p)
+{
+ m_pattern = p;
+ repaint();
+}
+
+void TestWidget::setSize(QSize size)
+{
+ if (size == QApplication::primaryScreen()->size())
+ setWindowState(Qt::WindowMaximized);
+ else
+ setFixedSize(size);
+}
+
+QImage TestWidget::grabImage()
+{
+ return grab().toImage();
+}
+
+void TestWidget::togglePattern()
+{
+ Pattern p = m_pattern == ColoredSquares ? Grid : ColoredSquares;
+ setDisplayPattern(p);
+}
+
+void TestWidget::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ p.setPen(Qt::NoPen);
+ p.setBrush(Qt::black);
+ p.drawRect(rect());
+
+ if (m_pattern == ColoredSquares)
+ drawColoredSquares(p);
+ else
+ drawGrid(p);
+
+ p.end();
+}
+
+void TestWidget::drawColoredSquares(QPainter &p)
+{
+ const std::vector<std::vector<Qt::GlobalColor>> colors = { { Qt::red, Qt::green, Qt::blue },
+ { Qt::white, Qt::white, Qt::white },
+ { Qt::blue, Qt::green, Qt::red } };
+
+ const QSize squareSize = size() / 3;
+ QRect rect{ QPoint{ 0, 0 }, squareSize };
+
+ for (const auto &row : colors) {
+ for (const auto &color : row) {
+ p.setBrush(color);
+ p.drawRect(rect);
+ rect.moveLeft(rect.left() + rect.width());
+ }
+ rect.moveTo({ 0, rect.bottom() });
+ }
+}
+
+void TestWidget::drawGrid(QPainter &p) const
+{
+ const QSize winSize = size();
+
+ p.setPen(Qt::white);
+
+ QLine vertical{ QPoint{ 5, 0 }, QPoint{ 5, winSize.height() } };
+ while (vertical.x1() < winSize.width()) {
+ p.drawLine(vertical);
+ vertical.translate(10, 0);
+ }
+ QLine horizontal{ QPoint{ 0, 5 }, QPoint{ winSize.width(), 5 } };
+ while (horizontal.y1() < winSize.height()) {
+ p.drawLine(horizontal);
+ horizontal.translate(0, 10);
+ }
+}
+
+bool showCaptureWindow(const QString &windowTitle)
+{
+ const QNativeIpcKey key{ windowTitle };
+ QSystemSemaphore windowVisible(key);
+
+ TestWidget widget{ windowTitle };
+ widget.show();
+
+ // Wait for window to be visible and suitable for window capturing
+ const bool result = QTest::qWaitForWindowExposed(&widget, s_testTimeout.count());
+ if (!result)
+ qDebug() << "Failed to show window";
+
+ // Signal to host process that the window is visible
+ windowVisible.release();
+
+ // Keep window visible until a termination signal is received
+ QApplication::exec();
+
+ return result;
+}
+
+#include "moc_widget.cpp"
diff --git a/tests/auto/integration/qwindowcapturebackend/widget.h b/tests/auto/integration/qwindowcapturebackend/widget.h
new file mode 100644
index 000000000..56427a566
--- /dev/null
+++ b/tests/auto/integration/qwindowcapturebackend/widget.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef WINDOW_CAPTURE_WIDGET_H
+#define WINDOW_CAPTURE_WIDGET_H
+
+#include <qwidget.h>
+#include <qscreen.h>
+#include <qpainter.h>
+#include <quuid.h>
+
+/*!
+ Window capable of drawing test patterns used for capture tests
+ */
+class TestWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ enum Pattern { ColoredSquares, Grid };
+
+ TestWidget(const QString &uuid = QUuid::createUuid().toString(), QScreen *screen = nullptr);
+
+ void setDisplayPattern(Pattern p);
+ void setSize(QSize size);
+ QImage grabImage();
+
+public slots:
+ void togglePattern();
+
+protected:
+ void paintEvent(QPaintEvent *) override;
+
+private:
+ void drawColoredSquares(QPainter &p);
+ void drawGrid(QPainter &p) const;
+
+ Pattern m_pattern = ColoredSquares;
+};
+
+bool showCaptureWindow(const QString &windowTitle);
+
+#endif
diff --git a/tests/auto/integration/shared/mediabackendutils.h b/tests/auto/integration/shared/mediabackendutils.h
new file mode 100644
index 000000000..3279f7044
--- /dev/null
+++ b/tests/auto/integration/shared/mediabackendutils.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef MEDIABACKENDUTILS_H
+#define MEDIABACKENDUTILS_H
+
+#include <QtTest/qtestcase.h>
+#include <private/qplatformmediaintegration_p.h>
+
+inline bool isGStreamerPlatform()
+{
+ return QPlatformMediaIntegration::instance()->name() == "gstreamer";
+}
+
+inline bool isDarwinPlatform()
+{
+ return QPlatformMediaIntegration::instance()->name() == "darwin";
+}
+
+inline bool isAndroidPlatform()
+{
+ return QPlatformMediaIntegration::instance()->name() == "android";
+}
+
+inline bool isFFMPEGPlatform()
+{
+ return QPlatformMediaIntegration::instance()->name() == "ffmpeg";
+}
+
+inline bool isWindowsPlatform()
+{
+ return QPlatformMediaIntegration::instance()->name() == "windows";
+}
+
+#define QSKIP_GSTREAMER(message) \
+ do { \
+ if (isGStreamerPlatform()) \
+ QSKIP(message); \
+ } while (0)
+
+#define QSKIP_FFMPEG(message) \
+ do { \
+ if (isFFMPEGPlatform()) \
+ QSKIP(message); \
+ } while (0)
+
+#define QEXPECT_FAIL_GSTREAMER(dataIndex, comment, mode) \
+ do { \
+ if (isGStreamerPlatform()) \
+ QEXPECT_FAIL(dataIndex, comment, mode); \
+ } while (0)
+
+#endif // MEDIABACKENDUTILS_H
diff --git a/tests/auto/integration/shared/mediafileselector.h b/tests/auto/integration/shared/mediafileselector.h
index 8a9c3e86a..e36677f34 100644
--- a/tests/auto/integration/shared/mediafileselector.h
+++ b/tests/auto/integration/shared/mediafileselector.h
@@ -1,75 +1,170 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MEDIAFILESELECTOR_H
#define MEDIAFILESELECTOR_H
#include <QUrl>
-#include <QMediaPlayer>
-#include <QAudioOutput>
+#include <qmediaplayer.h>
+#include <qaudiooutput.h>
+#include <qvideosink.h>
#include <qsignalspy.h>
#include <qfileinfo.h>
#include <qtest.h>
+#include <private/qmultimediautils_p.h>
+
+#include <unordered_map>
QT_BEGIN_NAMESPACE
-namespace MediaFileSelector {
+using MaybeUrl = QMaybe<QUrl, QString>;
+
+#define CHECK_SELECTED_URL(maybeUrl) \
+ if (!maybeUrl) \
+ QSKIP((QLatin1String("\nUnable to select none of the media candidates:\n") + maybeUrl.error()) \
+ .toLocal8Bit() \
+ .data())
-static QUrl selectMediaFile(const QStringList& mediaCandidates)
+class MediaFileSelector
{
- QMediaPlayer player;
- QAudioOutput audioOutput;
- QVideoSink videoOutput;
- player.setAudioOutput(&audioOutput);
- player.setVideoOutput(&videoOutput);
+public:
+ int failedSelectionsCount() const { return m_failedSelectionsCount; }
- QSignalSpy errorSpy(&player, SIGNAL(errorOccurred(QMediaPlayer::Error, const QString&)));
+ QString dumpErrors() const
+ {
+ QStringList failedMedias;
+ for (const auto &mediaToError : m_mediaToErrors)
+ if (!mediaToError.second.isEmpty())
+ failedMedias.emplace_back(mediaToError.first);
- for (const QString &media : mediaCandidates) {
- player.setSource(media);
- player.play();
+ failedMedias.sort();
+ return dumpErrors(failedMedias);
+ }
+
+ template <typename... Media>
+ MaybeUrl select(Media... media)
+ {
+ return select({ std::move(nativeFileName(media))... });
+ }
+
+ MaybeUrl select(const QStringList &candidates)
+ {
+ QUrl foundUrl;
+ for (const auto &media : candidates) {
+ auto emplaceRes = m_mediaToErrors.try_emplace(media, QString());
+ if (emplaceRes.second) {
+ auto maybeUrl = selectMediaFile(media);
+ if (!maybeUrl) {
+ Q_ASSERT(!maybeUrl.error().isEmpty());
+ emplaceRes.first->second = maybeUrl.error();
+ }
+ }
- for (int i = 0; i < 2000 && player.mediaStatus() != QMediaPlayer::BufferedMedia && errorSpy.isEmpty(); i+=50) {
- QTest::qWait(50);
+ if (foundUrl.isEmpty() && emplaceRes.first->second.isEmpty())
+ foundUrl = media;
}
- if (player.mediaStatus() == QMediaPlayer::BufferedMedia && errorSpy.isEmpty()) {
- return media;
+ if (!foundUrl.isEmpty())
+ return foundUrl;
+
+ ++m_failedSelectionsCount;
+ return { QUnexpect{}, dumpErrors(candidates) };
+ }
+
+private:
+ QString dumpErrors(const QStringList &medias) const
+ {
+ using namespace Qt::StringLiterals;
+ QString result;
+
+ for (const auto &media : medias) {
+ auto it = m_mediaToErrors.find(media);
+ if (it != m_mediaToErrors.end() && !it->second.isEmpty())
+ result.append("\t"_L1)
+ .append(it->first)
+ .append(": "_L1)
+ .append(it->second)
+ .append("\n"_L1);
}
- errorSpy.clear();
+
+ return result;
+ }
+
+ static MaybeUrl selectMediaFile(QString media)
+ {
+ if (qEnvironmentVariableIsSet("QTEST_SKIP_MEDIA_VALIDATION"))
+ return QUrl(media);
+
+ using namespace Qt::StringLiterals;
+
+ QAudioOutput audioOutput;
+ QVideoSink videoOutput;
+ QMediaPlayer player;
+ player.setAudioOutput(&audioOutput);
+ player.setVideoOutput(&videoOutput);
+
+ player.setSource(media);
+ player.play();
+
+ const auto waitingFinished = QTest::qWaitFor([&]() {
+ const auto status = player.mediaStatus();
+ return status == QMediaPlayer::BufferedMedia || status == QMediaPlayer::EndOfMedia
+ || status == QMediaPlayer::InvalidMedia
+ || player.error() != QMediaPlayer::NoError;
+ });
+
+ auto enumValueToString = [](auto enumValue) {
+ return QString(QMetaEnum::fromType<decltype(enumValue)>().valueToKey(enumValue));
+ };
+
+ if (!waitingFinished)
+ return { QUnexpect{},
+ "The media got stuck in the status "_L1
+ + enumValueToString(player.mediaStatus()) };
+
+ if (player.mediaStatus() == QMediaPlayer::InvalidMedia)
+ return { QUnexpect{},
+ "Unable to load the media. Error ["_L1 + enumValueToString(player.error())
+ + " "_L1 + player.errorString() + "]"_L1 };
+
+ if (player.error() != QMediaPlayer::NoError)
+ return { QUnexpect{},
+ "Unable to start playing the media, codecs issues. Error ["_L1
+ + enumValueToString(player.error()) + " "_L1 + player.errorString()
+ + "]"_L1 };
+
+ return QUrl(media);
}
- return QUrl();
-}
+ QString nativeFileName(const QString &media)
+ {
+#ifdef Q_OS_ANDROID
+ auto it = m_nativeFiles.find(media);
+ if (it != m_nativeFiles.end())
+ return it->second->fileName();
+
+ QFile file(media);
+ if (file.open(QIODevice::ReadOnly)) {
+ m_nativeFiles.insert({ media, std::unique_ptr<QTemporaryFile>(QTemporaryFile::createNativeFile(file))});
+ return m_nativeFiles[media]->fileName();
+ }
+ qWarning() << "Failed to create temporary file";
+#endif // Q_OS_ANDROID
+
+ return media;
+ }
-} // MediaFileSelector namespace
+private:
+#ifdef Q_OS_ANDROID
+ std::unordered_map<QString, std::unique_ptr<QTemporaryFile>> m_nativeFiles;
+#endif
+ std::unordered_map<QString, QString> m_mediaToErrors;
+ int m_failedSelectionsCount = 0;
+};
QT_END_NAMESPACE
+Q_DECLARE_METATYPE(MaybeUrl)
+
#endif
diff --git a/tests/auto/integration/shared/testvideosink.h b/tests/auto/integration/shared/testvideosink.h
new file mode 100644
index 000000000..8c3f9a5bd
--- /dev/null
+++ b/tests/auto/integration/shared/testvideosink.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TESTVIDEOSINK_H
+#define TESTVIDEOSINK_H
+
+#include <qvideosink.h>
+#include <qvideoframe.h>
+#include <qelapsedtimer.h>
+#include <qsignalspy.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ This is a simple video surface which records all presented frames.
+*/
+class TestVideoSink : public QVideoSink
+{
+ Q_OBJECT
+public:
+ explicit TestVideoSink(bool storeFrames = false) : m_storeFrames(storeFrames)
+ {
+ connect(this, &QVideoSink::videoFrameChanged, this, &TestVideoSink::addVideoFrame);
+ connect(this, &QVideoSink::videoFrameChanged, this, &TestVideoSink::videoFrameChangedSync);
+ }
+
+ QVideoFrame waitForFrame()
+ {
+ QSignalSpy spy(this, &TestVideoSink::videoFrameChangedSync);
+ return spy.wait() ? spy.at(0).at(0).value<QVideoFrame>() : QVideoFrame{};
+ }
+
+ void setStoreFrames(bool storeFrames = true) { m_storeFrames = storeFrames; }
+
+private Q_SLOTS:
+ void addVideoFrame(const QVideoFrame &frame)
+ {
+ if (!m_elapsedTimer.isValid())
+ m_elapsedTimer.start();
+ else
+ m_elapsedTimer.restart();
+
+ if (m_storeFrames)
+ m_frameList.append(frame);
+ ++m_totalFrames;
+ }
+
+signals:
+ void videoFrameChangedSync(const QVideoFrame &frame);
+
+public:
+ QList<QVideoFrame> m_frameList;
+ int m_totalFrames = 0; // used instead of the list when frames are not stored
+ QElapsedTimer m_elapsedTimer;
+
+private:
+ bool m_storeFrames;
+};
+
+QT_END_NAMESPACE
+
+#endif // TESTVIDEOSINK_H
diff --git a/tests/auto/runautotests.py b/tests/auto/runautotests.py
index ba277e943..d163cf98c 100755
--- a/tests/auto/runautotests.py
+++ b/tests/auto/runautotests.py
@@ -1,31 +1,6 @@
#! /usr/bin/env python
-#############################################################################
-##
-## Copyright (C) 2016 The Qt Company Ltd.
-## Contact: https://www.qt.io/licensing/
-##
-## This file is part of the build configuration tools of the Qt Toolkit.
-##
-## $QT_BEGIN_LICENSE:GPL-EXCEPT$
-## Commercial License Usage
-## Licensees holding valid commercial Qt licenses may use this file in
-## accordance with the commercial license agreement provided with the
-## Software or, alternatively, in accordance with the terms contained in
-## a written agreement between you and The Qt Company. For licensing terms
-## and conditions see https://www.qt.io/terms-conditions. For further
-## information use the contact form at https://www.qt.io/contact-us.
-##
-## GNU General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU
-## General Public License version 3 as published by the Free Software
-## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-## included in the packaging of this file. Please review the following
-## information to ensure the GNU General Public License requirements will
-## be met: https://www.gnu.org/licenses/gpl-3.0.html.
-##
-## $QT_END_LICENSE$
-##
-#############################################################################
+# Copyright (C) 2016 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import sys;
diff --git a/tests/auto/unit/CMakeLists.txt b/tests/auto/unit/CMakeLists.txt
index d6fe98cc4..d43c29198 100644
--- a/tests/auto/unit/CMakeLists.txt
+++ b/tests/auto/unit/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from unit.pro.
add_subdirectory(mockbackend)
diff --git a/tests/auto/unit/mockbackend/CMakeLists.txt b/tests/auto/unit/mockbackend/CMakeLists.txt
index 56f2c11aa..959341366 100644
--- a/tests/auto/unit/mockbackend/CMakeLists.txt
+++ b/tests/auto/unit/mockbackend/CMakeLists.txt
@@ -1,32 +1,33 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from mockbackend.pro.
#####################################################################
-## QtMultimediaMockBackend Generic Library:
+## MockMultimediaPlugin Generic Library:
#####################################################################
-# special case begin
-add_library(QtMultimediaMockBackend INTERFACE)
-target_include_directories(QtMultimediaMockBackend INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(QtMultimediaMockBackend INTERFACE
- Qt::Core
- Qt::Gui
- Qt::MultimediaPrivate
-)
-target_sources(QtMultimediaMockBackend INTERFACE
- qmockaudiodecoder.h
- qmockaudiooutput.h
- qmockcamera.h
- qmockimagecapture.h qmockimagecapture.cpp
- qmockmediaplayer.h
- qmockmediaencoder.h
- qmockmediacapturesession.h
- qmockvideosink.h
- qmockmediadevices.cpp
- qmockmediadevices_p.h
- qmockintegration.cpp
- qmockintegration_p.h
+qt_internal_add_plugin(MockMultimediaPlugin
+ STATIC
+ OUTPUT_NAME mockmultimediaplugin
+ PLUGIN_TYPE multimedia
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../plugins"
+ DEFAULT_IF FALSE
+ SOURCES
+ qmockaudiodecoder.cpp qmockaudiodecoder.h
+ qmockaudiooutput.h
+ qmockcamera.cpp qmockcamera.h
+ qmockimagecapture.cpp qmockimagecapture.h
+ qmockmediaplayer.h
+ qmockmediaencoder.h
+ qmockmediacapturesession.h
+ qmockvideosink.h
+ qmockmediadevices.cpp qmockmediadevices.h
+ qmockintegration.cpp qmockintegration.h
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::CorePrivate
)
-# special case end
#### Keys ignored in scope 1:.:.:mockbackend.pro:<TRUE>:
# TEMPLATE = "lib"
diff --git a/tests/auto/unit/mockbackend/mock.json b/tests/auto/unit/mockbackend/mock.json
new file mode 100644
index 000000000..499a3de8c
--- /dev/null
+++ b/tests/auto/unit/mockbackend/mock.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "mock" ]
+}
diff --git a/tests/auto/unit/mockbackend/qmockaudiodecoder.cpp b/tests/auto/unit/mockbackend/qmockaudiodecoder.cpp
new file mode 100644
index 000000000..3c6b940a9
--- /dev/null
+++ b/tests/auto/unit/mockbackend/qmockaudiodecoder.cpp
@@ -0,0 +1,139 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qmockaudiodecoder.h"
+
+QT_BEGIN_NAMESPACE
+
+QMockAudioDecoder::QMockAudioDecoder(QAudioDecoder *parent)
+ : QPlatformAudioDecoder(parent), mDevice(0), mPosition(-1), mSerial(0)
+{
+ mFormat.setChannelCount(1);
+ mFormat.setSampleFormat(QAudioFormat::UInt8);
+ mFormat.setSampleRate(1000);
+}
+
+QUrl QMockAudioDecoder::source() const
+{
+ return mSource;
+}
+
+void QMockAudioDecoder::setSource(const QUrl &fileName)
+{
+ mSource = fileName;
+ mDevice = 0;
+ stop();
+}
+
+QIODevice *QMockAudioDecoder::sourceDevice() const
+{
+ return mDevice;
+}
+
+void QMockAudioDecoder::setSourceDevice(QIODevice *device)
+{
+ mDevice = device;
+ mSource.clear();
+ stop();
+}
+
+QAudioFormat QMockAudioDecoder::audioFormat() const
+{
+ return mFormat;
+}
+
+void QMockAudioDecoder::setAudioFormat(const QAudioFormat &format)
+{
+ if (mFormat != format) {
+ mFormat = format;
+ formatChanged(mFormat);
+ }
+}
+
+// When decoding we decode to first buffer, then second buffer
+// we then stop until the first is read again and so on, for
+// 5 buffers
+void QMockAudioDecoder::start()
+{
+ if (!isDecoding()) {
+ if (!mSource.isEmpty()) {
+ setIsDecoding(true);
+ durationChanged(duration());
+
+ QTimer::singleShot(50, this, &QMockAudioDecoder::pretendDecode);
+ } else {
+ error(QAudioDecoder::ResourceError, "No source set");
+ }
+ }
+}
+
+void QMockAudioDecoder::stop()
+{
+ if (isDecoding()) {
+ mSerial = 0;
+ mPosition = 0;
+ mBuffers.clear();
+ setIsDecoding(false);
+ bufferAvailableChanged(false);
+ }
+}
+
+QAudioBuffer QMockAudioDecoder::read()
+{
+ QAudioBuffer a;
+ if (mBuffers.size() > 0) {
+ a = mBuffers.takeFirst();
+ mPosition = a.startTime() / 1000;
+ positionChanged(mPosition);
+
+ if (mBuffers.isEmpty())
+ bufferAvailableChanged(false);
+
+ if (mBuffers.isEmpty() && mSerial >= MOCK_DECODER_MAX_BUFFERS) {
+ finished();
+ } else
+ QTimer::singleShot(50, this, &QMockAudioDecoder::pretendDecode);
+ }
+
+ return a;
+}
+
+bool QMockAudioDecoder::bufferAvailable() const
+{
+ return mBuffers.size() > 0;
+}
+
+qint64 QMockAudioDecoder::position() const
+{
+ return mPosition;
+}
+
+qint64 QMockAudioDecoder::duration() const
+{
+ return (sizeof(mSerial) * MOCK_DECODER_MAX_BUFFERS * qint64(1000))
+ / (mFormat.sampleRate() * mFormat.channelCount());
+}
+
+void QMockAudioDecoder::pretendDecode()
+{
+ // Check if we've reached end of stream
+ if (mSerial >= MOCK_DECODER_MAX_BUFFERS)
+ return;
+
+ // We just keep the length of mBuffers to 3 or less.
+ if (mBuffers.size() < 3) {
+ QByteArray b(sizeof(mSerial), 0);
+ memcpy(b.data(), &mSerial, sizeof(mSerial));
+ qint64 position = (sizeof(mSerial) * mSerial * qint64(1000000))
+ / (mFormat.sampleRate() * mFormat.channelCount());
+ mSerial++;
+ mBuffers.push_back(QAudioBuffer(b, mFormat, position));
+ bufferReady();
+ if (mBuffers.size() == 1)
+ bufferAvailableChanged(true);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qmockaudiodecoder.cpp"
diff --git a/tests/auto/unit/mockbackend/qmockaudiodecoder.h b/tests/auto/unit/mockbackend/qmockaudiodecoder.h
index abc710ac8..89e21ef23 100644
--- a/tests/auto/unit/mockbackend/qmockaudiodecoder.h
+++ b/tests/auto/unit/mockbackend/qmockaudiodecoder.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MOCKAUDIODECODERCONTROL_H
#define MOCKAUDIODECODERCONTROL_H
@@ -47,136 +22,37 @@ class QMockAudioDecoder : public QPlatformAudioDecoder
Q_OBJECT
public:
- QMockAudioDecoder(QAudioDecoder *parent = 0)
- : QPlatformAudioDecoder(parent)
- , mDevice(0)
- , mPosition(-1)
- , mSerial(0)
- {
- mFormat.setChannelCount(1);
- mFormat.setSampleFormat(QAudioFormat::UInt8);
- mFormat.setSampleRate(1000);
- }
-
- QUrl source() const override
- {
- return mSource;
- }
-
- void setSource(const QUrl &fileName) override
- {
- mSource = fileName;
- mDevice = 0;
- stop();
- }
-
- QIODevice* sourceDevice() const override
- {
- return mDevice;
- }
-
- void setSourceDevice(QIODevice *device) override
- {
- mDevice = device;
- mSource.clear();
- stop();
- }
-
- QAudioFormat audioFormat() const override
- {
- return mFormat;
- }
-
- void setAudioFormat(const QAudioFormat &format) override
- {
- if (mFormat != format) {
- mFormat = format;
- emit formatChanged(mFormat);
- }
- }
+ QMockAudioDecoder(QAudioDecoder *parent = nullptr);
+
+ QUrl source() const override;
+
+ void setSource(const QUrl &fileName) override;
+
+ QIODevice *sourceDevice() const override;
+
+ void setSourceDevice(QIODevice *device) override;
+
+ QAudioFormat audioFormat() const override;
+
+ void setAudioFormat(const QAudioFormat &format) override;
// When decoding we decode to first buffer, then second buffer
// we then stop until the first is read again and so on, for
// 5 buffers
- void start() override
- {
- if (!isDecoding()) {
- if (!mSource.isEmpty()) {
- setIsDecoding(true);
- emit durationChanged(duration());
-
- QTimer::singleShot(50, this, SLOT(pretendDecode()));
- } else {
- emit error(QAudioDecoder::ResourceError, "No source set");
- }
- }
- }
-
- void stop() override
- {
- if (isDecoding()) {
- mSerial = 0;
- mPosition = 0;
- mBuffers.clear();
- setIsDecoding(false);
- emit bufferAvailableChanged(false);
- }
- }
-
- QAudioBuffer read() override
- {
- QAudioBuffer a;
- if (mBuffers.length() > 0) {
- a = mBuffers.takeFirst();
- mPosition = a.startTime() / 1000;
- positionChanged(mPosition);
-
- if (mBuffers.isEmpty())
- emit bufferAvailableChanged(false);
-
- if (mBuffers.isEmpty() && mSerial >= MOCK_DECODER_MAX_BUFFERS) {
- emit finished();
- } else
- QTimer::singleShot(50, this, SLOT(pretendDecode()));
- }
-
- return a;
- }
-
- bool bufferAvailable() const override
- {
- return mBuffers.length() > 0;
- }
-
- qint64 position() const override
- {
- return mPosition;
- }
-
- qint64 duration() const override
- {
- return (sizeof(mSerial) * MOCK_DECODER_MAX_BUFFERS * qint64(1000)) / (mFormat.sampleRate() * mFormat.channelCount());
- }
+ void start() override;
+
+ void stop() override;
+
+ QAudioBuffer read() override;
+
+ bool bufferAvailable() const override;
+
+ qint64 position() const override;
+
+ qint64 duration() const override;
private slots:
- void pretendDecode()
- {
- // Check if we've reached end of stream
- if (mSerial >= MOCK_DECODER_MAX_BUFFERS)
- return;
-
- // We just keep the length of mBuffers to 3 or less.
- if (mBuffers.length() < 3) {
- QByteArray b(sizeof(mSerial), 0);
- memcpy(b.data(), &mSerial, sizeof(mSerial));
- qint64 position = (sizeof(mSerial) * mSerial * qint64(1000000)) / (mFormat.sampleRate() * mFormat.channelCount());
- mSerial++;
- mBuffers.push_back(QAudioBuffer(b, mFormat, position));
- emit bufferReady();
- if (mBuffers.count() == 1)
- emit bufferAvailableChanged(true);
- }
- }
+ void pretendDecode();
public:
QUrl mSource;
diff --git a/tests/auto/unit/mockbackend/qmockaudiooutput.h b/tests/auto/unit/mockbackend/qmockaudiooutput.h
index 30cc46604..a35fc8fe4 100644
--- a/tests/auto/unit/mockbackend/qmockaudiooutput.h
+++ b/tests/auto/unit/mockbackend/qmockaudiooutput.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QMOCKAUDIOOUTPUT_H
#define QMOCKAUDIOOUTPUT_H
diff --git a/tests/auto/unit/mockbackend/qmockcamera.cpp b/tests/auto/unit/mockbackend/qmockcamera.cpp
new file mode 100644
index 000000000..9347e8c84
--- /dev/null
+++ b/tests/auto/unit/mockbackend/qmockcamera.cpp
@@ -0,0 +1,164 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qmockcamera.h"
+
+QT_BEGIN_NAMESPACE
+
+QMockCamera::QMockCamera(QCamera *parent)
+ : QPlatformCamera(parent), m_propertyChangesSupported(false)
+{
+ if (!simpleCamera) {
+ minIsoChanged(100);
+ maxIsoChanged(800);
+ minExposureTimeChanged(.001f);
+ maxExposureTimeChanged(1.f);
+ exposureCompensationRangeChanged(-2, 2);
+ maximumZoomFactorChanged(4.);
+ setFlashMode(QCamera::FlashAuto);
+ }
+}
+
+QMockCamera::~QMockCamera() { }
+
+bool QMockCamera::isActive() const
+{
+ return m_active;
+}
+
+void QMockCamera::setActive(bool active)
+{
+ if (m_active == active)
+ return;
+ m_active = active;
+ emit activeChanged(active);
+}
+
+/* helper method to emit the signal error */
+void QMockCamera::setError(QCamera::Error err, QString errorString)
+{
+ emit error(err, errorString);
+}
+
+void QMockCamera::setCamera(const QCameraDevice &camera)
+{
+ m_camera = camera;
+}
+
+bool QMockCamera::setCameraFormat(const QCameraFormat &format)
+{
+ if (!format.isNull() && !m_camera.videoFormats().contains(format))
+ return false;
+ return true;
+}
+
+void QMockCamera::setFocusMode(QCamera::FocusMode mode)
+{
+ if (isFocusModeSupported(mode))
+ focusModeChanged(mode);
+}
+
+bool QMockCamera::isFocusModeSupported(QCamera::FocusMode mode) const
+{
+ return simpleCamera ? mode == QCamera::FocusModeAuto : mode != QCamera::FocusModeInfinity;
+}
+
+void QMockCamera::setCustomFocusPoint(const QPointF &point)
+{
+ if (!simpleCamera)
+ customFocusPointChanged(point);
+}
+
+void QMockCamera::setFocusDistance(float d)
+{
+ if (!simpleCamera)
+ focusDistanceChanged(d);
+}
+
+void QMockCamera::zoomTo(float newZoomFactor, float /*rate*/)
+{
+ zoomFactorChanged(newZoomFactor);
+}
+
+void QMockCamera::setFlashMode(QCamera::FlashMode mode)
+{
+ if (!simpleCamera)
+ flashModeChanged(mode);
+ flashReadyChanged(mode != QCamera::FlashOff);
+}
+bool QMockCamera::isFlashModeSupported(QCamera::FlashMode mode) const
+{
+ return simpleCamera ? mode == QCamera::FlashOff : true;
+}
+
+bool QMockCamera::isFlashReady() const
+{
+ return flashMode() != QCamera::FlashOff;
+}
+
+void QMockCamera::setExposureMode(QCamera::ExposureMode mode)
+{
+ if (!simpleCamera && isExposureModeSupported(mode))
+ exposureModeChanged(mode);
+}
+
+bool QMockCamera::isExposureModeSupported(QCamera::ExposureMode mode) const
+{
+ return simpleCamera ? mode == QCamera::ExposureAuto : mode <= QCamera::ExposureBeach;
+}
+
+void QMockCamera::setExposureCompensation(float c)
+{
+ if (!simpleCamera)
+ exposureCompensationChanged(qBound(-2., c, 2.));
+}
+
+int QMockCamera::isoSensitivity() const
+{
+ if (simpleCamera)
+ return -1;
+ return manualIsoSensitivity() > 0 ? manualIsoSensitivity() : 100;
+}
+
+void QMockCamera::setManualIsoSensitivity(int iso)
+{
+ if (!simpleCamera)
+ isoSensitivityChanged(qBound(100, iso, 800));
+}
+
+void QMockCamera::setManualExposureTime(float secs)
+{
+ if (!simpleCamera)
+ exposureTimeChanged(qBound(0.001, secs, 1.));
+}
+
+float QMockCamera::exposureTime() const
+{
+ if (simpleCamera)
+ return -1.;
+ return manualExposureTime() > 0 ? manualExposureTime() : .05;
+}
+
+bool QMockCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const
+{
+ if (simpleCamera)
+ return mode == QCamera::WhiteBalanceAuto;
+ return mode == QCamera::WhiteBalanceAuto || mode == QCamera::WhiteBalanceManual
+ || mode == QCamera::WhiteBalanceSunlight;
+}
+
+void QMockCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode)
+{
+ if (isWhiteBalanceModeSupported(mode))
+ whiteBalanceModeChanged(mode);
+}
+
+void QMockCamera::setColorTemperature(int temperature)
+{
+ if (!simpleCamera)
+ colorTemperatureChanged(temperature);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qmockcamera.cpp"
diff --git a/tests/auto/unit/mockbackend/qmockcamera.h b/tests/auto/unit/mockbackend/qmockcamera.h
index 69251e1a6..df8a08874 100644
--- a/tests/auto/unit/mockbackend/qmockcamera.h
+++ b/tests/auto/unit/mockbackend/qmockcamera.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QMOCKCAMERA_H
#define QMOCKCAMERA_H
@@ -48,135 +23,56 @@ public:
~Simple() { simpleCamera = false; }
};
- QMockCamera(QCamera *parent)
- : QPlatformCamera(parent),
- m_propertyChangesSupported(false)
- {
- if (!simpleCamera) {
- minIsoChanged(100);
- maxIsoChanged(800);
- minExposureTimeChanged(.001f);
- maxExposureTimeChanged(1.f);
- exposureCompensationRangeChanged(-2, 2);
- maximumZoomFactorChanged(4.);
- setFlashMode(QCamera::FlashAuto);
- }
- }
-
- ~QMockCamera() {}
-
- bool isActive() const override { return m_active; }
- void setActive(bool active) override {
- if (m_active == active)
- return;
- m_active = active;
- emit activeChanged(active);
- }
+ QMockCamera(QCamera *parent);
+
+ ~QMockCamera() override;
+
+ bool isActive() const override;
+
+ void setActive(bool active) override;
/* helper method to emit the signal error */
- void setError(QCamera::Error err, QString errorString)
- {
- emit error(err, errorString);
- }
-
- void setCamera(const QCameraDevice &camera) override
- {
- m_camera = camera;
- }
-
- bool setCameraFormat(const QCameraFormat& format) override
- {
- if (!format.isNull() && !m_camera.videoFormats().contains(format))
- return false;
- return true;
- }
-
- void setFocusMode(QCamera::FocusMode mode) override
- {
- if (isFocusModeSupported(mode))
- focusModeChanged(mode);
- }
- bool isFocusModeSupported(QCamera::FocusMode mode) const override
- { return simpleCamera ? mode == QCamera::FocusModeAuto : mode != QCamera::FocusModeInfinity; }
-
- void setCustomFocusPoint(const QPointF &point) override
- {
- if (!simpleCamera)
- customFocusPointChanged(point);
- }
-
- void setFocusDistance(float d) override
- {
- if (!simpleCamera)
- focusDistanceChanged(d);
- }
-
- void zoomTo(float newZoomFactor, float /*rate*/) override { zoomFactorChanged(newZoomFactor); }
-
- void setFlashMode(QCamera::FlashMode mode) override
- {
- if (!simpleCamera)
- flashModeChanged(mode);
- flashReadyChanged(mode != QCamera::FlashOff);
- }
- bool isFlashModeSupported(QCamera::FlashMode mode) const override { return simpleCamera ? mode == QCamera::FlashOff : true; }
- bool isFlashReady() const override { return flashMode() != QCamera::FlashOff; }
-
- void setExposureMode(QCamera::ExposureMode mode) override
- {
- if (!simpleCamera && isExposureModeSupported(mode))
- exposureModeChanged(mode);
- }
- bool isExposureModeSupported(QCamera::ExposureMode mode) const override
- {
- return simpleCamera ? mode == QCamera::ExposureAuto : mode <= QCamera::ExposureBeach;
- }
- void setExposureCompensation(float c) override
- {
- if (!simpleCamera)
- exposureCompensationChanged(qBound(-2., c, 2.));
- }
- int isoSensitivity() const override
- {
- if (simpleCamera)
- return -1;
- return manualIsoSensitivity() > 0 ? manualIsoSensitivity() : 100;
- }
- void setManualIsoSensitivity(int iso) override
- {
- if (!simpleCamera)
- isoSensitivityChanged(qBound(100, iso, 800));
- }
- void setManualExposureTime(float secs) override
- {
- if (!simpleCamera)
- exposureTimeChanged(qBound(0.001, secs, 1.));
- }
- float exposureTime() const override
- {
- if (simpleCamera)
- return -1.;
- return manualExposureTime() > 0 ? manualExposureTime() : .05;
- }
-
- bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override
- {
- if (simpleCamera)
- return mode == QCamera::WhiteBalanceAuto;
- return mode == QCamera::WhiteBalanceAuto ||
- mode == QCamera::WhiteBalanceManual ||
- mode == QCamera::WhiteBalanceSunlight;
- }
- void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override
- {
- if (isWhiteBalanceModeSupported(mode))
- whiteBalanceModeChanged(mode);
- }
- void setColorTemperature(int temperature) override
- {
- if (!simpleCamera)
- colorTemperatureChanged(temperature);
- }
+ void setError(QCamera::Error err, QString errorString);
+
+ void setCamera(const QCameraDevice &camera) override;
+
+ bool setCameraFormat(const QCameraFormat &format) override;
+
+ void setFocusMode(QCamera::FocusMode mode) override;
+
+ bool isFocusModeSupported(QCamera::FocusMode mode) const override;
+
+ void setCustomFocusPoint(const QPointF &point) override;
+
+ void setFocusDistance(float d) override;
+
+ void zoomTo(float newZoomFactor, float /*rate*/) override;
+
+ void setFlashMode(QCamera::FlashMode mode) override;
+
+ bool isFlashModeSupported(QCamera::FlashMode mode) const override;
+
+ bool isFlashReady() const override;
+
+ void setExposureMode(QCamera::ExposureMode mode) override;
+
+ bool isExposureModeSupported(QCamera::ExposureMode mode) const override;
+
+ void setExposureCompensation(float c) override;
+
+ int isoSensitivity() const override;
+
+ void setManualIsoSensitivity(int iso) override;
+
+ void setManualExposureTime(float secs) override;
+
+ float exposureTime() const override;
+
+ bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override;
+
+ void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override;
+
+ void setColorTemperature(int temperature) override;
bool m_active = false;
QCameraDevice m_camera;
diff --git a/tests/auto/unit/mockbackend/qmockimagecapture.cpp b/tests/auto/unit/mockbackend/qmockimagecapture.cpp
index 9e2396113..96e53b2f4 100644
--- a/tests/auto/unit/mockbackend/qmockimagecapture.cpp
+++ b/tests/auto/unit/mockbackend/qmockimagecapture.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qmockimagecapture.h>
#include <qmockcamera.h>
@@ -32,6 +7,8 @@
#include <qimagecapture.h>
#include <qcamera.h>
+QT_BEGIN_NAMESPACE
+
QMockImageCapture::QMockImageCapture(QImageCapture *parent)
: QPlatformImageCapture(parent)
{
@@ -48,7 +25,7 @@ int QMockImageCapture::capture(const QString &fileName)
m_fileName = fileName;
m_captureRequest++;
emit readyForCaptureChanged(m_ready = false);
- QTimer::singleShot(5, this, SLOT(captured()));
+ QTimer::singleShot(5, this, &QMockImageCapture::captured);
return m_captureRequest;
} else {
emit error(-1, QImageCapture::NotReadyError,
@@ -63,7 +40,7 @@ void QMockImageCapture::captured()
emit imageCaptured(m_captureRequest, QImage());
QMediaMetaData metaData;
- metaData.insert(QMediaMetaData::Author, QString::fromUtf8("Author"));
+ metaData.insert(QMediaMetaData::Author, QStringLiteral("Author"));
metaData.insert(QMediaMetaData::Date, QDateTime(QDate(2021, 1, 1), QTime()));
emit imageMetadataAvailable(m_captureRequest, metaData);
@@ -76,3 +53,7 @@ void QMockImageCapture::captured()
emit imageSaved(m_captureRequest, m_fileName);
}
+
+QT_END_NAMESPACE
+
+#include "moc_qmockimagecapture.cpp"
diff --git a/tests/auto/unit/mockbackend/qmockimagecapture.h b/tests/auto/unit/mockbackend/qmockimagecapture.h
index 8d7cea1a4..376f53cbc 100644
--- a/tests/auto/unit/mockbackend/qmockimagecapture.h
+++ b/tests/auto/unit/mockbackend/qmockimagecapture.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QMOCKCAMERAIMAGECAPTURE_H
#define QMOCKCAMERAIMAGECAPTURE_H
diff --git a/tests/auto/unit/mockbackend/qmockintegration.cpp b/tests/auto/unit/mockbackend/qmockintegration.cpp
index da4ac787f..b554b31e0 100644
--- a/tests/auto/unit/mockbackend/qmockintegration.cpp
+++ b/tests/auto/unit/mockbackend/qmockintegration.cpp
@@ -1,43 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qmockintegration_p.h"
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtMultimedia/private/qplatformmediaplugin_p.h>
+#include "qmockintegration.h"
#include "qmockmediaplayer.h"
#include "qmockaudiodecoder.h"
#include "qmockcamera.h"
@@ -45,20 +10,106 @@
#include "qmockvideosink.h"
#include "qmockimagecapture.h"
#include "qmockaudiooutput.h"
+#include "qmocksurfacecapture.h"
+#include <private/qcameradevice_p.h>
+#include <private/qplatformvideodevices_p.h>
+
+#include "qmockmediadevices.h"
QT_BEGIN_NAMESPACE
-QMockIntegration::QMockIntegration()
+class MockMultimediaPlugin : public QPlatformMediaPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformMediaPlugin_iid FILE "mock.json")
+
+public:
+ MockMultimediaPlugin() : QPlatformMediaPlugin() { }
+
+ QPlatformMediaIntegration *create(const QString &name) override
+ {
+ if (name == QLatin1String("mock"))
+ return new QMockIntegration;
+ return nullptr;
+ }
+};
+
+class QMockVideoDevices : public QPlatformVideoDevices
{
- setIntegration(this);
+public:
+ QMockVideoDevices(QPlatformMediaIntegration *pmi)
+ : QPlatformVideoDevices(pmi)
+ {
+ QCameraDevicePrivate *info = new QCameraDevicePrivate;
+ info->description = QStringLiteral("defaultCamera");
+ info->id = "default";
+ info->isDefault = true;
+ auto *f = new QCameraFormatPrivate{
+ QSharedData(),
+ QVideoFrameFormat::Format_ARGB8888,
+ QSize(640, 480),
+ 0,
+ 30
+ };
+ info->videoFormats << f->create();
+ m_cameraDevices.append(info->create());
+ info = new QCameraDevicePrivate;
+ info->description = QStringLiteral("frontCamera");
+ info->id = "front";
+ info->isDefault = false;
+ info->position = QCameraDevice::FrontFace;
+ f = new QCameraFormatPrivate{
+ QSharedData(),
+ QVideoFrameFormat::Format_XRGB8888,
+ QSize(1280, 720),
+ 0,
+ 30
+ };
+ info->videoFormats << f->create();
+ m_cameraDevices.append(info->create());
+ info = new QCameraDevicePrivate;
+ info->description = QStringLiteral("backCamera");
+ info->id = "back";
+ info->isDefault = false;
+ info->position = QCameraDevice::BackFace;
+ m_cameraDevices.append(info->create());
+ }
+
+ void addNewCamera()
+ {
+ auto info = new QCameraDevicePrivate;
+ info->description = QLatin1String("newCamera") + QString::number(m_cameraDevices.size());
+ info->id =
+ QString(QLatin1String("camera") + QString::number(m_cameraDevices.size())).toUtf8();
+ info->isDefault = false;
+ m_cameraDevices.append(info->create());
+
+ emit videoInputsChanged();
+ }
+
+ QList<QCameraDevice> videoDevices() const override
+ {
+ return m_cameraDevices;
+ }
+
+private:
+ QList<QCameraDevice> m_cameraDevices;
+};
+
+QMockIntegration::QMockIntegration() : QPlatformMediaIntegration(QLatin1String("mock")) { }
+QMockIntegration::~QMockIntegration() = default;
+
+QPlatformVideoDevices *QMockIntegration::createVideoDevices()
+{
+ return new QMockVideoDevices(this);
}
-QMockIntegration::~QMockIntegration()
+std::unique_ptr<QPlatformMediaDevices> QMockIntegration::createMediaDevices()
{
- setIntegration(nullptr);
+ return std::make_unique<QMockMediaDevices>();
}
-QPlatformAudioDecoder *QMockIntegration::createAudioDecoder(QAudioDecoder *decoder)
+QMaybe<QPlatformAudioDecoder *> QMockIntegration::createAudioDecoder(QAudioDecoder *decoder)
{
if (m_flags & NoAudioDecoderInterface)
m_lastAudioDecoderControl = nullptr;
@@ -67,7 +118,7 @@ QPlatformAudioDecoder *QMockIntegration::createAudioDecoder(QAudioDecoder *decod
return m_lastAudioDecoderControl;
}
-QPlatformMediaPlayer *QMockIntegration::createPlayer(QMediaPlayer *parent)
+QMaybe<QPlatformMediaPlayer *> QMockIntegration::createPlayer(QMediaPlayer *parent)
{
if (m_flags & NoPlayerInterface)
m_lastPlayer = nullptr;
@@ -76,7 +127,7 @@ QPlatformMediaPlayer *QMockIntegration::createPlayer(QMediaPlayer *parent)
return m_lastPlayer;
}
-QPlatformCamera *QMockIntegration::createCamera(QCamera *parent)
+QMaybe<QPlatformCamera *> QMockIntegration::createCamera(QCamera *parent)
{
if (m_flags & NoCaptureInterface)
m_lastCamera = nullptr;
@@ -85,17 +136,37 @@ QPlatformCamera *QMockIntegration::createCamera(QCamera *parent)
return m_lastCamera;
}
-QPlatformImageCapture *QMockIntegration::createImageCapture(QImageCapture *capture)
+QMaybe<QPlatformImageCapture *> QMockIntegration::createImageCapture(QImageCapture *capture)
{
return new QMockImageCapture(capture);
}
-QPlatformMediaRecorder *QMockIntegration::createRecorder(QMediaRecorder *recorder)
+QMaybe<QPlatformMediaRecorder *> QMockIntegration::createRecorder(QMediaRecorder *recorder)
{
return new QMockMediaEncoder(recorder);
}
-QPlatformMediaCaptureSession *QMockIntegration::createCaptureSession()
+QPlatformSurfaceCapture *QMockIntegration::createScreenCapture(QScreenCapture * /*capture*/)
+{
+ if (m_flags & NoCaptureInterface)
+ m_lastScreenCapture = nullptr;
+ else
+ m_lastScreenCapture = new QMockSurfaceCapture(QPlatformSurfaceCapture::ScreenSource{});
+
+ return m_lastScreenCapture;
+}
+
+QPlatformSurfaceCapture *QMockIntegration::createWindowCapture(QWindowCapture *)
+{
+ if (m_flags & NoCaptureInterface)
+ m_lastWindowCapture = nullptr;
+ else
+ m_lastWindowCapture = new QMockSurfaceCapture(QPlatformSurfaceCapture::WindowSource{});
+
+ return m_lastWindowCapture;
+}
+
+QMaybe<QPlatformMediaCaptureSession *> QMockIntegration::createCaptureSession()
{
if (m_flags & NoCaptureInterface)
m_lastCaptureService = nullptr;
@@ -104,17 +175,24 @@ QPlatformMediaCaptureSession *QMockIntegration::createCaptureSession()
return m_lastCaptureService;
}
-QPlatformVideoSink *QMockIntegration::createVideoSink(QVideoSink *sink)
+QMaybe<QPlatformVideoSink *> QMockIntegration::createVideoSink(QVideoSink *sink)
{
m_lastVideoSink = new QMockVideoSink(sink);
return m_lastVideoSink;
}
-QPlatformAudioOutput *QMockIntegration::createAudioOutput(QAudioOutput *q)
+QMaybe<QPlatformAudioOutput *> QMockIntegration::createAudioOutput(QAudioOutput *q)
{
return new QMockAudioOutput(q);
}
+void QMockIntegration::addNewCamera()
+{
+ static_cast<QMockVideoDevices *>(videoDevices())->addNewCamera();
+}
+
bool QMockCamera::simpleCamera = false;
QT_END_NAMESPACE
+
+#include "qmockintegration.moc"
diff --git a/tests/auto/unit/mockbackend/qmockintegration.h b/tests/auto/unit/mockbackend/qmockintegration.h
new file mode 100644
index 000000000..20b61721c
--- /dev/null
+++ b/tests/auto/unit/mockbackend/qmockintegration.h
@@ -0,0 +1,103 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QMOCKINTEGRATION_H
+#define QMOCKINTEGRATION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformmediaintegration_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMockMediaPlayer;
+class QMockAudioDecoder;
+class QMockCamera;
+class QMockMediaCaptureSession;
+class QMockVideoSink;
+class QMockSurfaceCapture;
+
+class QMockIntegration : public QPlatformMediaIntegration
+{
+public:
+ QMockIntegration();
+ ~QMockIntegration();
+
+ static QMockIntegration *instance()
+ {
+ return static_cast<QMockIntegration *>(QPlatformMediaIntegration::instance());
+ }
+
+ QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *decoder) override;
+ QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *) override;
+ QMaybe<QPlatformCamera *> createCamera(QCamera *) override;
+ QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *) override;
+ QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *) override;
+ QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() override;
+ QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *) override;
+
+ QMaybe<QPlatformAudioOutput *> createAudioOutput(QAudioOutput *) override;
+
+ QPlatformSurfaceCapture *createScreenCapture(QScreenCapture *) override;
+ QPlatformSurfaceCapture *createWindowCapture(QWindowCapture *) override;
+
+ void addNewCamera();
+
+ enum Flag { NoPlayerInterface = 0x1, NoAudioDecoderInterface = 0x2, NoCaptureInterface = 0x4 };
+ Q_DECLARE_FLAGS(Flags, Flag);
+
+ void setFlags(Flags f) { m_flags = f; }
+ Flags flags() const { return m_flags; }
+
+ QMockMediaPlayer *lastPlayer() const { return m_lastPlayer; }
+ QMockAudioDecoder *lastAudioDecoder() const { return m_lastAudioDecoderControl; }
+ QMockCamera *lastCamera() const { return m_lastCamera; }
+ // QMockMediaEncoder *lastEncoder const { return m_lastEncoder; }
+ QMockMediaCaptureSession *lastCaptureService() const { return m_lastCaptureService; }
+ QMockVideoSink *lastVideoSink() const { return m_lastVideoSink; }
+ QMockSurfaceCapture *lastScreenCapture() { return m_lastScreenCapture; }
+ QMockSurfaceCapture *lastWindowCapture() { return m_lastWindowCapture; }
+
+protected:
+ QPlatformVideoDevices *createVideoDevices() override;
+ std::unique_ptr<QPlatformMediaDevices> createMediaDevices() override;
+
+private:
+
+ Flags m_flags = {};
+ QMockMediaPlayer *m_lastPlayer = nullptr;
+ QMockAudioDecoder *m_lastAudioDecoderControl = nullptr;
+ QMockCamera *m_lastCamera = nullptr;
+ // QMockMediaEncoder *m_lastEncoder = nullptr;
+ QMockMediaCaptureSession *m_lastCaptureService = nullptr;
+ QMockVideoSink *m_lastVideoSink = nullptr;
+ QMockSurfaceCapture *m_lastScreenCapture = nullptr;
+ QMockSurfaceCapture *m_lastWindowCapture = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QMockIntegration::Flags);
+
+#define Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN \
+ Q_IMPORT_PLUGIN(MockMultimediaPlugin) \
+ struct EnableMockPlugin \
+ { \
+ EnableMockPlugin() \
+ { \
+ qputenv("QT_MEDIA_BACKEND", "mock"); \
+ } \
+ }; \
+ static EnableMockPlugin s_mockMultimediaPluginEnabler;
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/unit/mockbackend/qmockintegration_p.h b/tests/auto/unit/mockbackend/qmockintegration_p.h
deleted file mode 100644
index f5a6d167e..000000000
--- a/tests/auto/unit/mockbackend/qmockintegration_p.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QMOCKINTEGRATION_H
-#define QMOCKINTEGRATION_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <private/qplatformmediaintegration_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QMockMediaPlayer;
-class QMockAudioDecoder;
-class QMockCamera;
-class QMockMediaCaptureSession;
-class QMockVideoSink;
-
-class QMockIntegration : public QPlatformMediaIntegration
-{
-public:
- QMockIntegration();
- ~QMockIntegration();
-
- QPlatformMediaFormatInfo *formatInfo() override { return nullptr; }
-
- QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *decoder) override;
- QPlatformMediaPlayer *createPlayer(QMediaPlayer *) override;
- QPlatformCamera *createCamera(QCamera *) override;
- QPlatformMediaRecorder *createRecorder(QMediaRecorder *) override;
- QPlatformImageCapture *createImageCapture(QImageCapture *) override;
- QPlatformMediaCaptureSession *createCaptureSession() override;
- QPlatformVideoSink *createVideoSink(QVideoSink *) override;
-
- QPlatformAudioOutput *createAudioOutput(QAudioOutput *) override;
-
- enum Flag {
- NoPlayerInterface = 0x1,
- NoAudioDecoderInterface = 0x2,
- NoCaptureInterface = 0x4
- };
- Q_DECLARE_FLAGS(Flags, Flag);
-
- void setFlags(Flags f) { m_flags = f; }
- Flags flags() const { return m_flags; }
-
- QMockMediaPlayer *lastPlayer() const { return m_lastPlayer; }
- QMockAudioDecoder *lastAudioDecoder() const { return m_lastAudioDecoderControl; }
- QMockCamera *lastCamera() const { return m_lastCamera; }
- // QMockMediaEncoder *lastEncoder const { return m_lastEncoder; }
- QMockMediaCaptureSession *lastCaptureService() const { return m_lastCaptureService; }
- QMockVideoSink *lastVideoSink() const { return m_lastVideoSink; }
-
-private:
- Flags m_flags = {};
- QMockMediaPlayer *m_lastPlayer = nullptr;
- QMockAudioDecoder *m_lastAudioDecoderControl = nullptr;
- QMockCamera *m_lastCamera = nullptr;
- // QMockMediaEncoder *m_lastEncoder = nullptr;
- QMockMediaCaptureSession *m_lastCaptureService = nullptr;
- QMockVideoSink *m_lastVideoSink;
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(QMockIntegration::Flags);
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/tests/auto/unit/mockbackend/qmockmediacapturesession.h b/tests/auto/unit/mockbackend/qmockmediacapturesession.h
index c9e31fb56..24453d795 100644
--- a/tests/auto/unit/mockbackend/qmockmediacapturesession.h
+++ b/tests/auto/unit/mockbackend/qmockmediacapturesession.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QMOCKMEDIACAPTURESESSION_H
#define QMOCKMEDIACAPTURESESSION_H
@@ -33,13 +8,13 @@
#include "qmockimagecapture.h"
#include "qmockcamera.h"
#include "qmockimagecapture.h"
+#include "qmocksurfacecapture.h"
#include <private/qplatformmediacapture_p.h>
QT_BEGIN_NAMESPACE
class QMockMediaCaptureSession : public QPlatformMediaCaptureSession
{
- Q_OBJECT
public:
QMockMediaCaptureSession()
: hasControls(true)
@@ -87,10 +62,14 @@ public:
m_audioInput = input;
}
+ QPlatformSurfaceCapture *screenCapture() override { return m_screenCapture; }
+ void setScreenCapture(QPlatformSurfaceCapture *capture) override { m_screenCapture = capture; }
+
QMockCamera *mockCameraControl = nullptr;
QPlatformImageCapture *mockImageCapture = nullptr;
QMockMediaEncoder *mockControl = nullptr;
QPlatformAudioInput *m_audioInput = nullptr;
+ QPlatformSurfaceCapture *m_screenCapture = nullptr;
bool hasControls;
};
diff --git a/tests/auto/unit/mockbackend/qmockmediadevices.cpp b/tests/auto/unit/mockbackend/qmockmediadevices.cpp
index 216991925..7f2478741 100644
--- a/tests/auto/unit/mockbackend/qmockmediadevices.cpp
+++ b/tests/auto/unit/mockbackend/qmockmediadevices.cpp
@@ -1,43 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include "qmockmediadevices_p.h"
+#include "qmockmediadevices.h"
#include "private/qcameradevice_p.h"
QT_BEGIN_NAMESPACE
@@ -45,42 +9,6 @@ QT_BEGIN_NAMESPACE
QMockMediaDevices::QMockMediaDevices()
: QPlatformMediaDevices()
{
- setDevices(this);
-
- QCameraDevicePrivate *info = new QCameraDevicePrivate;
- info->description = QString::fromUtf8("defaultCamera");
- info->id = "default";
- info->isDefault = true;
- auto *f = new QCameraFormatPrivate{
- QSharedData(),
- QVideoFrameFormat::Format_ARGB8888,
- QSize(640, 480),
- 0,
- 30
- };
- info->videoFormats << f->create();
- m_cameraDevices.append(info->create());
- info = new QCameraDevicePrivate;
- info->description = QString::fromUtf8("frontCamera");
- info->id = "front";
- info->isDefault = false;
- info->position = QCameraDevice::FrontFace;
- f = new QCameraFormatPrivate{
- QSharedData(),
- QVideoFrameFormat::Format_XRGB8888,
- QSize(1280, 720),
- 0,
- 30
- };
- info->videoFormats << f->create();
- m_cameraDevices.append(info->create());
- info = new QCameraDevicePrivate;
- info->description = QString::fromUtf8("backCamera");
- info->id = "back";
- info->isDefault = false;
- info->position = QCameraDevice::BackFace;
- m_cameraDevices.append(info->create());
-
}
QMockMediaDevices::~QMockMediaDevices() = default;
@@ -95,20 +23,19 @@ QList<QAudioDevice> QMockMediaDevices::audioOutputs() const
return m_outputDevices;
}
-QList<QCameraDevice> QMockMediaDevices::videoInputs() const
-{
- return m_cameraDevices;
-}
-
-QPlatformAudioSource *QMockMediaDevices::createAudioSource(const QAudioDevice &info)
+QPlatformAudioSource *QMockMediaDevices::createAudioSource(const QAudioDevice &info,
+ QObject *parent)
{
Q_UNUSED(info);
+ Q_UNUSED(parent);
return nullptr;// ###
}
-QPlatformAudioSink *QMockMediaDevices::createAudioSink(const QAudioDevice &info)
+QPlatformAudioSink *QMockMediaDevices::createAudioSink(const QAudioDevice &info,
+ QObject *parent)
{
Q_UNUSED(info);
+ Q_UNUSED(parent);
return nullptr; //###
}
diff --git a/tests/auto/unit/mockbackend/qmockmediadevices.h b/tests/auto/unit/mockbackend/qmockmediadevices.h
new file mode 100644
index 000000000..e9e823194
--- /dev/null
+++ b/tests/auto/unit/mockbackend/qmockmediadevices.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QMOCKMEDIADEVICES_H
+#define QMOCKMEDIADEVICES_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qplatformmediadevices_p.h>
+#include <qelapsedtimer.h>
+#include <qaudiodevice.h>
+#include <qcameradevice.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCameraDevice;
+
+class QMockMediaDevices : public QPlatformMediaDevices
+{
+public:
+ QMockMediaDevices();
+ ~QMockMediaDevices();
+
+ QList<QAudioDevice> audioInputs() const override;
+ QList<QAudioDevice> audioOutputs() const override;
+ QPlatformAudioSource *createAudioSource(const QAudioDevice &info, QObject *parent) override;
+ QPlatformAudioSink *createAudioSink(const QAudioDevice &info, QObject *parent) override;
+
+private:
+ QList<QAudioDevice> m_inputDevices;
+ QList<QAudioDevice> m_outputDevices;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/unit/mockbackend/qmockmediadevices_p.h b/tests/auto/unit/mockbackend/qmockmediadevices_p.h
deleted file mode 100644
index cfa0398f3..000000000
--- a/tests/auto/unit/mockbackend/qmockmediadevices_p.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QMOCKMEDIADEVICES_H
-#define QMOCKMEDIADEVICES_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <private/qplatformmediadevices_p.h>
-#include <qelapsedtimer.h>
-#include <qaudiodevice.h>
-#include <qcameradevice.h>
-
-QT_BEGIN_NAMESPACE
-
-class QCameraDevice;
-
-class QMockMediaDevices : public QPlatformMediaDevices
-{
-public:
- QMockMediaDevices();
- ~QMockMediaDevices();
-
- QList<QAudioDevice> audioInputs() const override;
- QList<QAudioDevice> audioOutputs() const override;
- QList<QCameraDevice> videoInputs() const override;
- QPlatformAudioSource *createAudioSource(const QAudioDevice &info) override;
- QPlatformAudioSink *createAudioSink(const QAudioDevice &info) override;
-
-private:
- QList<QAudioDevice> m_inputDevices;
- QList<QAudioDevice> m_outputDevices;
- QList<QCameraDevice> m_cameraDevices;
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/tests/auto/unit/mockbackend/qmockmediaencoder.h b/tests/auto/unit/mockbackend/qmockmediaencoder.h
index 88ef5b3f9..6cfea1bb0 100644
--- a/tests/auto/unit/mockbackend/qmockmediaencoder.h
+++ b/tests/auto/unit/mockbackend/qmockmediaencoder.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef MOCKRECORDERCONTROL_H
#define MOCKRECORDERCONTROL_H
@@ -66,7 +41,7 @@ public:
}
virtual QMediaMetaData metaData() const override { return m_metaData; }
- using QPlatformMediaRecorder::error;
+ using QPlatformMediaRecorder::updateError;
public:
void record(QMediaEncoderSettings &settings) override
diff --git a/tests/auto/unit/mockbackend/qmockmediaplayer.h b/tests/auto/unit/mockbackend/qmockmediaplayer.h
index aef736cf3..8db0b3a1a 100644
--- a/tests/auto/unit/mockbackend/qmockmediaplayer.h
+++ b/tests/auto/unit/mockbackend/qmockmediaplayer.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QMOCKMEDIAPLAYER_H
#define QMOCKMEDIAPLAYER_H
diff --git a/tests/auto/unit/mockbackend/qmocksurfacecapture.h b/tests/auto/unit/mockbackend/qmocksurfacecapture.h
new file mode 100644
index 000000000..0924b7255
--- /dev/null
+++ b/tests/auto/unit/mockbackend/qmocksurfacecapture.h
@@ -0,0 +1,86 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QMOCKSURFACECAPTURE_H
+#define QMOCKSURFACECAPTURE_H
+
+#include "private/qplatformsurfacecapture_p.h"
+
+#include "qmockvideobuffer.h"
+#include "qthread.h"
+
+QT_BEGIN_NAMESPACE
+
+class QMockSurfaceCapture : public QPlatformSurfaceCapture
+{
+ class Grabber : public QThread
+ {
+ public:
+ Grabber(QMockSurfaceCapture &capture) : QThread(&capture), m_capture(capture) { }
+
+ void run() override
+ {
+ for (int i = 0; !isInterruptionRequested(); ++i) {
+ QImage image(m_capture.m_imageSize, QImage::Format_ARGB32);
+
+ image.fill(i % 2 ? Qt::red : Qt::blue);
+
+ QVideoFrame frame(new QMockVideoBuffer(image),
+ QVideoFrameFormat(m_capture.m_imageSize,
+ QVideoFrameFormat::pixelFormatFromImageFormat(
+ m_capture.m_imageFormat)));
+
+ emit m_capture.newVideoFrame(frame);
+ }
+ }
+
+ private:
+ QMockSurfaceCapture &m_capture;
+ };
+
+public:
+ using QPlatformSurfaceCapture::QPlatformSurfaceCapture;
+
+ ~QMockSurfaceCapture() { resetGrabber(); }
+
+ bool setActiveInternal(bool active) override
+ {
+ if (active) {
+ m_grabber = std::make_unique<Grabber>(*this);
+ m_grabber->start();
+ } else {
+ resetGrabber();
+ }
+
+ return true;
+ }
+
+ bool isActive() const override { return bool(m_grabber); }
+
+ QVideoFrameFormat frameFormat() const override
+ {
+ return m_grabber ? QVideoFrameFormat(
+ m_imageSize, QVideoFrameFormat::pixelFormatFromImageFormat(m_imageFormat))
+ : QVideoFrameFormat{};
+ }
+
+private:
+ void resetGrabber()
+ {
+ if (m_grabber) {
+ m_grabber->requestInterruption();
+ m_grabber->quit();
+ m_grabber->wait();
+ m_grabber.reset();
+ }
+ }
+
+private:
+ std::unique_ptr<Grabber> m_grabber;
+ const QImage::Format m_imageFormat = QImage::Format_ARGB32;
+ const QSize m_imageSize = QSize(2, 3);
+};
+
+QT_END_NAMESPACE
+
+#endif // QMOCKSURFACECAPTURE_H
diff --git a/tests/auto/unit/mockbackend/qmockvideobuffer.h b/tests/auto/unit/mockbackend/qmockvideobuffer.h
new file mode 100644
index 000000000..f82a09a59
--- /dev/null
+++ b/tests/auto/unit/mockbackend/qmockvideobuffer.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QMOCKVIDEOBUFFER_H
+#define QMOCKVIDEOBUFFER_H
+
+#include "qimage.h"
+#include "private/qabstractvideobuffer_p.h"
+
+class QMockVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ QMockVideoBuffer(QImage image) : QAbstractVideoBuffer(QVideoFrame::NoHandle), m_image(image) { }
+
+ QVideoFrame::MapMode mapMode() const override { return m_mapMode; }
+
+ MapData map(QVideoFrame::MapMode mode) override
+ {
+ MapData mapData;
+ if (m_mapMode == QVideoFrame::NotMapped && !m_image.isNull()
+ && mode != QVideoFrame::NotMapped) {
+ m_mapMode = mode;
+
+ mapData.nPlanes = 1;
+ mapData.bytesPerLine[0] = m_image.bytesPerLine();
+ mapData.data[0] = m_image.bits();
+ mapData.size[0] = m_image.sizeInBytes();
+ }
+
+ return mapData;
+ }
+
+ void unmap() override { m_mapMode = QVideoFrame::NotMapped; }
+
+private:
+ QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped;
+ QImage m_image;
+};
+
+#endif // QMOCKVIDEOBUFFER_H
diff --git a/tests/auto/unit/mockbackend/qmockvideosink.h b/tests/auto/unit/mockbackend/qmockvideosink.h
index 0a8baeef0..d93178668 100644
--- a/tests/auto/unit/mockbackend/qmockvideosink.h
+++ b/tests/auto/unit/mockbackend/qmockvideosink.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QMOCKVIDEOSINK_H
#define QMOCKVIDEOSINK_H
@@ -57,8 +21,6 @@ QT_BEGIN_NAMESPACE
class QMockVideoSink : public QPlatformVideoSink
{
- Q_OBJECT
-
public:
explicit QMockVideoSink(QVideoSink *parent)
: QPlatformVideoSink(parent)
diff --git a/tests/auto/unit/multimedia/CMakeLists.txt b/tests/auto/unit/multimedia/CMakeLists.txt
index d95cab75a..f259691d0 100644
--- a/tests/auto/unit/multimedia/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/CMakeLists.txt
@@ -1,9 +1,13 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from multimedia.pro.
add_subdirectory(qabstractvideobuffer)
add_subdirectory(qaudiorecorder)
add_subdirectory(qaudioformat)
add_subdirectory(qaudionamespace)
+add_subdirectory(qaudiostatemachine)
add_subdirectory(qcamera)
add_subdirectory(qcameradevice)
add_subdirectory(qimagecapture)
@@ -12,8 +16,25 @@ add_subdirectory(qmediaplayer)
#add_subdirectory(qmediaplaylist)
add_subdirectory(qmediarecorder)
add_subdirectory(qmediatimerange)
+add_subdirectory(qmultimediautils)
add_subdirectory(qvideoframe)
add_subdirectory(qvideoframeformat)
+if(QT_FEATURE_ffmpeg)
+ add_subdirectory(qvideoframecolormanagement)
+endif()
add_subdirectory(qaudiobuffer)
add_subdirectory(qaudiodecoder)
add_subdirectory(qsamplecache)
+add_subdirectory(qscreencapture)
+add_subdirectory(qvideotexturehelper)
+add_subdirectory(qmaybe)
+add_subdirectory(qmediadevices)
+add_subdirectory(qerrorinfo)
+add_subdirectory(qvideobuffers)
+add_subdirectory(qwavedecoder)
+
+if(QT_FEATURE_gstreamer)
+ add_subdirectory(gstreamer_backend)
+ add_subdirectory(qmediacapture_gstreamer)
+ add_subdirectory(qmediaplayer_gstreamer)
+endif()
diff --git a/tests/auto/unit/multimedia/gstreamer_backend/CMakeLists.txt b/tests/auto/unit/multimedia/gstreamer_backend/CMakeLists.txt
new file mode 100644
index 000000000..6d52d09e1
--- /dev/null
+++ b/tests/auto/unit/multimedia/gstreamer_backend/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_gstreamer_backend Test:
+#####################################################################
+
+qt_internal_add_test(tst_gstreamer_backend
+ SOURCES
+ tst_gstreamer_backend.cpp
+ tst_gstreamer_backend.h
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::QGstreamerMediaPluginPrivate
+)
diff --git a/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.cpp b/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.cpp
new file mode 100644
index 000000000..2cecbe21b
--- /dev/null
+++ b/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.cpp
@@ -0,0 +1,156 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_gstreamer_backend.h"
+
+#include <QtTest/QtTest>
+
+#include <QtQGstreamerMediaPlugin/private/qgst_handle_types_p.h>
+#include <QtQGstreamerMediaPlugin/private/qgst_p.h>
+#include <QtQGstreamerMediaPlugin/private/qgstpipeline_p.h>
+#include <QtQGstreamerMediaPlugin/private/qgstreamermetadata_p.h>
+
+QT_USE_NAMESPACE
+
+using namespace Qt::Literals;
+
+QGstTagListHandle tst_GStreamer::parseTagList(const char *str)
+{
+ QGstTagListHandle tagList{
+ gst_tag_list_new_from_string(str),
+ QGstTagListHandle::NeedsRef,
+ };
+ return tagList;
+}
+
+QGstTagListHandle tst_GStreamer::parseTagList(const QByteArray &ba)
+{
+ return parseTagList(ba.constData());
+}
+
+void tst_GStreamer::qGstCasts_withElement()
+{
+ QGstElement element = QGstElement::createFromFactory("identity", "myPipeline");
+ QVERIFY(element);
+
+ QVERIFY(!qIsGstObjectOfType<GstPipeline>(element.element()));
+ QVERIFY(!qIsGstObjectOfType<GstBin>(element.element()));
+}
+
+void tst_GStreamer::qGstCasts_withBin()
+{
+ QGstBin bin = QGstBin::create("bin");
+ QVERIFY(bin);
+
+ QVERIFY(!qIsGstObjectOfType<GstPipeline>(bin.element()));
+ QVERIFY(qIsGstObjectOfType<GstBin>(bin.element()));
+}
+
+void tst_GStreamer::qGstCasts_withPipeline()
+{
+ QGstPipeline pipeline = QGstPipeline::create("myPipeline");
+
+ QGstElement element{
+ qGstSafeCast<GstElement>(pipeline.pipeline()),
+ QGstElement::NeedsRef,
+ };
+
+ QVERIFY(element);
+ QVERIFY(qIsGstObjectOfType<GstPipeline>(element.element()));
+ QVERIFY(qIsGstObjectOfType<GstBin>(element.element()));
+}
+
+void tst_GStreamer::metadata_taglistToMetaData()
+{
+ QGstTagListHandle tagList = parseTagList(R"(taglist, title="My Video", comment="yada")");
+
+ QMediaMetaData parsed = taglistToMetaData(tagList);
+
+ QCOMPARE(parsed.stringValue(QMediaMetaData::Title), u"My Video"_s);
+ QCOMPARE(parsed.stringValue(QMediaMetaData::Comment), u"yada"_s);
+}
+
+void tst_GStreamer::metadata_taglistToMetaData_extractsOrientation()
+{
+ QFETCH(QByteArray, taglist);
+ QFETCH(QtVideo::Rotation, rotation);
+
+ QGstTagListHandle tagList = parseTagList(taglist);
+ QMediaMetaData parsed = taglistToMetaData(tagList);
+ QCOMPARE(parsed[QMediaMetaData::Orientation].value<QtVideo::Rotation>(), rotation);
+}
+
+void tst_GStreamer::metadata_taglistToMetaData_extractsOrientation_data()
+{
+ QTest::addColumn<QByteArray>("taglist");
+ QTest::addColumn<QtVideo::Rotation>("rotation");
+
+ QTest::newRow("no rotation") << R"(taglist, title="My Video", comment="yada")"_ba
+ << QtVideo::Rotation::None;
+ QTest::newRow("90 degree")
+ << R"(taglist, title="My Video", comment="yada", image-orientation=(string)rotate-90)"_ba
+ << QtVideo::Rotation::Clockwise90;
+ QTest::newRow("180 degree")
+ << R"(taglist, title="My Video", comment="yada", image-orientation=(string)rotate-180)"_ba
+ << QtVideo::Rotation::Clockwise180;
+ QTest::newRow("270 degree")
+ << R"(taglist, title="My Video", comment="yada", image-orientation=(string)rotate-270)"_ba
+ << QtVideo::Rotation::Clockwise270;
+}
+
+void tst_GStreamer::metadata_taglistToMetaData_extractsDuration()
+{
+ QGstTagListHandle tagList = parseTagList(
+ R"__(taglist, video-codec=(string)"On2\ VP9", container-specific-track-id=(string)1, extended-comment=(string){ "ALPHA_MODE\=1", "HANDLER_NAME\=Apple\ Video\ Media\ Handler", "VENDOR_ID\=appl", "TIMECODE\=00:00:00:00", "DURATION\=00:00:00.400000000" }, encoder=(string)"Lavc59.37.100\ libvpx-vp9")__");
+
+ QMediaMetaData parsed = taglistToMetaData(tagList.get());
+
+ QCOMPARE(parsed[QMediaMetaData::Duration].value<int>(), 400);
+}
+
+void tst_GStreamer::QGstBin_createFromPipelineDescription()
+{
+ QGstBin bin = QGstBin::createFromPipelineDescription("identity name=foo ! identity name=bar");
+
+ QVERIFY(bin);
+ QVERIFY(bin.findByName("foo"));
+ QCOMPARE_EQ(bin.findByName("foo").getParent(), bin);
+ QVERIFY(bin.findByName("bar"));
+ QVERIFY(!bin.findByName("baz"));
+ bin.dumpGraph("QGstBin_createFromPipelineDescription");
+}
+
+void tst_GStreamer::QGstElement_createFromPipelineDescription()
+{
+ using namespace std::string_view_literals;
+ QGstElement element = QGstElement::createFromPipelineDescription("identity name=foo");
+ QCOMPARE_EQ(element.name(), "foo"sv);
+ QCOMPARE_EQ(element.typeName(), "GstIdentity"sv);
+}
+
+void tst_GStreamer::QGstElement_createFromPipelineDescription_multipleElementsCreatesBin()
+{
+ using namespace std::string_view_literals;
+ QGstElement element =
+ QGstElement::createFromPipelineDescription("identity name=foo ! identity name=bar");
+
+ QVERIFY(element);
+ QCOMPARE_EQ(element.typeName(), "GstPipeline"sv);
+
+ QGstBin bin{
+ qGstSafeCast<GstBin>(element.element()),
+ QGstBin::NeedsRef,
+ };
+
+ QVERIFY(bin);
+ QVERIFY(bin.findByName("foo"));
+ QCOMPARE_EQ(bin.findByName("foo").getParent(), bin);
+ QVERIFY(bin.findByName("bar"));
+ QVERIFY(!bin.findByName("baz"));
+
+ bin.dumpGraph("QGstElement_createFromPipelineDescription_multipleElements");
+}
+
+QTEST_GUILESS_MAIN(tst_GStreamer)
+
+#include "moc_tst_gstreamer_backend.cpp"
diff --git a/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.h b/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.h
new file mode 100644
index 000000000..7b490c22b
--- /dev/null
+++ b/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_GSTREAMER_BACKEND_H
+#define TST_GSTREAMER_BACKEND_H
+
+#include <QtTest/QtTest>
+
+#include <QtQGstreamerMediaPlugin/private/qgstreamerintegration_p.h>
+#include <QtQGstreamerMediaPlugin/private/qgst_handle_types_p.h>
+
+QT_USE_NAMESPACE
+
+class tst_GStreamer : public QObject
+{
+ Q_OBJECT
+
+ QGstTagListHandle parseTagList(const char *);
+ QGstTagListHandle parseTagList(const QByteArray &);
+
+private slots:
+ void qGstCasts_withElement();
+ void qGstCasts_withBin();
+ void qGstCasts_withPipeline();
+
+ void metadata_taglistToMetaData();
+ void metadata_taglistToMetaData_extractsOrientation();
+ void metadata_taglistToMetaData_extractsOrientation_data();
+ void metadata_taglistToMetaData_extractsDuration();
+
+ void QGstBin_createFromPipelineDescription();
+ void QGstElement_createFromPipelineDescription();
+ void QGstElement_createFromPipelineDescription_multipleElementsCreatesBin();
+
+private:
+ QGstreamerIntegration integration;
+};
+
+#endif // TST_GSTREAMER_BACKEND_H
diff --git a/tests/auto/unit/multimedia/qabstractvideobuffer/CMakeLists.txt b/tests/auto/unit/multimedia/qabstractvideobuffer/CMakeLists.txt
index bd7f67a19..8f59a0e40 100644
--- a/tests/auto/unit/multimedia/qabstractvideobuffer/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qabstractvideobuffer/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qabstractvideobuffer.pro.
#####################################################################
@@ -7,7 +10,7 @@
qt_internal_add_test(tst_qabstractvideobuffer
SOURCES
tst_qabstractvideobuffer.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/unit/multimedia/qabstractvideobuffer/tst_qabstractvideobuffer.cpp b/tests/auto/unit/multimedia/qabstractvideobuffer/tst_qabstractvideobuffer.cpp
index 94594ba99..567b086f2 100644
--- a/tests/auto/unit/multimedia/qabstractvideobuffer/tst_qabstractvideobuffer.cpp
+++ b/tests/auto/unit/multimedia/qabstractvideobuffer/tst_qabstractvideobuffer.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -121,7 +94,7 @@ void tst_QAbstractVideoBuffer::handle()
{
QtTestVideoBuffer buffer(QVideoFrame::NoHandle);
- QVERIFY(buffer.textureHandle(0) == 0);
+ QVERIFY(buffer.textureHandle(nullptr, 0) == 0);
}
void tst_QAbstractVideoBuffer::mapMode()
diff --git a/tests/auto/unit/multimedia/qaudiobuffer/CMakeLists.txt b/tests/auto/unit/multimedia/qaudiobuffer/CMakeLists.txt
index 6e9a99180..807f3acaa 100644
--- a/tests/auto/unit/multimedia/qaudiobuffer/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qaudiobuffer/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qaudiobuffer.pro.
#####################################################################
@@ -7,7 +10,7 @@
qt_internal_add_test(tst_qaudiobuffer
SOURCES
tst_qaudiobuffer.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Multimedia
)
diff --git a/tests/auto/unit/multimedia/qaudiobuffer/tst_qaudiobuffer.cpp b/tests/auto/unit/multimedia/qaudiobuffer/tst_qaudiobuffer.cpp
index 875025559..7c74fe994 100644
--- a/tests/auto/unit/multimedia/qaudiobuffer/tst_qaudiobuffer.cpp
+++ b/tests/auto/unit/multimedia/qaudiobuffer/tst_qaudiobuffer.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QString>
#include <QtTest/QtTest>
diff --git a/tests/auto/unit/multimedia/qaudiodecoder/CMakeLists.txt b/tests/auto/unit/multimedia/qaudiodecoder/CMakeLists.txt
index d56507756..cced66bda 100644
--- a/tests/auto/unit/multimedia/qaudiodecoder/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qaudiodecoder/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qaudiodecoder.pro.
#####################################################################
@@ -9,10 +12,10 @@ qt_internal_add_test(tst_qaudiodecoder
tst_qaudiodecoder.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::Multimedia
Qt::MultimediaPrivate
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp b/tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp
index 6049bd4a2..77e161fda 100644
--- a/tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp
+++ b/tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QString>
@@ -32,13 +7,18 @@
#include "qaudiodecoder.h"
#include "qmockaudiodecoder.h"
-#include "qmockintegration_p.h"
+#include "qmockintegration.h"
+
+QT_USE_NAMESPACE
+
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
class tst_QAudioDecoder : public QObject
{
Q_OBJECT
public:
+
tst_QAudioDecoder();
private Q_SLOTS:
@@ -49,9 +29,6 @@ private Q_SLOTS:
void source();
void readAll();
void nullControl();
-
-private:
- QMockIntegration mockIntegration;
};
tst_QAudioDecoder::tst_QAudioDecoder()
@@ -63,7 +40,7 @@ void tst_QAudioDecoder::ctors()
QAudioDecoder d;
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QCOMPARE(d.source(), QString(""));
+ QCOMPARE(d.source(), QStringLiteral(""));
d.setSource(QUrl());
QVERIFY(!d.isDecoding());
@@ -77,8 +54,8 @@ void tst_QAudioDecoder::read()
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
- QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged);
QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
// Starting with empty source == error
@@ -87,9 +64,9 @@ void tst_QAudioDecoder::read()
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QCOMPARE(readySpy.count(), 0);
- QCOMPARE(bufferChangedSpy.count(), 0);
- QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(readySpy.size(), 0);
+ QCOMPARE(bufferChangedSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 1);
// Set the source to something
d.setSource(QUrl::fromLocalFile("Blah"));
@@ -122,13 +99,13 @@ void tst_QAudioDecoder::read()
QVERIFY(b.format().channelCount() == 1);
QVERIFY(b.sampleCount() == 4);
- QVERIFY(readySpy.count() >= 1);
- QVERIFY(errorSpy.count() == 0);
+ QVERIFY(readySpy.size() >= 1);
+ QVERIFY(errorSpy.size() == 0);
if (d.bufferAvailable()) {
- QVERIFY(bufferChangedSpy.count() == 1);
+ QVERIFY(bufferChangedSpy.size() == 1);
} else {
- QVERIFY(bufferChangedSpy.count() == 2);
+ QVERIFY(bufferChangedSpy.size() == 2);
}
}
@@ -138,8 +115,8 @@ void tst_QAudioDecoder::stop()
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
- QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged);
QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
// Starting with empty source == error
@@ -148,9 +125,9 @@ void tst_QAudioDecoder::stop()
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QCOMPARE(readySpy.count(), 0);
- QCOMPARE(bufferChangedSpy.count(), 0);
- QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(readySpy.size(), 0);
+ QCOMPARE(bufferChangedSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 1);
// Set the source to something
d.setSource(QUrl::fromLocalFile("Blah"));
@@ -190,8 +167,8 @@ void tst_QAudioDecoder::format()
QVERIFY(!d.isDecoding());
QVERIFY(d.bufferAvailable() == false);
- QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
- QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady);
+ QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged);
QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
// Set the source to something
@@ -278,19 +255,19 @@ void tst_QAudioDecoder::readAll()
d.setSource(QUrl::fromLocalFile("Foo"));
QVERIFY(!d.isDecoding());
- QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64)));
- QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64)));
- QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool)));
- QSignalSpy finishedSpy(&d, SIGNAL(finished()));
- QSignalSpy bufferAvailableSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged);
+ QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged);
+ QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged);
+ QSignalSpy finishedSpy(&d, &QAudioDecoder::finished);
+ QSignalSpy bufferAvailableSpy(&d, &QAudioDecoder::bufferAvailableChanged);
d.start();
int i = 0;
forever {
QVERIFY(d.isDecoding());
- QCOMPARE(isDecodingSpy.count(), 1);
- QCOMPARE(durationSpy.count(), 1);
+ QCOMPARE(isDecodingSpy.size(), 1);
+ QCOMPARE(durationSpy.size(), 1);
QVERIFY(finishedSpy.isEmpty());
- QTRY_VERIFY(bufferAvailableSpy.count() >= 1);
+ QTRY_VERIFY(bufferAvailableSpy.size() >= 1);
if (d.bufferAvailable()) {
QAudioBuffer b = d.read();
QVERIFY(b.isValid());
@@ -301,8 +278,8 @@ void tst_QAudioDecoder::readAll()
i++;
if (i == MOCK_DECODER_MAX_BUFFERS) {
- QCOMPARE(finishedSpy.count(), 1);
- QCOMPARE(isDecodingSpy.count(), 2);
+ QCOMPARE(finishedSpy.size(), 1);
+ QCOMPARE(isDecodingSpy.size(), 2);
QVERIFY(!d.isDecoding());
QList<QVariant> arguments = isDecodingSpy.takeLast();
QVERIFY(arguments.at(0).toBool() == false);
@@ -319,7 +296,7 @@ void tst_QAudioDecoder::readAll()
void tst_QAudioDecoder::nullControl()
{
- mockIntegration.setFlags(QMockIntegration::NoAudioDecoderInterface);
+ QMockIntegration::instance()->setFlags(QMockIntegration::NoAudioDecoderInterface);
QAudioDecoder d;
QVERIFY(d.error() == QAudioDecoder::NotSupportedError);
diff --git a/tests/auto/unit/multimedia/qaudioformat/CMakeLists.txt b/tests/auto/unit/multimedia/qaudioformat/CMakeLists.txt
index 2cd6fc9a7..5f4c47bd7 100644
--- a/tests/auto/unit/multimedia/qaudioformat/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qaudioformat/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qaudioformat.pro.
#####################################################################
@@ -7,7 +10,7 @@
qt_internal_add_test(tst_qaudioformat
SOURCES
tst_qaudioformat.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/unit/multimedia/qaudioformat/tst_qaudioformat.cpp b/tests/auto/unit/multimedia/qaudioformat/tst_qaudioformat.cpp
index b2ee24808..c4f4e10cf 100644
--- a/tests/auto/unit/multimedia/qaudioformat/tst_qaudioformat.cpp
+++ b/tests/auto/unit/multimedia/qaudioformat/tst_qaudioformat.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -34,8 +9,6 @@
#include <QStringList>
#include <QList>
-//TESTED_COMPONENT=src/multimedia
-
class tst_QAudioFormat : public QObject
{
Q_OBJECT
diff --git a/tests/auto/unit/multimedia/qaudionamespace/CMakeLists.txt b/tests/auto/unit/multimedia/qaudionamespace/CMakeLists.txt
index 2c9bb0605..3d4ac7126 100644
--- a/tests/auto/unit/multimedia/qaudionamespace/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qaudionamespace/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qaudionamespace.pro.
#####################################################################
@@ -7,7 +10,7 @@
qt_internal_add_test(tst_qaudionamespace
SOURCES
tst_qaudionamespace.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/unit/multimedia/qaudionamespace/tst_qaudionamespace.cpp b/tests/auto/unit/multimedia/qaudionamespace/tst_qaudionamespace.cpp
index e3e7e17bf..cae002306 100644
--- a/tests/auto/unit/multimedia/qaudionamespace/tst_qaudionamespace.cpp
+++ b/tests/auto/unit/multimedia/qaudionamespace/tst_qaudionamespace.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -235,7 +208,7 @@ void tst_QAudioNamespace::convertVolume()
qreal actualOutput = QAudio::convertVolume(input, from, to);
QVERIFY2(qAbs(actualOutput - expectedOutput) < 0.01,
- QString("actual: %1, expected: %2").arg(actualOutput).arg(expectedOutput).toLocal8Bit().constData());
+ QStringLiteral("actual: %1, expected: %2").arg(actualOutput).arg(expectedOutput).toLocal8Bit().constData());
}
QTEST_MAIN(tst_QAudioNamespace)
diff --git a/tests/auto/unit/multimedia/qaudiorecorder/CMakeLists.txt b/tests/auto/unit/multimedia/qaudiorecorder/CMakeLists.txt
index 3ee8b5692..b2dc16b69 100644
--- a/tests/auto/unit/multimedia/qaudiorecorder/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qaudiorecorder/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qaudiorecorder.pro.
#####################################################################
@@ -9,9 +12,9 @@ qt_internal_add_test(tst_qaudiorecorder
tst_qaudiorecorder.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::MultimediaPrivate
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimedia/qaudiorecorder/tst_qaudiorecorder.cpp b/tests/auto/unit/multimedia/qaudiorecorder/tst_qaudiorecorder.cpp
index 52f563bb2..7d6309976 100644
--- a/tests/auto/unit/multimedia/qaudiorecorder/tst_qaudiorecorder.cpp
+++ b/tests/auto/unit/multimedia/qaudiorecorder/tst_qaudiorecorder.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
@@ -37,13 +12,13 @@
#include <qaudiosource.h>
#include <qmediacapturesession.h>
-//TESTED_COMPONENT=src/multimedia
-
#include "qmockmediacapturesession.h"
-#include "qmockintegration_p.h"
+#include "qmockintegration.h"
QT_USE_NAMESPACE
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
class tst_QAudioRecorder: public QObject
{
Q_OBJECT
@@ -59,20 +34,16 @@ private slots:
private:
QMediaRecorder *encoder = nullptr;
- QMockIntegration *mockIntegration;
};
void tst_QAudioRecorder::init()
{
- mockIntegration = new QMockIntegration;
encoder = nullptr;
}
void tst_QAudioRecorder::cleanup()
{
delete encoder;
- delete mockIntegration;
- mockIntegration = nullptr;
encoder = nullptr;
}
diff --git a/tests/auto/unit/multimedia/qaudiostatemachine/CMakeLists.txt b/tests/auto/unit/multimedia/qaudiostatemachine/CMakeLists.txt
new file mode 100644
index 000000000..715091dac
--- /dev/null
+++ b/tests/auto/unit/multimedia/qaudiostatemachine/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_qaudiostatemachine
+ SOURCES
+ tst_qaudiostatemachine.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::MultimediaPrivate
+)
+
diff --git a/tests/auto/unit/multimedia/qaudiostatemachine/tst_qaudiostatemachine.cpp b/tests/auto/unit/multimedia/qaudiostatemachine/tst_qaudiostatemachine.cpp
new file mode 100644
index 000000000..433590d09
--- /dev/null
+++ b/tests/auto/unit/multimedia/qaudiostatemachine/tst_qaudiostatemachine.cpp
@@ -0,0 +1,661 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// TESTED_COMPONENT=src/multimedia
+
+#include <QtTest/QtTest>
+#include <private/qaudiostatemachine_p.h>
+#include <private/qaudiosystem_p.h>
+#include <QThread>
+
+QT_USE_NAMESPACE
+
+template<typename F>
+static std::unique_ptr<QThread> createTestThread(std::vector<std::atomic_int> &counters,
+ size_t index, F &&functor,
+ int minAttemptsCount = 2000)
+{
+ return std::unique_ptr<QThread>(QThread::create([=, &counters]() {
+ auto checkCounter = [=](int counter) { return counter < minAttemptsCount; };
+ for (; !QTest::currentTestFailed()
+ && std::any_of(counters.begin(), counters.end(), checkCounter);
+ ++counters[index])
+ functor();
+ }));
+}
+
+class tst_QAudioStateMachine : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void constructor_setsStoppedStateWithNoError();
+
+ void start_changesState_whenStateIsStopped_data();
+ void start_changesState_whenStateIsStopped();
+
+ void start_doesntChangeState_whenStateIsNotStopped_data();
+ void start_doesntChangeState_whenStateIsNotStopped();
+
+ void stop_changesState_whenStateIsNotStopped_data();
+ void stop_changesState_whenStateIsNotStopped();
+
+ void stop_doesntChangeState_whenStateIsStopped_data();
+ void stop_doesntChangeState_whenStateIsStopped();
+
+ void stopWithDraining_changesState_whenStateIsNotStopped_data();
+ void stopWithDraining_changesState_whenStateIsNotStopped();
+
+ void methods_dontChangeState_whenDraining();
+
+ void onDrained_finishesDraining();
+
+ void onDrained_getsFailed_whenDrainHasntBeenCalled_data();
+ void onDrained_getsFailed_whenDrainHasntBeenCalled();
+
+ void updateActiveOrIdle_doesntChangeState_whenStateIsNotActiveOrIdle_data();
+ void updateActiveOrIdle_doesntChangeState_whenStateIsNotActiveOrIdle();
+
+ void updateActiveOrIdle_changesState_whenStateIsActiveOrIdle_data();
+ void updateActiveOrIdle_changesState_whenStateIsActiveOrIdle();
+
+ void suspendAndResume_saveAndRestoreState_whenStateIsActiveOrIdle_data();
+ void suspendAndResume_saveAndRestoreState_whenStateIsActiveOrIdle();
+
+ void suspend_doesntChangeState_whenStateIsNotActiveOrIdle_data();
+ void suspend_doesntChangeState_whenStateIsNotActiveOrIdle();
+
+ void resume_doesntChangeState_whenStateIsNotSuspended_data();
+ void resume_doesntChangeState_whenStateIsNotSuspended();
+
+ void deleteNotifierInSlot_suppressesAdjacentSignal();
+
+ void twoThreadsToggleSuspendResumeAndIdleActive_statesAreConsistent();
+
+ void twoThreadsToggleStartStop_statesAreConsistent();
+
+private:
+ void generateNotStoppedPrevStates()
+ {
+ QTest::addColumn<QAudio::State>("prevState");
+ QTest::addColumn<QAudio::Error>("prevError");
+
+ QTest::newRow("from IdleState") << QAudio::IdleState << QAudio::UnderrunError;
+ QTest::newRow("from ActiveState") << QAudio::ActiveState << QAudio::NoError;
+ QTest::newRow("from SuspendedState") << QAudio::SuspendedState << QAudio::NoError;
+ }
+
+ void generateStoppedAndSuspendedPrevStates()
+ {
+ QTest::addColumn<QAudio::State>("prevState");
+
+ QTest::newRow("from StoppedState") << QAudio::StoppedState;
+ QTest::newRow("from SuspendedState") << QAudio::SuspendedState;
+ }
+};
+
+void tst_QAudioStateMachine::constructor_setsStoppedStateWithNoError()
+{
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ QCOMPARE(stateMachine.state(), QAudio::StoppedState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+ QVERIFY(!stateMachine.isActiveOrIdle());
+}
+
+void tst_QAudioStateMachine::start_changesState_whenStateIsStopped_data()
+{
+ QTest::addColumn<bool>("active");
+ QTest::addColumn<QAudio::State>("expectedState");
+
+ QTest::newRow("to active") << true << QAudio::ActiveState;
+ QTest::newRow("to not active") << false << QAudio::IdleState;
+}
+
+void tst_QAudioStateMachine::start_changesState_whenStateIsStopped()
+{
+ QFETCH(bool, active);
+ QFETCH(QAudio::State, expectedState);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY(stateMachine.start(active));
+
+ QCOMPARE(stateSpy.size(), 1);
+ QCOMPARE(stateSpy.front().front().value<QAudio::State>(), expectedState);
+ QCOMPARE(errorSpy.size(), 0);
+ QCOMPARE(stateMachine.state(), expectedState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+}
+
+void tst_QAudioStateMachine::start_doesntChangeState_whenStateIsNotStopped_data()
+{
+ generateNotStoppedPrevStates();
+}
+
+void tst_QAudioStateMachine::start_doesntChangeState_whenStateIsNotStopped()
+{
+ QFETCH(QAudio::State, prevState);
+ QFETCH(QAudio::Error, prevError);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+ stateMachine.forceSetState(prevState, prevError);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY2(!stateMachine.start(), "Cannot start (active)");
+ QVERIFY2(!stateMachine.start(false), "Cannot start (not active)");
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+
+ QCOMPARE(stateMachine.state(), prevState);
+ QCOMPARE(stateMachine.error(), prevError);
+}
+
+void tst_QAudioStateMachine::stop_changesState_whenStateIsNotStopped_data()
+{
+ generateNotStoppedPrevStates();
+}
+
+void tst_QAudioStateMachine::stop_changesState_whenStateIsNotStopped()
+{
+ QFETCH(QAudio::State, prevState);
+ QFETCH(QAudio::Error, prevError);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(prevState, prevError);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ auto notifier = stateMachine.stop();
+ QVERIFY(notifier);
+
+ QCOMPARE(stateMachine.state(), QAudio::StoppedState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+ QVERIFY(!notifier.isDraining());
+
+ notifier.reset();
+
+ QCOMPARE(stateSpy.size(), 1);
+ QCOMPARE(stateSpy.front().front().value<QAudio::State>(), QAudio::StoppedState);
+ QCOMPARE(errorSpy.size(), prevError == QAudio::NoError ? 0 : 1);
+ if (!errorSpy.empty())
+ QCOMPARE(errorSpy.front().front().value<QAudio::Error>(), QAudio::NoError);
+
+ QCOMPARE(stateMachine.state(), QAudio::StoppedState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+ QVERIFY(!stateMachine.isDraining());
+}
+
+void tst_QAudioStateMachine::stop_doesntChangeState_whenStateIsStopped_data()
+{
+ QTest::addColumn<QAudio::Error>("error");
+
+ QTest::newRow("from NoError") << QAudio::NoError;
+ QTest::newRow("from IOError") << QAudio::IOError;
+}
+
+void tst_QAudioStateMachine::stop_doesntChangeState_whenStateIsStopped()
+{
+ QFETCH(QAudio::Error, error);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.setError(error);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY2(!stateMachine.stop(), "should return false if already stopped");
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+ QCOMPARE(stateMachine.state(), QAudio::StoppedState);
+ QCOMPARE(stateMachine.error(), error);
+ QVERIFY(!stateMachine.isDraining());
+}
+
+void tst_QAudioStateMachine::stopWithDraining_changesState_whenStateIsNotStopped_data()
+{
+ generateNotStoppedPrevStates();
+}
+
+void tst_QAudioStateMachine::stopWithDraining_changesState_whenStateIsNotStopped()
+{
+ QFETCH(QAudio::State, prevState);
+ QFETCH(QAudio::Error, prevError);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(prevState, prevError);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+
+ auto notifier = stateMachine.stop(QAudio::NoError, true);
+ QVERIFY(notifier);
+ QCOMPARE(notifier.isDraining(), prevState == QAudio::ActiveState);
+ notifier.reset();
+
+ QCOMPARE(stateMachine.state(), QAudio::StoppedState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+ QCOMPARE(stateMachine.isDraining(), prevState == QAudio::ActiveState);
+
+ QCOMPARE(stateSpy.size(), 1);
+}
+
+void tst_QAudioStateMachine::methods_dontChangeState_whenDraining()
+{
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(QAudio::ActiveState);
+ stateMachine.stop(QAudio::IOError, true);
+ QVERIFY(stateMachine.isDraining());
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY(!stateMachine.start());
+ QVERIFY(!stateMachine.stop());
+ QVERIFY(!stateMachine.stop(QAudio::NoError, true));
+ QVERIFY(!stateMachine.suspend());
+ QVERIFY(!stateMachine.resume());
+ QVERIFY(!stateMachine.updateActiveOrIdle(false));
+ QVERIFY(!stateMachine.updateActiveOrIdle(true));
+
+ QCOMPARE(stateMachine.state(), QAudio::StoppedState);
+ QCOMPARE(stateMachine.error(), QAudio::IOError);
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+
+ QVERIFY(stateMachine.isDraining());
+}
+
+void tst_QAudioStateMachine::onDrained_finishesDraining()
+{
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(QAudio::ActiveState);
+ stateMachine.stop(QAudio::IOError, true);
+ QVERIFY(stateMachine.isDraining());
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY(stateMachine.onDrained());
+ QVERIFY(!stateMachine.isDraining());
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+
+ QCOMPARE(stateMachine.state(), QAudio::StoppedState);
+ QCOMPARE(stateMachine.error(), QAudio::IOError);
+
+ QVERIFY(stateMachine.start());
+}
+
+void tst_QAudioStateMachine::onDrained_getsFailed_whenDrainHasntBeenCalled_data()
+{
+ generateNotStoppedPrevStates();
+ QTest::newRow("from Stopped State") << QAudio::StoppedState << QAudio::IOError;
+}
+
+void tst_QAudioStateMachine::onDrained_getsFailed_whenDrainHasntBeenCalled()
+{
+ QFETCH(QAudio::State, prevState);
+ QFETCH(QAudio::Error, prevError);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(prevState, prevError);
+
+ QVERIFY(!stateMachine.onDrained());
+
+ QCOMPARE(stateMachine.state(), prevState);
+ QCOMPARE(stateMachine.error(), prevError);
+}
+
+void tst_QAudioStateMachine::updateActiveOrIdle_doesntChangeState_whenStateIsNotActiveOrIdle_data()
+{
+ generateStoppedAndSuspendedPrevStates();
+}
+
+void tst_QAudioStateMachine::updateActiveOrIdle_doesntChangeState_whenStateIsNotActiveOrIdle()
+{
+ QFETCH(QAudio::State, prevState);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(prevState);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY(!stateMachine.updateActiveOrIdle(true));
+ QVERIFY(!stateMachine.updateActiveOrIdle(false));
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+}
+
+void tst_QAudioStateMachine::updateActiveOrIdle_changesState_whenStateIsActiveOrIdle_data()
+{
+ QTest::addColumn<QAudio::State>("prevState");
+ QTest::addColumn<QAudio::Error>("prevError");
+ QTest::addColumn<bool>("active");
+ QTest::addColumn<QAudio::Error>("error");
+
+ QTest::newRow("from ActiveState+NoError -> not active+NoError")
+ << QAudio::ActiveState << QAudio::NoError << false << QAudio::NoError;
+ QTest::newRow("from Idle(UnderrunError) -> active+NoError")
+ << QAudio::IdleState << QAudio::UnderrunError << true << QAudio::NoError;
+ QTest::newRow("from Idle(UnderrunError) -> not active+UnderrunError")
+ << QAudio::IdleState << QAudio::UnderrunError << false << QAudio::UnderrunError;
+}
+
+void tst_QAudioStateMachine::updateActiveOrIdle_changesState_whenStateIsActiveOrIdle()
+{
+ QFETCH(QAudio::State, prevState);
+ QFETCH(QAudio::Error, prevError);
+ QFETCH(bool, active);
+ QFETCH(QAudio::Error, error);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(prevState, prevError);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ const auto expectedState = active ? QAudio::ActiveState : QAudio::IdleState;
+
+ auto notifier = stateMachine.updateActiveOrIdle(active, error);
+ QVERIFY(notifier);
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+
+ QCOMPARE(stateMachine.state(), expectedState);
+ QCOMPARE(stateMachine.error(), error);
+
+ notifier.reset();
+
+ QCOMPARE(stateSpy.size(), expectedState == prevState ? 0 : 1);
+ if (!stateSpy.empty())
+ QCOMPARE(stateSpy.front().front().value<QAudio::State>(), expectedState);
+
+ QCOMPARE(errorSpy.size(), prevError == error ? 0 : 1);
+ if (!errorSpy.empty())
+ QCOMPARE(errorSpy.front().front().value<QAudio::Error>(), error);
+
+ QCOMPARE(stateMachine.state(), expectedState);
+ QCOMPARE(stateMachine.error(), error);
+}
+
+void tst_QAudioStateMachine::suspendAndResume_saveAndRestoreState_whenStateIsActiveOrIdle_data()
+{
+ QTest::addColumn<QAudio::State>("prevState");
+ QTest::addColumn<QAudio::Error>("prevError");
+
+ QTest::newRow("from Active+NoError") << QAudio::ActiveState << QAudio::NoError;
+ QTest::newRow("from Idle+UnderrunError") << QAudio::IdleState << QAudio::UnderrunError;
+}
+
+void tst_QAudioStateMachine::suspendAndResume_saveAndRestoreState_whenStateIsActiveOrIdle()
+{
+ QFETCH(QAudio::State, prevState);
+ QFETCH(QAudio::Error, prevError);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(prevState, prevError);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY(stateMachine.suspend());
+
+ QCOMPARE(stateSpy.size(), 1);
+ QCOMPARE(stateSpy.front().front().value<QAudio::State>(), QAudio::SuspendedState);
+ QCOMPARE(errorSpy.size(), prevError == QAudio::NoError ? 0 : 1);
+
+ QCOMPARE(stateMachine.state(), QAudio::SuspendedState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+
+ stateSpy.clear();
+ errorSpy.clear();
+
+ QVERIFY(!stateMachine.suspend());
+ QVERIFY(stateMachine.resume());
+
+ QCOMPARE(stateSpy.size(), 1);
+ QCOMPARE(stateSpy.front().front().value<QAudio::State>(), prevState);
+ QCOMPARE(errorSpy.size(), 0);
+
+ QCOMPARE(stateMachine.state(), prevState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+}
+
+void tst_QAudioStateMachine::suspend_doesntChangeState_whenStateIsNotActiveOrIdle_data()
+{
+ generateStoppedAndSuspendedPrevStates();
+}
+
+void tst_QAudioStateMachine::suspend_doesntChangeState_whenStateIsNotActiveOrIdle()
+{
+ QFETCH(QAudio::State, prevState);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(prevState);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY(!stateMachine.suspend());
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+
+ QCOMPARE(stateMachine.state(), prevState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+}
+
+void tst_QAudioStateMachine::resume_doesntChangeState_whenStateIsNotSuspended_data()
+{
+ QTest::addColumn<QAudio::State>("prevState");
+
+ QTest::newRow("from StoppedState") << QAudio::StoppedState;
+ QTest::newRow("from ActiveState") << QAudio::ActiveState;
+ QTest::newRow("from IdleState") << QAudio::IdleState;
+}
+
+void tst_QAudioStateMachine::resume_doesntChangeState_whenStateIsNotSuspended()
+{
+ QFETCH(QAudio::State, prevState);
+
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ stateMachine.forceSetState(prevState);
+
+ QSignalSpy stateSpy(&changeNotifier, &QAudioStateChangeNotifier::stateChanged);
+ QSignalSpy errorSpy(&changeNotifier, &QAudioStateChangeNotifier::errorChanged);
+
+ QVERIFY(!stateMachine.resume());
+
+ QCOMPARE(stateSpy.size(), 0);
+ QCOMPARE(errorSpy.size(), 0);
+
+ QCOMPARE(stateMachine.state(), prevState);
+ QCOMPARE(stateMachine.error(), QAudio::NoError);
+}
+
+void tst_QAudioStateMachine::deleteNotifierInSlot_suppressesAdjacentSignal()
+{
+ auto changeNotifier = std::make_unique<QAudioStateChangeNotifier>();
+ QAudioStateMachine stateMachine(*changeNotifier);
+ stateMachine.start();
+
+ auto onSignal = [&]() {
+ QVERIFY2(changeNotifier, "The 2nd signal shouldn't be emitted");
+ changeNotifier.reset();
+ };
+
+ connect(changeNotifier.get(), &QAudioStateChangeNotifier::errorChanged,
+ this, onSignal, Qt::DirectConnection);
+ connect(changeNotifier.get(), &QAudioStateChangeNotifier::stateChanged,
+ this, onSignal, Qt::DirectConnection);
+
+ stateMachine.stop(QAudio::IOError);
+}
+
+void tst_QAudioStateMachine::twoThreadsToggleSuspendResumeAndIdleActive_statesAreConsistent()
+{
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ QVERIFY(stateMachine.start());
+ QCOMPARE(stateMachine.state(), QAudio::ActiveState);
+
+ std::atomic<int> signalsCount = 0;
+ std::atomic<int> changesCount = 0;
+
+ connect(&changeNotifier, &QAudioStateChangeNotifier::stateChanged,
+ this, [&](QAudio::State) { ++signalsCount; }, Qt::DirectConnection);
+
+ std::vector<std::atomic_int> counters(2);
+
+ auto threadSuspendResume = createTestThread(counters, 0, [&]() {
+ {
+ auto notifier = stateMachine.suspend();
+ QVERIFY(notifier);
+ QVERIFY(notifier.isStateChanged());
+ QCOMPARE(notifier.audioState(), QAudio::SuspendedState);
+ ++changesCount;
+ }
+
+ {
+ auto notifier = stateMachine.resume();
+ QVERIFY(notifier);
+ QVERIFY(notifier.isStateChanged());
+ QCOMPARE_NE(notifier.audioState(), QAudio::SuspendedState);
+ ++changesCount;
+ }
+ });
+
+ auto threadIdleActive = createTestThread(counters, 1, [&]() {
+ if (auto notifier = stateMachine.updateActiveOrIdle(false)) {
+ if (notifier.isStateChanged())
+ ++changesCount;
+
+ QCOMPARE(notifier.audioState(), QAudio::IdleState);
+ }
+
+ if (auto notifier = stateMachine.updateActiveOrIdle(true)) {
+ if (notifier.isStateChanged())
+ ++changesCount;
+
+ QCOMPARE(notifier.audioState(), QAudio::ActiveState);
+ }
+ });
+
+ threadSuspendResume->start();
+ threadIdleActive->start();
+
+ threadSuspendResume->wait();
+ threadIdleActive->wait();
+
+ if (QTest::currentTestFailed()) {
+ qDebug() << "counterSuspendResume:" << counters[0];
+ qDebug() << "counterIdleActive:" << counters[1];
+ }
+
+ QCOMPARE(signalsCount, changesCount);
+}
+
+void tst_QAudioStateMachine::twoThreadsToggleStartStop_statesAreConsistent()
+{
+ QAudioStateChangeNotifier changeNotifier;
+ QAudioStateMachine stateMachine(changeNotifier);
+
+ QVERIFY(stateMachine.start());
+ QCOMPARE(stateMachine.state(), QAudio::ActiveState);
+
+ std::atomic<int> signalsCount = 0;
+ std::atomic<int> changesCount = 0;
+
+ connect(&changeNotifier, &QAudioStateChangeNotifier::stateChanged,
+ this, [&](QAudio::State) { ++signalsCount; }, Qt::DirectConnection);
+
+ std::vector<std::atomic_int> counters(2);
+
+ auto threadStartActive = createTestThread(counters, 0, [&]() {
+ if (auto startNotifier = stateMachine.start()) {
+ QCOMPARE(startNotifier.prevAudioState(), QAudio::StoppedState);
+ QCOMPARE(startNotifier.audioState(), QAudio::ActiveState);
+ ++changesCount;
+ startNotifier.reset();
+
+ auto stopNotifier = stateMachine.stop();
+ ++changesCount;
+ QVERIFY(stopNotifier);
+ QCOMPARE(stopNotifier.prevAudioState(), QAudio::ActiveState);
+ }
+ });
+
+ auto threadStartIdle = createTestThread(counters, 1, [&]() {
+ if (auto startNotifier = stateMachine.start(false)) {
+ QCOMPARE(startNotifier.prevAudioState(), QAudio::StoppedState);
+ QCOMPARE(startNotifier.audioState(), QAudio::IdleState);
+ ++changesCount;
+ startNotifier.reset();
+
+ auto stopNotifier = stateMachine.stop();
+ ++changesCount;
+ QVERIFY(stopNotifier);
+ QCOMPARE(stopNotifier.audioState(), QAudio::StoppedState);
+ QCOMPARE(stopNotifier.prevAudioState(), QAudio::IdleState);
+ }
+ });
+
+ threadStartActive->start();
+ threadStartIdle->start();
+
+ threadStartActive->wait();
+ threadStartIdle->wait();
+
+ if (QTest::currentTestFailed()) {
+ qDebug() << "counterSuspendResume:" << counters[0];
+ qDebug() << "counterIdleActive:" << counters[1];
+ }
+
+ QCOMPARE(signalsCount, changesCount);
+}
+
+QTEST_GUILESS_MAIN(tst_QAudioStateMachine)
+
+#include "tst_qaudiostatemachine.moc"
diff --git a/tests/auto/unit/multimedia/qcamera/CMakeLists.txt b/tests/auto/unit/multimedia/qcamera/CMakeLists.txt
index 8413c6de4..484793923 100644
--- a/tests/auto/unit/multimedia/qcamera/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qcamera/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qcamera.pro.
#####################################################################
@@ -9,9 +12,9 @@ qt_internal_add_test(tst_multimedia_qcamera
tst_qcamera.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::MultimediaPrivate
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp b/tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp
index 52a34d4f4..1ba624eec 100644
--- a/tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp
+++ b/tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
@@ -41,12 +14,13 @@
#include <qobject.h>
#include <qmediadevices.h>
-#include "qmockintegration_p.h"
+#include "qmockintegration.h"
#include "qmockmediacapturesession.h"
#include "qmockcamera.h"
QT_USE_NAMESPACE
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
class tst_QCamera: public QObject
{
@@ -97,13 +71,14 @@ private slots:
void testSignalIsoSensitivityChanged();
void testSignalShutterSpeedChanged();
void testSignalFlashReady();
-
-private:
- QMockIntegration integration;
};
void tst_QCamera::initTestCase()
{
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("Flakiness on macOS CI, to be investigated, QTBUG-111812");
+#endif
}
void tst_QCamera::init()
@@ -222,14 +197,14 @@ void tst_QCamera::testSimpleCameraCapture()
QCOMPARE(imageCapture.error(), QImageCapture::NoError);
QVERIFY(imageCapture.errorString().isEmpty());
- QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,QString)));
- imageCapture.captureToFile(QString::fromLatin1("/dev/null"));
+ QSignalSpy errorSignal(&imageCapture, &QImageCapture::errorOccurred);
+ imageCapture.captureToFile(QStringLiteral("/dev/null"));
QCOMPARE(errorSignal.size(), 1);
QCOMPARE(imageCapture.error(), QImageCapture::NotReadyError);
QVERIFY(!imageCapture.errorString().isEmpty());
camera.start();
- imageCapture.captureToFile(QString::fromLatin1("/dev/null"));
+ imageCapture.captureToFile(QStringLiteral("/dev/null"));
QCOMPARE(errorSignal.size(), 1);
QCOMPARE(imageCapture.error(), QImageCapture::NoError);
QVERIFY(imageCapture.errorString().isEmpty());
@@ -245,10 +220,10 @@ void tst_QCamera::testCameraCapture()
QVERIFY(!imageCapture.isReadyForCapture());
- QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage)));
- QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,QString)));
+ QSignalSpy capturedSignal(&imageCapture, &QImageCapture::imageCaptured);
+ QSignalSpy errorSignal(&imageCapture, &QImageCapture::errorOccurred);
- imageCapture.captureToFile(QString::fromLatin1("/dev/null"));
+ imageCapture.captureToFile(QStringLiteral("/dev/null"));
QCOMPARE(capturedSignal.size(), 0);
QCOMPARE(errorSignal.size(), 1);
QCOMPARE(imageCapture.error(), QImageCapture::NotReadyError);
@@ -259,7 +234,7 @@ void tst_QCamera::testCameraCapture()
QVERIFY(imageCapture.isReadyForCapture());
QCOMPARE(errorSignal.size(), 0);
- imageCapture.captureToFile(QString::fromLatin1("/dev/null"));
+ imageCapture.captureToFile(QStringLiteral("/dev/null"));
QTRY_COMPARE(capturedSignal.size(), 1);
QCOMPARE(errorSignal.size(), 0);
@@ -274,11 +249,11 @@ void tst_QCamera::testCameraCaptureMetadata()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy metadataSignal(&imageCapture, SIGNAL(imageMetadataAvailable(int,const QMediaMetaData&)));
- QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
+ QSignalSpy metadataSignal(&imageCapture, &QImageCapture::imageMetadataAvailable);
+ QSignalSpy savedSignal(&imageCapture, &QImageCapture::imageSaved);
camera.start();
- int id = imageCapture.captureToFile(QString::fromLatin1("/dev/null"));
+ int id = imageCapture.captureToFile(QStringLiteral("/dev/null"));
QTRY_COMPARE(savedSignal.size(), 1);
@@ -287,7 +262,7 @@ void tst_QCamera::testCameraCaptureMetadata()
QVariantList metadata = metadataSignal[0];
QCOMPARE(metadata[0].toInt(), id);
QMediaMetaData data = metadata[1].value<QMediaMetaData>();
- QCOMPARE(data.keys().length(), 2);
+ QCOMPARE(data.keys().size(), 2);
QCOMPARE(data[QMediaMetaData::Author].toString(), "Author");
QCOMPARE(data[QMediaMetaData::Date].toDateTime().date().year(), 2021);
}
@@ -444,11 +419,11 @@ void tst_QCamera::testCameraEncodingProperyChange()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy activeChangedSignal(&camera, SIGNAL(activeChanged(bool)));
+ QSignalSpy activeChangedSignal(&camera, &QCamera::activeChanged);
camera.start();
QCOMPARE(camera.isActive(), true);
- QCOMPARE(activeChangedSignal.count(), 1);
+ QCOMPARE(activeChangedSignal.size(), 1);
}
void tst_QCamera::testSetVideoOutput()
@@ -625,32 +600,32 @@ void tst_QCamera::testErrorSignal()
QMediaCaptureSession session;
QCamera camera;
session.setCamera(&camera);
- auto *service = integration.lastCaptureService();
+ auto *service = QMockIntegration::instance()->lastCaptureService();
Q_ASSERT(service);
Q_ASSERT(service->mockCameraControl);
- QSignalSpy spyError(&camera, SIGNAL(errorOccurred(QCamera::Error,const QString&)));
+ QSignalSpy spyError(&camera, &QCamera::errorOccurred);
/* Set the QPlatformCamera error and verify if the signal is emitted correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("Camera Error"));
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("Camera Error"));
- QVERIFY(spyError.count() == 1);
+ QVERIFY(spyError.size() == 1);
QCamera::Error err = qvariant_cast<QCamera::Error >(spyError.at(0).at(0));
QVERIFY(err == QCamera::CameraError);
spyError.clear();
/* Set the QPlatformCamera error and verify if the signal is emitted correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("InvalidRequestError Error"));
- QVERIFY(spyError.count() == 1);
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("InvalidRequestError Error"));
+ QVERIFY(spyError.size() == 1);
err = qvariant_cast<QCamera::Error >(spyError.at(0).at(0));
QVERIFY(err == QCamera::CameraError);
spyError.clear();
/* Set the QPlatformCamera error and verify if the signal is emitted correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("NotSupportedFeatureError Error"));
- QVERIFY(spyError.count() == 1);
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("NotSupportedFeatureError Error"));
+ QVERIFY(spyError.size() == 1);
err = qvariant_cast<QCamera::Error >(spyError.at(0).at(0));
QVERIFY(err == QCamera::CameraError);
@@ -662,18 +637,18 @@ void tst_QCamera::testError()
QMediaCaptureSession session;
QCamera camera;
session.setCamera(&camera);
- auto *service = integration.lastCaptureService();
+ auto *service = QMockIntegration::instance()->lastCaptureService();
/* Set the QPlatformCamera error and verify if it is set correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("Camera Error"));
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("Camera Error"));
QVERIFY(camera.error() == QCamera::CameraError);
/* Set the QPlatformCamera error and verify if it is set correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("InvalidRequestError Error"));
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("InvalidRequestError Error"));
QVERIFY(camera.error() == QCamera::CameraError);
/* Set the QPlatformCamera error and verify if it is set correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("CameraError Error"));
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("CameraError Error"));
QVERIFY(camera.error() == QCamera::CameraError);
}
@@ -684,19 +659,19 @@ void tst_QCamera::testErrorString()
QMediaCaptureSession session;
QCamera camera;
session.setCamera(&camera);
- auto *service = integration.lastCaptureService();
+ auto *service = QMockIntegration::instance()->lastCaptureService();
/* Set the QPlatformCamera error and verify if it is set correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("Camera Error"));
- QVERIFY(camera.errorString() == QString("Camera Error"));
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("Camera Error"));
+ QVERIFY(camera.errorString() == QStringLiteral("Camera Error"));
/* Set the QPlatformCamera error and verify if it is set correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("InvalidRequestError Error"));
- QVERIFY(camera.errorString() == QString("InvalidRequestError Error"));
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("InvalidRequestError Error"));
+ QVERIFY(camera.errorString() == QStringLiteral("InvalidRequestError Error"));
/* Set the QPlatformCamera error and verify if it is set correctly in QCamera */
- service->mockCameraControl->setError(QCamera::CameraError,QString("CameraError Error"));
- QVERIFY(camera.errorString() == QString("CameraError Error"));
+ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("CameraError Error"));
+ QVERIFY(camera.errorString() == QStringLiteral("CameraError Error"));
}
void tst_QCamera::testSetCameraFormat()
@@ -704,34 +679,34 @@ void tst_QCamera::testSetCameraFormat()
QCamera camera;
QCameraDevice device = camera.cameraDevice();
auto videoFormats = device.videoFormats();
- QVERIFY(videoFormats.count());
+ QVERIFY(videoFormats.size());
QCameraFormat cameraFormat = videoFormats.first();
- QSignalSpy spy(&camera, SIGNAL(cameraFormatChanged()));
- QVERIFY(spy.count() == 0);
+ QSignalSpy spy(&camera, &QCamera::cameraFormatChanged);
+ QVERIFY(spy.size() == 0);
camera.setCameraFormat(cameraFormat);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(camera.cameraFormat(), cameraFormat);
QCOMPARE(camera.cameraFormat().pixelFormat(), QVideoFrameFormat::Format_ARGB8888);
QCOMPARE(camera.cameraFormat().resolution(), QSize(640, 480));
QCOMPARE(camera.cameraFormat().minFrameRate(), 0);
QCOMPARE(camera.cameraFormat().maxFrameRate(), 30);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
spy.clear();
camera.setCameraFormat({});
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(camera.cameraFormat(), QCameraFormat());
spy.clear();
camera.setCameraDevice(QMediaDevices::videoInputs().at(1));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(camera.cameraFormat(), QCameraFormat());
camera.setCameraFormat(camera.cameraDevice().videoFormats().first());
QCOMPARE(camera.cameraFormat().pixelFormat(), QVideoFrameFormat::Format_XRGB8888);
QCOMPARE(camera.cameraFormat().resolution(), QSize(1280, 720));
QCOMPARE(camera.cameraFormat().minFrameRate(), 0);
QCOMPARE(camera.cameraFormat().maxFrameRate(), 30);
- QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.size(), 2);
}
//Added this code to cover QCamera::FocusModeHyperfocal and QCamera::FocusModeAutoNear
@@ -758,14 +733,14 @@ void tst_QCamera::testZoomChanged()
QCamera camera;
session.setCamera(&camera);
- QSignalSpy spy(&camera, SIGNAL(zoomFactorChanged(float)));
- QVERIFY(spy.count() == 0);
+ QSignalSpy spy(&camera, &QCamera::zoomFactorChanged);
+ QVERIFY(spy.size() == 0);
camera.setZoomFactor(2.0);
- QVERIFY(spy.count() == 1);
+ QVERIFY(spy.size() == 1);
camera.zoomTo(3.0, 1);
- QVERIFY(spy.count() == 2);
+ QVERIFY(spy.size() == 2);
camera.zoomTo(1.0, 0);
- QVERIFY(spy.count() == 3);
+ QVERIFY(spy.size() == 3);
}
void tst_QCamera::testMaxZoomChangedSignal()
@@ -773,12 +748,12 @@ void tst_QCamera::testMaxZoomChangedSignal()
QMediaCaptureSession session;
QCamera camera;
session.setCamera(&camera);
- QMockCamera *mock = integration.lastCamera();
+ QMockCamera *mock = QMockIntegration::instance()->lastCamera();
// ### change max zoom factor on backend, e.g. by changing camera
- QSignalSpy spy(&camera, SIGNAL(maximumZoomFactorChanged(float)));
+ QSignalSpy spy(&camera, &QCamera::maximumZoomFactorChanged);
mock->maximumZoomFactorChanged(55);
- QVERIFY(spy.count() == 1);
+ QVERIFY(spy.size() == 1);
QCOMPARE(camera.maximumZoomFactor(), 55);
}
@@ -788,9 +763,9 @@ void tst_QCamera::testSignalExposureCompensationChanged()
QCamera camera;
session.setCamera(&camera);
- QSignalSpy spyExposureCompensationChanged(&camera, SIGNAL(exposureCompensationChanged(float)));
+ QSignalSpy spyExposureCompensationChanged(&camera, &QCamera::exposureCompensationChanged);
- QVERIFY(spyExposureCompensationChanged.count() ==0);
+ QVERIFY(spyExposureCompensationChanged.size() == 0);
QVERIFY(camera.exposureCompensation() != 800);
camera.setExposureCompensation(2.0);
@@ -799,14 +774,14 @@ void tst_QCamera::testSignalExposureCompensationChanged()
QVERIFY(camera.exposureCompensation() == 2.0);
- QCOMPARE(spyExposureCompensationChanged.count(),1);
+ QCOMPARE(spyExposureCompensationChanged.size(),1);
// Setting the same should not result in a signal
camera.setExposureCompensation(2.0);
QTest::qWait(100);
QVERIFY(camera.exposureCompensation() == 2.0);
- QCOMPARE(spyExposureCompensationChanged.count(),1);
+ QCOMPARE(spyExposureCompensationChanged.size(),1);
}
void tst_QCamera::testSignalIsoSensitivityChanged()
@@ -815,13 +790,13 @@ void tst_QCamera::testSignalIsoSensitivityChanged()
QCamera camera;
session.setCamera(&camera);
- QSignalSpy spyisoSensitivityChanged(&camera, SIGNAL(isoSensitivityChanged(int)));
+ QSignalSpy spyisoSensitivityChanged(&camera, &QCamera::isoSensitivityChanged);
- QVERIFY(spyisoSensitivityChanged.count() ==0);
+ QVERIFY(spyisoSensitivityChanged.size() ==0);
camera.setManualIsoSensitivity(800); //set the manualiso sentivity to 800
QTest::qWait(100);
- QVERIFY(spyisoSensitivityChanged.count() ==1);
+ QVERIFY(spyisoSensitivityChanged.size() ==1);
}
void tst_QCamera::testSignalShutterSpeedChanged()
@@ -830,14 +805,14 @@ void tst_QCamera::testSignalShutterSpeedChanged()
QCamera camera;
session.setCamera(&camera);
- QSignalSpy spySignalExposureTimeChanged(&camera, SIGNAL(exposureTimeChanged(float)));
+ QSignalSpy spySignalExposureTimeChanged(&camera, &QCamera::exposureTimeChanged);
- QVERIFY(spySignalExposureTimeChanged.count() ==0);
+ QVERIFY(spySignalExposureTimeChanged.size() == 0);
camera.setManualExposureTime(2.0);//set the ManualShutterSpeed to 2.0
QTest::qWait(100);
- QVERIFY(spySignalExposureTimeChanged.count() ==1);
+ QVERIFY(spySignalExposureTimeChanged.size() ==1);
}
void tst_QCamera::testSignalFlashReady()
@@ -846,9 +821,9 @@ void tst_QCamera::testSignalFlashReady()
QCamera camera;
session.setCamera(&camera);
- QSignalSpy spyflashReady(&camera,SIGNAL(flashReady(bool)));
+ QSignalSpy spyflashReady(&camera, &QCamera::flashReady);
- QVERIFY(spyflashReady.count() == 0);
+ QVERIFY(spyflashReady.size() == 0);
QVERIFY(camera.flashMode() == QCamera::FlashAuto);
@@ -856,7 +831,7 @@ void tst_QCamera::testSignalFlashReady()
QVERIFY(camera.flashMode() == QCamera::FlashOff);
- QCOMPARE(spyflashReady.count(), 1);
+ QCOMPARE(spyflashReady.size(), 1);
}
QTEST_MAIN(tst_QCamera)
diff --git a/tests/auto/unit/multimedia/qcameradevice/CMakeLists.txt b/tests/auto/unit/multimedia/qcameradevice/CMakeLists.txt
index 150307fea..4d9977e8e 100644
--- a/tests/auto/unit/multimedia/qcameradevice/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qcameradevice/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qcameradevice.pro.
#####################################################################
@@ -9,9 +12,9 @@ qt_internal_add_test(tst_qcameradevice
tst_qcameradevice.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::MultimediaPrivate
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimedia/qcameradevice/tst_qcameradevice.cpp b/tests/auto/unit/multimedia/qcameradevice/tst_qcameradevice.cpp
index c8f93330f..7ca2b5980 100644
--- a/tests/auto/unit/multimedia/qcameradevice/tst_qcameradevice.cpp
+++ b/tests/auto/unit/multimedia/qcameradevice/tst_qcameradevice.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
@@ -33,11 +8,13 @@
#include <qcameradevice.h>
#include <qmediadevices.h>
-#include "qmockintegration_p.h"
+#include "qmockintegration.h"
#include "qmockmediacapturesession.h"
QT_USE_NAMESPACE
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
class tst_QCameraDevice: public QObject
{
Q_OBJECT
@@ -52,9 +29,6 @@ private slots:
void defaultCamera();
void availableCameras();
void equality_operators();
-
-private:
- QMockIntegration integration;
};
void tst_QCameraDevice::initTestCase()
@@ -119,7 +93,7 @@ void tst_QCameraDevice::defaultCamera()
void tst_QCameraDevice::availableCameras()
{
QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
- QCOMPARE(cameras.count(), 3);
+ QCOMPARE(cameras.size(), 3);
QCameraDevice info = cameras.at(0);
QVERIFY(!info.isNull());
@@ -133,7 +107,7 @@ void tst_QCameraDevice::availableCameras()
QCOMPARE(info.description(), QStringLiteral("frontCamera"));
QCOMPARE(info.position(), QCameraDevice::FrontFace);
- QCOMPARE(cameras.count(), 3);
+ QCOMPARE(cameras.size(), 3);
info = cameras.at(2);
QVERIFY(!info.isNull());
QCOMPARE(info.id(), QStringLiteral("back"));
diff --git a/tests/auto/unit/multimedia/qerrorinfo/CMakeLists.txt b/tests/auto/unit/multimedia/qerrorinfo/CMakeLists.txt
new file mode 100644
index 000000000..ea6ac5690
--- /dev/null
+++ b/tests/auto/unit/multimedia/qerrorinfo/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_qerrorinfo
+ SOURCES
+ tst_qerrorinfo.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::MultimediaPrivate
+)
diff --git a/tests/auto/unit/multimedia/qerrorinfo/tst_qerrorinfo.cpp b/tests/auto/unit/multimedia/qerrorinfo/tst_qerrorinfo.cpp
new file mode 100644
index 000000000..0d266705d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qerrorinfo/tst_qerrorinfo.cpp
@@ -0,0 +1,117 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// TESTED_COMPONENT=src/multimedia
+
+#include <QtTest/QtTest>
+#include <QSignalSpy>
+#include <private/qerrorinfo_p.h>
+
+QT_USE_NAMESPACE
+
+enum class TestError { ErrorA, ErrorB, NoError };
+
+using TestErrorInfo = QErrorInfo<TestError>;
+
+class TestNotifier : public QObject
+{
+ Q_OBJECT
+public:
+signals:
+ void errorOccurred(TestError error, QString errorDescription);
+ void errorChanged();
+};
+
+class tst_QErrorInfo : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void defaultConstructor_setsNoError();
+ void constructor_setsPassedError();
+
+ void setAndNotify_setsErrorAndNotifes_data();
+ void setAndNotify_setsErrorAndNotifes();
+};
+
+void tst_QErrorInfo::defaultConstructor_setsNoError()
+{
+ TestErrorInfo errorInfo;
+ QCOMPARE(errorInfo.code(), TestError::NoError);
+ QCOMPARE(errorInfo.description(), QString());
+}
+
+void tst_QErrorInfo::constructor_setsPassedError()
+{
+ TestErrorInfo errorInfo(TestError::ErrorB, "test error");
+ QCOMPARE(errorInfo.code(), TestError::ErrorB);
+ QCOMPARE(errorInfo.description(), "test error");
+}
+
+void tst_QErrorInfo::setAndNotify_setsErrorAndNotifes_data()
+{
+ QTest::addColumn<TestError>("initialError");
+ QTest::addColumn<QString>("initialErrorDescription");
+ QTest::addColumn<TestError>("error");
+ QTest::addColumn<QString>("errorDescription");
+ QTest::addColumn<bool>("errorChangedEmitted");
+ QTest::addColumn<bool>("errorOccurredEmitted");
+
+ QTest::newRow("No error -> No error")
+ << TestError::NoError << QString() << TestError::NoError << QString() << false << false;
+ QTest::newRow("No error -> An error with empty string")
+ << TestError::NoError << QString() << TestError::ErrorA << QString() << true << true;
+ QTest::newRow("No error -> An error with non-empty string")
+ << TestError::NoError << QString() << TestError::ErrorB << QStringLiteral("error") << true
+ << true;
+ QTest::newRow("An error with empty string -> No error")
+ << TestError::ErrorA << QString() << TestError::NoError << QString() << true << false;
+ QTest::newRow("An error with non-empty string -> No Error")
+ << TestError::ErrorA << QStringLiteral("error") << TestError::NoError << QString() << true
+ << false;
+ QTest::newRow("An error -> Another error")
+ << TestError::ErrorA << QStringLiteral("error A") << TestError::ErrorB << QStringLiteral("error B")
+ << true << true;
+ QTest::newRow("An error -> Another error with empty string")
+ << TestError::ErrorA << QStringLiteral("error A") << TestError::ErrorB << QString() << true
+ << true;
+ QTest::newRow("An error -> The same error with changed string")
+ << TestError::ErrorA << QStringLiteral("error") << TestError::ErrorA
+ << QStringLiteral("another error") << true << true;
+}
+
+void tst_QErrorInfo::setAndNotify_setsErrorAndNotifes()
+{
+ QFETCH(TestError, initialError);
+ QFETCH(QString, initialErrorDescription);
+ QFETCH(TestError, error);
+ QFETCH(QString, errorDescription);
+ QFETCH(bool, errorChangedEmitted);
+ QFETCH(bool, errorOccurredEmitted);
+
+ TestErrorInfo errorInfo(initialError, initialErrorDescription);
+
+ TestNotifier notifier;
+ QSignalSpy errorOccurredSpy(&notifier, &TestNotifier::errorOccurred);
+ QSignalSpy errorChangedSpy(&notifier, &TestNotifier::errorChanged);
+
+ errorInfo.setAndNotify(error, errorDescription, notifier);
+
+ QCOMPARE(errorInfo.code(), error);
+ QCOMPARE(errorInfo.description(), errorDescription);
+
+ QList<QList<QVariant>> expectedErrorChanged;
+ if (errorChangedEmitted)
+ expectedErrorChanged.push_back({});
+
+ QList<QList<QVariant>> expectedErrorOccured;
+ if (errorOccurredEmitted)
+ expectedErrorOccured.push_back({ QVariant::fromValue(error), errorDescription });
+
+ QCOMPARE(errorOccurredSpy, expectedErrorOccured);
+ QCOMPARE(errorChangedSpy, expectedErrorChanged);
+}
+
+QTEST_GUILESS_MAIN(tst_QErrorInfo)
+
+#include "tst_qerrorinfo.moc"
diff --git a/tests/auto/unit/multimedia/qimagecapture/CMakeLists.txt b/tests/auto/unit/multimedia/qimagecapture/CMakeLists.txt
index 11a2c79bc..908154cf9 100644
--- a/tests/auto/unit/multimedia/qimagecapture/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qimagecapture/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qimagecapture.pro.
#####################################################################
@@ -9,9 +12,9 @@ qt_internal_add_test(tst_qimagecapture
tst_qimagecapture.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::MultimediaPrivate
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp b/tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp
index b81f989db..3267b6f40 100644
--- a/tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp
+++ b/tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
@@ -36,20 +11,16 @@
#include <qmediacapturesession.h>
#include "qmockmediacapturesession.h"
-#include "qmockintegration_p.h"
+#include "qmockintegration.h"
QT_USE_NAMESPACE
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
class tst_QImageCapture: public QObject
{
Q_OBJECT
-public slots:
- void initTestCase();
- void init();
- void cleanup();
- void cleanupTestCase();
-
private slots:
void constructor();
void isAvailable();
@@ -63,29 +34,8 @@ private slots:
void imageExposed();
void imageSaved();
void readyForCaptureChanged();
-
-private:
- QMockIntegration *mockIntegration;
};
-void tst_QImageCapture::initTestCase()
-{
- mockIntegration = new QMockIntegration;
-}
-
-void tst_QImageCapture::init()
-{
-}
-
-void tst_QImageCapture::cleanup()
-{
-}
-
-void tst_QImageCapture::cleanupTestCase()
-{
- delete mockIntegration;
-}
-
void tst_QImageCapture::constructor()
{
QMediaCaptureSession session;
@@ -199,7 +149,7 @@ void tst_QImageCapture::errors()
session.setImageCapture(&imageCapture);
QVERIFY(imageCapture.isAvailable() == true);
- imageCapture.captureToFile(QString::fromLatin1("/dev/null"));
+ imageCapture.captureToFile(QStringLiteral("/dev/null"));
QCOMPARE(imageCapture.error(), QImageCapture::NotReadyError);
QVERIFY(!imageCapture.errorString().isEmpty());
}
@@ -228,10 +178,10 @@ void tst_QImageCapture::error()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy spy(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,QString)));
+ QSignalSpy spy(&imageCapture, &QImageCapture::errorOccurred);
imageCapture.captureToFile();
QTest::qWait(30);
- QVERIFY(spy.count() == 1);
+ QVERIFY(spy.size() == 1);
QVERIFY(qvariant_cast<int>(spy.at(0).at(0)) == -1);
QVERIFY(qvariant_cast<QImageCapture::Error>(spy.at(0).at(1)) == QImageCapture::NotReadyError);
QVERIFY(qvariant_cast<QString>(spy.at(0).at(2)) == "Could not capture in stopped state");
@@ -246,14 +196,14 @@ void tst_QImageCapture::imageCaptured()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy spy(&imageCapture, SIGNAL(imageCaptured(int,QImage)));
+ QSignalSpy spy(&imageCapture, &QImageCapture::imageCaptured);
QVERIFY(imageCapture.isAvailable() == true);
QVERIFY(imageCapture.isReadyForCapture() == false);
camera.start();
imageCapture.captureToFile();
QTRY_VERIFY(imageCapture.isReadyForCapture());
- QVERIFY(spy.count() == 1);
+ QVERIFY(spy.size() == 1);
QVERIFY(qvariant_cast<int>(spy.at(0).at(0)) > 0);
QImage image = qvariant_cast<QImage>(spy.at(0).at(1));
QVERIFY(image.isNull() == true);
@@ -269,14 +219,14 @@ void tst_QImageCapture::imageExposed()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy spy(&imageCapture, SIGNAL(imageExposed(int)));
+ QSignalSpy spy(&imageCapture, &QImageCapture::imageExposed);
QVERIFY(imageCapture.isAvailable() == true);
QVERIFY(imageCapture.isReadyForCapture() == false);
camera.start();
imageCapture.captureToFile();
QTRY_VERIFY(imageCapture.isReadyForCapture());
- QVERIFY(spy.count() == 1);
+ QVERIFY(spy.size() == 1);
QVERIFY(qvariant_cast<int>(spy.at(0).at(0)) > 0);
spy.clear();
camera.stop();
@@ -290,14 +240,14 @@ void tst_QImageCapture::imageSaved()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy spy(&imageCapture, SIGNAL(imageSaved(int,QString)));
+ QSignalSpy spy(&imageCapture, &QImageCapture::imageSaved);
QVERIFY(imageCapture.isAvailable() == true);
QVERIFY(imageCapture.isReadyForCapture() == false);
camera.start();
- imageCapture.captureToFile(QString::fromLatin1("/usr/share"));
+ imageCapture.captureToFile(QStringLiteral("/usr/share"));
QTRY_VERIFY(imageCapture.isReadyForCapture());
- QVERIFY(spy.count() == 1);
+ QVERIFY(spy.size() == 1);
QVERIFY(qvariant_cast<int>(spy.at(0).at(0)) > 0);
QVERIFY(qvariant_cast<QString>(spy.at(0).at(1)) == "/usr/share");
spy.clear();
@@ -312,17 +262,17 @@ void tst_QImageCapture::readyForCaptureChanged()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy spy(&imageCapture, SIGNAL(readyForCaptureChanged(bool)));
+ QSignalSpy spy(&imageCapture, &QImageCapture::readyForCaptureChanged);
QVERIFY(imageCapture.isReadyForCapture() == false);
imageCapture.captureToFile();
QTest::qWait(100);
- QVERIFY(spy.count() == 0);
+ QVERIFY(spy.size() == 0);
QVERIFY2(!imageCapture.errorString().isEmpty(),"Could not capture in stopped state" );
camera.start();
QTest::qWait(100);
imageCapture.captureToFile();
QTest::qWait(100);
- QVERIFY(spy.count() == 2);
+ QVERIFY(spy.size() == 2);
QVERIFY(spy.at(0).at(0).toBool() == false);
QVERIFY(spy.at(1).at(0).toBool() == true);
camera.stop();
diff --git a/tests/auto/unit/multimedia/qmaybe/CMakeLists.txt b/tests/auto/unit/multimedia/qmaybe/CMakeLists.txt
new file mode 100644
index 000000000..a9053e87c
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmaybe/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_qmaybe
+ SOURCES
+ tst_qmaybe.cpp
+ LIBRARIES
+ Qt::MultimediaPrivate
+)
diff --git a/tests/auto/unit/multimedia/qmaybe/tst_qmaybe.cpp b/tests/auto/unit/multimedia/qmaybe/tst_qmaybe.cpp
new file mode 100644
index 000000000..fbf21a26d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmaybe/tst_qmaybe.cpp
@@ -0,0 +1,124 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <private/qmaybe_p.h>
+#include <QtCore/private/quniquehandle_p.h>
+#ifdef Q_OS_WINDOWS
+#include <private/qcomptr_p.h>
+#include <private/qcomobject_p.h>
+#endif
+
+QT_USE_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace {
+
+// Helpers used to verify interop with QUniqueHandle and ComPtr which
+// overloads operator&
+struct DummyHandleTraits
+{
+ using Type = int;
+ static Type invalidValue() { return -1; }
+ static bool close(Type /*handle*/) { return true; }
+};
+
+using DummyHandle = QUniqueHandle<DummyHandleTraits>;
+
+#ifdef Q_OS_WINDOWS
+struct DummyComObject : QComObject<IUnknown>
+{
+};
+#endif
+
+} // namespace
+
+//clang-format off
+
+class tst_QMaybe : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void operatorBool_returnsFalse_onlyWhenErrorSet()
+ {
+ {
+ const QMaybe<QString, int> error{ -1 }; // TOOD: Is it safe to deduce expected/unexpected only based on type?
+ QVERIFY(!static_cast<bool>(error));
+ }
+
+ {
+ const QMaybe<QString, int> success{ "It worked!"_L1 };
+ QVERIFY(static_cast<bool>(success));
+ }
+ }
+
+ void value_returnsReferenceToValue_whenValueSet()
+ {
+ {
+ QMaybe mutableVal{ 1 };
+ mutableVal.value() = 2;
+ QCOMPARE_EQ(*mutableVal, 2); // value() must have returned a mutable reference
+ }
+
+ {
+ const QMaybe immutableVal{ 2 };
+ QCOMPARE_EQ(*std::addressof(immutableVal.value()), 2); // value() must have returned a reference
+ static_assert(std::is_const_v<std::remove_reference_t<decltype(immutableVal.value())>>); // And it is const
+ }
+ }
+
+ void dereferenceOperator_returnsPointerToValue_whenValueTypeOverloadsAddressOfOperator()
+ {
+ {
+ QMaybe<DummyHandle, int> mutableValue{ DummyHandle{ 1 } };
+ QCOMPARE_EQ(mutableValue->get(), 1);
+ QVERIFY(mutableValue->isValid()); // We did not accidentally call operator& that resets
+ // QUniqueHandle
+ }
+
+ {
+ const QMaybe<DummyHandle, int> immutableValue{ DummyHandle{ 2 } };
+ QCOMPARE_EQ(immutableValue->get(), 2);
+ QVERIFY(immutableValue->isValid()); // We did not accidentally call operator& that
+ // resets QUniqueHandle
+ }
+ }
+
+#ifdef Q_OS_WINDOWS
+ void dereferenceOperator_returnsPointerToValue_whenValueIsComPtr()
+ {
+ // Similar test as with QUniqueHandle, but with ComPtr that is used
+ // frequently on Windows and may behave slightly differently
+
+ {
+ QMaybe<ComPtr<DummyComObject>, HRESULT> mutableObject{
+ makeComObject<DummyComObject>()
+ };
+ QCOMPARE_NE(mutableObject->Get(), nullptr);
+
+ const ComPtr<IUnknown> unknownFromMutable = mutableObject.value();
+ QVERIFY(unknownFromMutable); // We did not accidentally call operator& that resets
+ // QUniqueHandle
+ }
+
+ {
+ QMaybe<ComPtr<DummyComObject>, HRESULT> immutableObject{
+ makeComObject<DummyComObject>()
+ };
+ QCOMPARE_NE(immutableObject->Get(), nullptr);
+
+ const ComPtr<IUnknown> unknownFromImmutable = immutableObject.value();
+ QVERIFY(unknownFromImmutable); // We did not accidentally call operator& that resets
+ // QUniqueHandle
+ }
+ }
+#endif
+};
+
+QTEST_APPLESS_MAIN(tst_QMaybe)
+
+#include "tst_qmaybe.moc"
+
+//clang-format on
diff --git a/tests/auto/unit/multimedia/qmediacapture_gstreamer/CMakeLists.txt b/tests/auto/unit/multimedia/qmediacapture_gstreamer/CMakeLists.txt
new file mode 100644
index 000000000..8a4734434
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmediacapture_gstreamer/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qmediacapture_gstreamer Test:
+#####################################################################
+
+qt_internal_add_test(tst_qmediacapture_gstreamer
+ SOURCES
+ tst_qmediacapture_gstreamer.cpp
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::QGstreamerMediaPluginPrivate
+)
diff --git a/tests/auto/unit/multimedia/qmediacapture_gstreamer/tst_qmediacapture_gstreamer.cpp b/tests/auto/unit/multimedia/qmediacapture_gstreamer/tst_qmediacapture_gstreamer.cpp
new file mode 100644
index 000000000..55bce42f5
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmediacapture_gstreamer/tst_qmediacapture_gstreamer.cpp
@@ -0,0 +1,75 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QtMultimedia/QMediaCaptureSession>
+#include <QtMultimedia/private/qplatformmediacapture_p.h>
+#include <QtQGstreamerMediaPlugin/private/qgstpipeline_p.h>
+
+#include <memory>
+
+QT_USE_NAMESPACE
+
+class tst_QMediaCaptureGStreamer : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QMediaCaptureGStreamer();
+
+public slots:
+ void init();
+ void cleanup();
+
+private slots:
+ void constructor_preparesGstPipeline();
+
+private:
+ std::unique_ptr<QMediaCaptureSession> session;
+
+ GstPipeline *getGstPipeline()
+ {
+ return reinterpret_cast<GstPipeline *>(
+ QPlatformMediaCaptureSession::nativePipeline(session.get()));
+ }
+
+ void dumpGraph(const char *fileNamePrefix)
+ {
+ GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(getGstPipeline()),
+ GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_VERBOSE),
+ fileNamePrefix);
+ }
+};
+
+tst_QMediaCaptureGStreamer::tst_QMediaCaptureGStreamer()
+{
+ qputenv("QT_MEDIA_BACKEND", "gstreamer");
+}
+
+void tst_QMediaCaptureGStreamer::init()
+{
+ session = std::make_unique<QMediaCaptureSession>();
+}
+
+void tst_QMediaCaptureGStreamer::cleanup()
+{
+ session.reset();
+}
+
+void tst_QMediaCaptureGStreamer::constructor_preparesGstPipeline()
+{
+ auto *rawPipeline = getGstPipeline();
+ QVERIFY(rawPipeline);
+
+ QGstPipeline pipeline{
+ rawPipeline,
+ QGstPipeline::NeedsRef,
+ };
+ QVERIFY(pipeline);
+
+ dumpGraph("constructor_preparesGstPipeline");
+}
+
+QTEST_GUILESS_MAIN(tst_QMediaCaptureGStreamer)
+
+#include "tst_qmediacapture_gstreamer.moc"
diff --git a/tests/auto/unit/multimedia/qmediadevices/CMakeLists.txt b/tests/auto/unit/multimedia/qmediadevices/CMakeLists.txt
new file mode 100644
index 000000000..e8360f8b5
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmediadevices/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_qmediadevices
+ SOURCES
+ tst_qmediadevices.cpp
+ INCLUDE_DIRECTORIES
+ ../../mockbackend
+ LIBRARIES
+ Qt::Gui
+ Qt::MultimediaPrivate
+ MockMultimediaPlugin
+)
diff --git a/tests/auto/unit/multimedia/qmediadevices/tst_qmediadevices.cpp b/tests/auto/unit/multimedia/qmediadevices/tst_qmediadevices.cpp
new file mode 100644
index 000000000..9767c0e73
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmediadevices/tst_qmediadevices.cpp
@@ -0,0 +1,58 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QDebug>
+
+#include <qmediadevices.h>
+
+#include "qmockintegration.h"
+
+QT_USE_NAMESPACE
+
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
+class tst_QMediaDevices : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void videoInputsChangedEmitted_whenCamerasChanged();
+ void onlyVideoInputsChangedEmitted_when2MediaDevicesCreated_andCamerasChanged();
+};
+
+void tst_QMediaDevices::videoInputsChangedEmitted_whenCamerasChanged()
+{
+ QMediaDevices mediaDevices;
+ QSignalSpy videoInputsSpy(&mediaDevices, &QMediaDevices::videoInputsChanged);
+
+ QCOMPARE(videoInputsSpy.size(), 0);
+
+ QMockIntegration::instance()->addNewCamera();
+ QTRY_COMPARE(videoInputsSpy.size(), 1);
+
+ QMockIntegration::instance()->addNewCamera();
+ QCOMPARE(videoInputsSpy.size(), 2);
+}
+
+void tst_QMediaDevices::onlyVideoInputsChangedEmitted_when2MediaDevicesCreated_andCamerasChanged()
+{
+ QMediaDevices mediaDevicesA;
+ QMediaDevices mediaDevicesB;
+
+ QSignalSpy videoInputsSpyA(&mediaDevicesA, &QMediaDevices::videoInputsChanged);
+ QSignalSpy videoInputsSpyB(&mediaDevicesB, &QMediaDevices::videoInputsChanged);
+ QSignalSpy audioInputsSpy(&mediaDevicesA, &QMediaDevices::audioInputsChanged);
+ QSignalSpy audioOutputsSpy(&mediaDevicesA, &QMediaDevices::audioOutputsChanged);
+
+ QMockIntegration::instance()->addNewCamera();
+ QCOMPARE(videoInputsSpyA.size(), 1);
+ QCOMPARE(videoInputsSpyB.size(), 1);
+
+ QCOMPARE(audioInputsSpy.size(), 0);
+ QCOMPARE(audioOutputsSpy.size(), 0);
+}
+
+QTEST_MAIN(tst_QMediaDevices)
+
+#include "tst_qmediadevices.moc"
diff --git a/tests/auto/unit/multimedia/qmediaformat/CMakeLists.txt b/tests/auto/unit/multimedia/qmediaformat/CMakeLists.txt
index 23c4ad18c..4ad36e7bb 100644
--- a/tests/auto/unit/multimedia/qmediaformat/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qmediaformat/CMakeLists.txt
@@ -1,7 +1,10 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
qt_internal_add_test(tst_qmediaformat
SOURCES
tst_qmediaformat.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/unit/multimedia/qmediaformat/tst_qmediaformat.cpp b/tests/auto/unit/multimedia/qmediaformat/tst_qmediaformat.cpp
index b3d45e727..9f1345db6 100644
--- a/tests/auto/unit/multimedia/qmediaformat/tst_qmediaformat.cpp
+++ b/tests/auto/unit/multimedia/qmediaformat/tst_qmediaformat.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
diff --git a/tests/auto/unit/multimedia/qmediaplayer/CMakeLists.txt b/tests/auto/unit/multimedia/qmediaplayer/CMakeLists.txt
index cc9e39503..4d3f2f865 100644
--- a/tests/auto/unit/multimedia/qmediaplayer/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qmediaplayer/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmediaplayer.pro.
#####################################################################
@@ -9,12 +12,12 @@ qt_internal_add_test(tst_qmediaplayer
tst_qmediaplayer.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::MultimediaPrivate
Qt::Network
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
# Resources:
diff --git a/tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp b/tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp
index bf48b7f9a..3fb77ca2d 100644
--- a/tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp
+++ b/tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -38,7 +11,7 @@
#include <private/qplatformmediaplayer_p.h>
#include <qobject.h>
-#include "qmockintegration_p.h"
+#include "qmockintegration.h"
#include "qmockmediaplayer.h"
#include "qmockaudiooutput.h"
#include "qvideosink.h"
@@ -46,6 +19,8 @@
QT_USE_NAMESPACE
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
class AutoConnection
{
public:
@@ -123,7 +98,6 @@ private slots:
private:
void setupCommonTestData();
- QMockIntegration *mockIntegration;
QMockMediaPlayer *mockPlayer;
QAudioOutput *audioOutput = nullptr;
QMediaPlayer *player;
@@ -200,7 +174,7 @@ void tst_QMediaPlayer::setupCommonTestData()
qreal(1) << QMediaPlayer::NoError << QString();
QTest::newRow("valid+error") << true << QMediaPlayer::StoppedState << QMediaPlayer::InvalidMedia <<
QUrl(QUrl("http://example.com/stream")) << qint64(0) << qint64(0) << false << 50 << false << false << 0 <<
- qreal(0) << QMediaPlayer::ResourceError << QString("Resource unavailable");
+ qreal(0) << QMediaPlayer::ResourceError << QStringLiteral("Resource unavailable");
}
void tst_QMediaPlayer::initTestCase()
@@ -213,9 +187,8 @@ void tst_QMediaPlayer::cleanupTestCase()
void tst_QMediaPlayer::init()
{
- mockIntegration = new QMockIntegration;
player = new QMediaPlayer;
- mockPlayer = mockIntegration->lastPlayer();
+ mockPlayer = QMockIntegration::instance()->lastPlayer();
Q_ASSERT(mockPlayer);
audioOutput = new QAudioOutput;
player->setAudioOutput(audioOutput);
@@ -225,7 +198,6 @@ void tst_QMediaPlayer::init()
void tst_QMediaPlayer::cleanup()
{
delete player;
- delete mockIntegration;
}
void tst_QMediaPlayer::testValid()
@@ -322,36 +294,44 @@ void tst_QMediaPlayer::testPosition()
QVERIFY(player->duration() == duration);
if (seekable) {
- { QSignalSpy spy(player, SIGNAL(positionChanged(qint64)));
- player->setPosition(position);
- QCOMPARE(player->position(), position);
- QCOMPARE(spy.count(), 0); }
+ {
+ QSignalSpy spy(player, &QMediaPlayer::positionChanged);
+ player->setPosition(position);
+ QCOMPARE(player->position(), position);
+ QCOMPARE(spy.size(), 0);
+ }
mockPlayer->setPosition(position);
- { QSignalSpy spy(player, SIGNAL(positionChanged(qint64)));
- player->setPosition(0);
- QCOMPARE(player->position(), qint64(0));
- QCOMPARE(spy.count(), position == 0 ? 0 : 1); }
+ {
+ QSignalSpy spy(player, &QMediaPlayer::positionChanged);
+ player->setPosition(0);
+ QCOMPARE(player->position(), qint64(0));
+ QCOMPARE(spy.size(), position == 0 ? 0 : 1);
+ }
mockPlayer->setPosition(position);
- { QSignalSpy spy(player, SIGNAL(positionChanged(qint64)));
- player->setPosition(duration);
- QCOMPARE(player->position(), duration);
- QCOMPARE(spy.count(), position == duration ? 0 : 1); }
+ {
+ QSignalSpy spy(player, &QMediaPlayer::positionChanged);
+ player->setPosition(duration);
+ QCOMPARE(player->position(), duration);
+ QCOMPARE(spy.size(), position == duration ? 0 : 1);
+ }
mockPlayer->setPosition(position);
- { QSignalSpy spy(player, SIGNAL(positionChanged(qint64)));
- player->setPosition(-1);
- QCOMPARE(player->position(), qint64(0));
- QCOMPARE(spy.count(), position == 0 ? 0 : 1); }
+ {
+ QSignalSpy spy(player, &QMediaPlayer::positionChanged);
+ player->setPosition(-1);
+ QCOMPARE(player->position(), qint64(0));
+ QCOMPARE(spy.size(), position == 0 ? 0 : 1);
+ }
}
else {
- QSignalSpy spy(player, SIGNAL(positionChanged(qint64)));
+ QSignalSpy spy(player, &QMediaPlayer::positionChanged);
player->setPosition(position);
QCOMPARE(player->position(), position);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
}
}
@@ -370,25 +350,33 @@ void tst_QMediaPlayer::testVolume()
QVERIFY(audioOutput->volume() == vol);
if (valid) {
- { QSignalSpy spy(audioOutput, SIGNAL(volumeChanged(float)));
- audioOutput->setVolume(.1f);
- QCOMPARE(audioOutput->volume(), .1f);
- QCOMPARE(spy.count(), 1); }
-
- { QSignalSpy spy(audioOutput, SIGNAL(volumeChanged(float)));
- audioOutput->setVolume(-1000.f);
- QCOMPARE(audioOutput->volume(), 0.f);
- QCOMPARE(spy.count(), 1); }
-
- { QSignalSpy spy(audioOutput, SIGNAL(volumeChanged(float)));
- audioOutput->setVolume(1.f);
- QCOMPARE(audioOutput->volume(), 1.f);
- QCOMPARE(spy.count(), 1); }
-
- { QSignalSpy spy(audioOutput, SIGNAL(volumeChanged(float)));
- audioOutput->setVolume(1000.f);
- QCOMPARE(audioOutput->volume(), 1.f);
- QCOMPARE(spy.count(), 0); }
+ {
+ QSignalSpy spy(audioOutput, &QAudioOutput::volumeChanged);
+ audioOutput->setVolume(.1f);
+ QCOMPARE(audioOutput->volume(), .1f);
+ QCOMPARE(spy.size(), 1);
+ }
+
+ {
+ QSignalSpy spy(audioOutput, &QAudioOutput::volumeChanged);
+ audioOutput->setVolume(-1000.f);
+ QCOMPARE(audioOutput->volume(), 0.f);
+ QCOMPARE(spy.size(), 1);
+ }
+
+ {
+ QSignalSpy spy(audioOutput, &QAudioOutput::volumeChanged);
+ audioOutput->setVolume(1.f);
+ QCOMPARE(audioOutput->volume(), 1.f);
+ QCOMPARE(spy.size(), 1);
+ }
+
+ {
+ QSignalSpy spy(audioOutput, &QAudioOutput::volumeChanged);
+ audioOutput->setVolume(1000.f);
+ QCOMPARE(audioOutput->volume(), 1.f);
+ QCOMPARE(spy.size(), 0);
+ }
}
}
@@ -409,11 +397,11 @@ void tst_QMediaPlayer::testMuted()
audioOutput->setVolume(vol);
QVERIFY(audioOutput->isMuted() == muted);
- QSignalSpy spy(audioOutput, SIGNAL(mutedChanged(bool)));
+ QSignalSpy spy(audioOutput, &QAudioOutput::mutedChanged);
audioOutput->setMuted(!muted);
QCOMPARE(audioOutput->isMuted(), !muted);
QCOMPARE(audioOutput->volume(), vol);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
}
}
@@ -470,10 +458,10 @@ void tst_QMediaPlayer::testPlaybackRate()
mockPlayer->setPlaybackRate(playbackRate);
QVERIFY(player->playbackRate() == playbackRate);
- QSignalSpy spy(player, SIGNAL(playbackRateChanged(qreal)));
+ QSignalSpy spy(player, &QMediaPlayer::playbackRateChanged);
player->setPlaybackRate(playbackRate + 0.5f);
QCOMPARE(player->playbackRate(), playbackRate + 0.5f);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
}
}
@@ -537,19 +525,25 @@ void tst_QMediaPlayer::testPlay()
player->setSource(mediaContent);
mockPlayer->setState(state);
QCOMPARE(player->playbackState(), state);
+ QCOMPARE(player->isPlaying(), state == QMediaPlayer::PlayingState);
QCOMPARE(player->source(), mediaContent);
- QSignalSpy spy(player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)));
+ QSignalSpy spy(player, &QMediaPlayer::playbackStateChanged);
+ QSignalSpy playingChanged(player, &QMediaPlayer::playingChanged);
player->play();
if (!valid || mediaContent.isEmpty()) {
QCOMPARE(player->playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(spy.count(), 0);
+ QVERIFY(!player->isPlaying());
+ QCOMPARE(spy.size(), 0);
+ QVERIFY(playingChanged.empty());
}
else {
QCOMPARE(player->playbackState(), QMediaPlayer::PlayingState);
- QCOMPARE(spy.count(), state == QMediaPlayer::PlayingState ? 0 : 1);
+ QVERIFY(player->isPlaying());
+ QCOMPARE(spy.size(), state == QMediaPlayer::PlayingState ? 0 : 1);
+ QCOMPARE_EQ(playingChanged.size(), state == QMediaPlayer::PlayingState ? 0 : 1);
}
}
@@ -568,19 +562,25 @@ void tst_QMediaPlayer::testPause()
player->setSource(mediaContent);
mockPlayer->setState(state);
QVERIFY(player->playbackState() == state);
+ QCOMPARE(player->isPlaying(), state == QMediaPlayer::PlayingState);
QVERIFY(player->source() == mediaContent);
- QSignalSpy spy(player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)));
+ QSignalSpy spy(player, &QMediaPlayer::playbackStateChanged);
+ QSignalSpy playingChanged(player, &QMediaPlayer::playingChanged);
player->pause();
+ QVERIFY(!player->isPlaying());
+
if (!valid || mediaContent.isEmpty()) {
QCOMPARE(player->playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
+ QCOMPARE(playingChanged.size(), 0);
}
else {
QCOMPARE(player->playbackState(), QMediaPlayer::PausedState);
- QCOMPARE(spy.count(), state == QMediaPlayer::PausedState ? 0 : 1);
+ QCOMPARE(spy.size(), state == QMediaPlayer::PausedState ? 0 : 1);
+ QCOMPARE(playingChanged.size(), state == QMediaPlayer::PlayingState ? 1 : 0);
}
}
@@ -597,19 +597,25 @@ void tst_QMediaPlayer::testStop()
player->setSource(mediaContent);
mockPlayer->setState(state);
QVERIFY(player->playbackState() == state);
+ QCOMPARE(player->isPlaying(), state == QMediaPlayer::PlayingState);
QVERIFY(player->source() == mediaContent);
- QSignalSpy spy(player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)));
+ QSignalSpy spy(player, &QMediaPlayer::playbackStateChanged);
+ QSignalSpy playingChanged(player, &QMediaPlayer::playingChanged);
player->stop();
+ QVERIFY(!player->isPlaying());
+
if (mediaContent.isEmpty() || state == QMediaPlayer::StoppedState) {
QCOMPARE(player->playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
+ QCOMPARE(playingChanged.size(), 0);
}
else {
QCOMPARE(player->playbackState(), QMediaPlayer::StoppedState);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(playingChanged.size(), state == QMediaPlayer::PlayingState ? 1 : 0);
}
}
@@ -626,65 +632,65 @@ void tst_QMediaPlayer::testMediaStatus()
mockPlayer->setMediaStatus(QMediaPlayer::NoMedia);
mockPlayer->setBufferStatus(bufferProgress);
- QSignalSpy statusSpy(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
- QSignalSpy bufferSpy(player, SIGNAL(bufferProgressChanged(float)));
+ QSignalSpy statusSpy(player, &QMediaPlayer::mediaStatusChanged);
+ QSignalSpy bufferSpy(player, &QMediaPlayer::bufferProgressChanged);
QCOMPARE(player->mediaStatus(), QMediaPlayer::NoMedia);
mockPlayer->setMediaStatus(QMediaPlayer::LoadingMedia);
QCOMPARE(player->mediaStatus(), QMediaPlayer::LoadingMedia);
- QCOMPARE(statusSpy.count(), 1);
+ QCOMPARE(statusSpy.size(), 1);
QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(statusSpy.last().value(0)),
QMediaPlayer::LoadingMedia);
mockPlayer->setMediaStatus(QMediaPlayer::LoadedMedia);
QCOMPARE(player->mediaStatus(), QMediaPlayer::LoadedMedia);
- QCOMPARE(statusSpy.count(), 2);
+ QCOMPARE(statusSpy.size(), 2);
QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(statusSpy.last().value(0)),
QMediaPlayer::LoadedMedia);
// Verify the bufferProgressChanged() signal isn't being emitted.
- QCOMPARE(bufferSpy.count(), 0);
+ QCOMPARE(bufferSpy.size(), 0);
mockPlayer->setMediaStatus(QMediaPlayer::StalledMedia);
QCOMPARE(player->mediaStatus(), QMediaPlayer::StalledMedia);
- QCOMPARE(statusSpy.count(), 3);
+ QCOMPARE(statusSpy.size(), 3);
QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(statusSpy.last().value(0)),
QMediaPlayer::StalledMedia);
// Verify the bufferProgressChanged() signal is being emitted.
- QVERIFY(bufferSpy.count() > bufferSignals);
+ QVERIFY(bufferSpy.size() > bufferSignals);
QCOMPARE(bufferSpy.last().value(0).toInt(), bufferProgress);
- bufferSignals = bufferSpy.count();
+ bufferSignals = bufferSpy.size();
mockPlayer->setMediaStatus(QMediaPlayer::BufferingMedia);
QCOMPARE(player->mediaStatus(), QMediaPlayer::BufferingMedia);
- QCOMPARE(statusSpy.count(), 4);
+ QCOMPARE(statusSpy.size(), 4);
QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(statusSpy.last().value(0)),
QMediaPlayer::BufferingMedia);
// Verify the bufferProgressChanged() signal is being emitted.
- QVERIFY(bufferSpy.count() > bufferSignals);
+ QVERIFY(bufferSpy.size() > bufferSignals);
QCOMPARE(bufferSpy.last().value(0).toInt(), bufferProgress);
- bufferSignals = bufferSpy.count();
+ bufferSignals = bufferSpy.size();
mockPlayer->setMediaStatus(QMediaPlayer::BufferedMedia);
QCOMPARE(player->mediaStatus(), QMediaPlayer::BufferedMedia);
- QCOMPARE(statusSpy.count(), 5);
+ QCOMPARE(statusSpy.size(), 5);
QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(statusSpy.last().value(0)),
QMediaPlayer::BufferedMedia);
// Verify the bufferProgressChanged() signal isn't being emitted.
- QCOMPARE(bufferSpy.count(), bufferSignals);
+ QCOMPARE(bufferSpy.size(), bufferSignals);
mockPlayer->setMediaStatus(QMediaPlayer::EndOfMedia);
QCOMPARE(player->mediaStatus(), QMediaPlayer::EndOfMedia);
- QCOMPARE(statusSpy.count(), 6);
+ QCOMPARE(statusSpy.size(), 6);
QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(statusSpy.last().value(0)),
QMediaPlayer::EndOfMedia);
@@ -796,22 +802,22 @@ void tst_QMediaPlayer::testQrc()
mockPlayer->setState(QMediaPlayer::PlayingState, QMediaPlayer::NoMedia);
mockPlayer->setStreamPlaybackSupported(backendHasStream);
- QSignalSpy mediaSpy(player, SIGNAL(sourceChanged(QUrl)));
- QSignalSpy statusSpy(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
- QSignalSpy errorSpy(player, SIGNAL(errorOccurred(QMediaPlayer::Error,const QString&)));
+ QSignalSpy mediaSpy(player, &QMediaPlayer::sourceChanged);
+ QSignalSpy statusSpy(player, &QMediaPlayer::mediaStatusChanged);
+ QSignalSpy errorSpy(player, &QMediaPlayer::errorOccurred);
player->setSource(mediaContent);
QTRY_COMPARE(player->mediaStatus(), status);
- QCOMPARE(statusSpy.count(), 1);
+ QCOMPARE(statusSpy.size(), 1);
QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(statusSpy.last().value(0)), status);
QCOMPARE(player->source(), mediaContent);
- QCOMPARE(mediaSpy.count(), 1);
+ QCOMPARE(mediaSpy.size(), 1);
QCOMPARE(qvariant_cast<QUrl>(mediaSpy.last().value(0)), mediaContent);
QCOMPARE(player->error(), error);
- QCOMPARE(errorSpy.count(), errorCount);
+ QCOMPARE(errorSpy.size(), errorCount);
if (errorCount > 0) {
QCOMPARE(qvariant_cast<QMediaPlayer::Error>(errorSpy.last().value(0)), error);
QVERIFY(!player->errorString().isEmpty());
diff --git a/tests/auto/unit/multimedia/qmediaplayer_gstreamer/CMakeLists.txt b/tests/auto/unit/multimedia/qmediaplayer_gstreamer/CMakeLists.txt
new file mode 100644
index 000000000..2437df00c
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmediaplayer_gstreamer/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qmediaplayer_gstreamer Test:
+#####################################################################
+
+qt_internal_add_test(tst_qmediaplayer_gstreamer
+ SOURCES
+ tst_qmediaplayer_gstreamer.cpp
+ LIBRARIES
+ Qt::MultimediaPrivate
+ Qt::QGstreamerMediaPluginPrivate
+)
diff --git a/tests/auto/unit/multimedia/qmediaplayer_gstreamer/tst_qmediaplayer_gstreamer.cpp b/tests/auto/unit/multimedia/qmediaplayer_gstreamer/tst_qmediaplayer_gstreamer.cpp
new file mode 100644
index 000000000..a11e25f29
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmediaplayer_gstreamer/tst_qmediaplayer_gstreamer.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QtMultimedia/qmediaplayer.h>
+#include <QtMultimedia/private/qmediaplayer_p.h>
+#include <QtQGstreamerMediaPlugin/private/qgstpipeline_p.h>
+
+#include <memory>
+
+QT_USE_NAMESPACE
+
+class tst_QMediaPlayerGStreamer : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QMediaPlayerGStreamer();
+
+public slots:
+ void init();
+ void cleanup();
+
+private slots:
+ void constructor_preparesGstPipeline();
+
+private:
+ std::unique_ptr<QMediaPlayer> player;
+
+ GstPipeline *getGstPipeline()
+ {
+ return reinterpret_cast<GstPipeline *>(QPlatformMediaPlayer::nativePipeline(player.get()));
+ }
+
+ void dumpGraph(const char *fileNamePrefix)
+ {
+ GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(getGstPipeline()),
+ GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_VERBOSE),
+ fileNamePrefix);
+ }
+};
+
+tst_QMediaPlayerGStreamer::tst_QMediaPlayerGStreamer()
+{
+ qputenv("QT_MEDIA_BACKEND", "gstreamer");
+}
+
+void tst_QMediaPlayerGStreamer::init()
+{
+ player = std::make_unique<QMediaPlayer>();
+}
+
+void tst_QMediaPlayerGStreamer::cleanup()
+{
+ player.reset();
+}
+
+void tst_QMediaPlayerGStreamer::constructor_preparesGstPipeline()
+{
+ auto *rawPipeline = getGstPipeline();
+ QVERIFY(rawPipeline);
+
+ QGstPipeline pipeline{
+ rawPipeline,
+ QGstPipeline::NeedsRef,
+ };
+ QVERIFY(pipeline);
+
+ QVERIFY(pipeline.findByName("videoInputSelector"));
+ QVERIFY(pipeline.findByName("audioInputSelector"));
+ QVERIFY(pipeline.findByName("subTitleInputSelector"));
+
+ dumpGraph("constructor_preparesGstPipeline");
+}
+
+QTEST_GUILESS_MAIN(tst_QMediaPlayerGStreamer)
+
+#include "tst_qmediaplayer_gstreamer.moc"
diff --git a/tests/auto/unit/multimedia/qmediaplaylist/CMakeLists.txt b/tests/auto/unit/multimedia/qmediaplaylist/CMakeLists.txt
index 33d4d6d14..9300394b1 100644
--- a/tests/auto/unit/multimedia/qmediaplaylist/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qmediaplaylist/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmediaplaylist.pro.
#####################################################################
@@ -13,7 +16,7 @@ list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_qmediaplaylist
SOURCES
tst_qmediaplaylist.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
TESTDATA ${test_data}
diff --git a/tests/auto/unit/multimedia/qmediaplaylist/tst_qmediaplaylist.cpp b/tests/auto/unit/multimedia/qmediaplaylist/tst_qmediaplaylist.cpp
index 1c641f713..8ef882bbf 100644
--- a/tests/auto/unit/multimedia/qmediaplaylist/tst_qmediaplaylist.cpp
+++ b/tests/auto/unit/multimedia/qmediaplaylist/tst_qmediaplaylist.cpp
@@ -1,37 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
#include "qmediaplaylist.h"
-//TESTED_COMPONENT=src/multimedia
-
QT_USE_NAMESPACE
class tst_QMediaPlaylist : public QObject
diff --git a/tests/auto/unit/multimedia/qmediarecorder/CMakeLists.txt b/tests/auto/unit/multimedia/qmediarecorder/CMakeLists.txt
index 68234d7ab..83e40012a 100644
--- a/tests/auto/unit/multimedia/qmediarecorder/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qmediarecorder/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from QMediaRecorder.pro.
#####################################################################
@@ -9,9 +12,9 @@ qt_internal_add_test(tst_qmediarecorder
tst_qmediarecorder.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::MultimediaPrivate
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp b/tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp
index 9808d065d..a11f39207 100644
--- a/tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp
+++ b/tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
@@ -35,8 +8,10 @@
#include "private/qguiapplication_p.h"
#include <qmediarecorder.h>
#include <qaudioformat.h>
-#include <qmockintegration_p.h>
+#include <qmockintegration.h>
#include <qmediacapturesession.h>
+#include <qscreencapture.h>
+#include <qwindowcapture.h>
#include "qguiapplication_platform.h"
#include "qmockmediacapturesession.h"
@@ -44,6 +19,8 @@
QT_USE_NAMESPACE
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
class tst_QMediaRecorder : public QObject
{
Q_OBJECT
@@ -55,7 +32,7 @@ public slots:
private slots:
void testBasicSession();
void testNullControls();
- void testDeleteMediaSource();
+ void testDeleteMediaCapture();
void testError();
void testSink();
void testRecord();
@@ -75,7 +52,6 @@ private slots:
void testApplicationInative();
private:
- QMockIntegration *mockIntegration = nullptr;
QMediaCaptureSession *captureSession;
QCamera *object = nullptr;
QMockMediaCaptureSession *service = nullptr;
@@ -85,13 +61,12 @@ private:
void tst_QMediaRecorder::initTestCase()
{
- mockIntegration = new QMockIntegration;
captureSession = new QMediaCaptureSession;
object = new QCamera;
encoder = new QMediaRecorder;
captureSession->setCamera(object);
captureSession->setRecorder(encoder);
- service = mockIntegration->lastCaptureService();
+ service = QMockIntegration::instance()->lastCaptureService();
mock = service->mockControl;
}
@@ -100,7 +75,6 @@ void tst_QMediaRecorder::cleanupTestCase()
delete encoder;
delete object;
delete captureSession;
- delete mockIntegration;
}
void tst_QMediaRecorder::testBasicSession()
@@ -151,56 +125,80 @@ void tst_QMediaRecorder::testNullControls()
QCOMPARE(recorder.mediaFormat().videoCodec(), QMediaFormat::VideoCodec::VP9);
QCOMPARE(recorder.mediaFormat().fileFormat(), QMediaFormat::MPEG4);
- QSignalSpy spy(&recorder, SIGNAL(recorderStateChanged(RecorderState)));
+ QSignalSpy spy(&recorder, &QMediaRecorder::recorderStateChanged);
recorder.record();
QCOMPARE(recorder.recorderState(), QMediaRecorder::StoppedState);
QCOMPARE(recorder.error(), QMediaRecorder::NoError);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
recorder.pause();
QCOMPARE(recorder.recorderState(), QMediaRecorder::StoppedState);
QCOMPARE(recorder.error(), QMediaRecorder::NoError);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
recorder.stop();
QCOMPARE(recorder.recorderState(), QMediaRecorder::StoppedState);
QCOMPARE(recorder.error(), QMediaRecorder::NoError);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
}
-void tst_QMediaRecorder::testDeleteMediaSource()
+void tst_QMediaRecorder::testDeleteMediaCapture()
{
QMediaCaptureSession session;
- QCamera *camera = new QCamera;
- QMediaRecorder *recorder = new QMediaRecorder;
- session.setCamera(camera);
- session.setRecorder(recorder);
+ QMediaRecorder recorder;
+
+ session.setRecorder(&recorder);
+
+ auto checkSourceDeleting = [&](auto setter, auto getter, auto signal) {
+ using Type = std::remove_pointer_t<decltype((session.*getter)())>;
+
+ auto errorPrinter = qScopeGuard(
+ []() { qDebug() << QMetaType::fromType<Type>().name() << "deleting failed"; });
+
+ auto capture = std::make_unique<Type>();
+
+ (session.*setter)(capture.get());
+
+ QVERIFY((session.*getter)() == capture.get());
+
+ QSignalSpy spy(&session, signal);
+ capture.reset();
- QVERIFY(session.camera() == camera);
- QVERIFY(recorder->isAvailable());
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE((session.*getter)(), nullptr);
- delete camera;
+ QVERIFY(recorder.isAvailable());
- QVERIFY(session.camera() == nullptr);
- QVERIFY(recorder->isAvailable());
+ errorPrinter.dismiss();
+ };
- delete recorder;
+ checkSourceDeleting(&QMediaCaptureSession::setImageCapture,
+ &QMediaCaptureSession::imageCapture,
+ &QMediaCaptureSession::imageCaptureChanged);
+ checkSourceDeleting(&QMediaCaptureSession::setCamera, &QMediaCaptureSession::camera,
+ &QMediaCaptureSession::cameraChanged);
+ checkSourceDeleting(&QMediaCaptureSession::setScreenCapture,
+ &QMediaCaptureSession::screenCapture,
+ &QMediaCaptureSession::screenCaptureChanged);
+ checkSourceDeleting(&QMediaCaptureSession::setWindowCapture,
+ &QMediaCaptureSession::windowCapture,
+ &QMediaCaptureSession::windowCaptureChanged);
}
void tst_QMediaRecorder::testError()
{
const QString errorString(QLatin1String("format error"));
- QSignalSpy spy(encoder, SIGNAL(errorOccurred(Error, const QString&)));
+ QSignalSpy spy(encoder, &QMediaRecorder::errorOccurred);
QCOMPARE(encoder->error(), QMediaRecorder::NoError);
QCOMPARE(encoder->errorString(), QString());
- mock->error(QMediaRecorder::FormatError, errorString);
+ mock->updateError(QMediaRecorder::FormatError, errorString);
QCOMPARE(encoder->error(), QMediaRecorder::FormatError);
QCOMPARE(encoder->errorString(), errorString);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.last()[0].value<QMediaRecorder::Error>(), QMediaRecorder::FormatError);
}
@@ -209,14 +207,14 @@ void tst_QMediaRecorder::testSink()
{
encoder->setOutputLocation(QUrl("test.tmp"));
QUrl s = encoder->outputLocation();
- QCOMPARE(s.toString(), QString("test.tmp"));
+ QCOMPARE(s.toString(), QStringLiteral("test.tmp"));
QCOMPARE(encoder->actualLocation(), QUrl());
//the actual location is available after record
encoder->record();
- QCOMPARE(encoder->actualLocation().toString(), QString("test.tmp"));
+ QCOMPARE(encoder->actualLocation().toString(), QStringLiteral("test.tmp"));
encoder->stop();
- QCOMPARE(encoder->actualLocation().toString(), QString("test.tmp"));
+ QCOMPARE(encoder->actualLocation().toString(), QStringLiteral("test.tmp"));
//setOutputLocation resets the actual location
encoder->setOutputLocation(QUrl());
@@ -232,37 +230,37 @@ void tst_QMediaRecorder::testSink()
void tst_QMediaRecorder::testRecord()
{
- QSignalSpy stateSignal(encoder,SIGNAL(recorderStateChanged(RecorderState)));
- QSignalSpy progressSignal(encoder, SIGNAL(durationChanged(qint64)));
+ QSignalSpy stateSignal(encoder, &QMediaRecorder::recorderStateChanged);
+ QSignalSpy progressSignal(encoder, &QMediaRecorder::durationChanged);
encoder->record();
QCOMPARE(encoder->recorderState(), QMediaRecorder::RecordingState);
QCOMPARE(encoder->error(), QMediaRecorder::NoError);
QCOMPARE(encoder->errorString(), QString());
- QCOMPARE(stateSignal.count(), 1);
+ QCOMPARE(stateSignal.size(), 1);
QCOMPARE(stateSignal.last()[0].value<QMediaRecorder::RecorderState>(), QMediaRecorder::RecordingState);
QTestEventLoop::instance().enterLoop(1);
- QVERIFY(progressSignal.count() > 0);
+ QVERIFY(progressSignal.size() > 0);
encoder->pause();
QCOMPARE(encoder->recorderState(), QMediaRecorder::PausedState);
- QCOMPARE(stateSignal.count(), 2);
+ QCOMPARE(stateSignal.size(), 2);
QTestEventLoop::instance().enterLoop(1);
encoder->stop();
QCOMPARE(encoder->recorderState(), QMediaRecorder::StoppedState);
- QCOMPARE(stateSignal.count(), 3);
+ QCOMPARE(stateSignal.size(), 3);
QTestEventLoop::instance().enterLoop(1);
mock->stop();
- QCOMPARE(stateSignal.count(), 3);
+ QCOMPARE(stateSignal.size(), 3);
mock->reset();
}
@@ -388,10 +386,10 @@ void tst_QMediaRecorder::metaData()
QVERIFY(recorder.metaData().isEmpty());
QMediaMetaData data;
- data.insert(QMediaMetaData::Author, QString::fromUtf8("John Doe"));
+ data.insert(QMediaMetaData::Author, QStringLiteral("John Doe"));
recorder.setMetaData(data);
- QCOMPARE(recorder.metaData().value(QMediaMetaData::Author).toString(), QString::fromUtf8("John Doe"));
+ QCOMPARE(recorder.metaData().value(QMediaMetaData::Author).toString(), QStringLiteral("John Doe"));
}
void tst_QMediaRecorder::testIsAvailable()
@@ -415,15 +413,15 @@ void tst_QMediaRecorder::testEnum()
{
const QString errorString(QLatin1String("resource error"));
- QSignalSpy spy(encoder, SIGNAL(errorOccurred(Error, const QString&)));
+ QSignalSpy spy(encoder, &QMediaRecorder::errorOccurred);
QCOMPARE(encoder->error(), QMediaRecorder::NoError);
QCOMPARE(encoder->errorString(), QString());
- emit mock->error(QMediaRecorder::ResourceError, errorString);
+ mock->updateError(QMediaRecorder::ResourceError, errorString);
QCOMPARE(encoder->error(), QMediaRecorder::ResourceError);
QCOMPARE(encoder->errorString(), errorString);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.last()[0].value<QMediaRecorder::Error>(), QMediaRecorder::ResourceError);
}
@@ -473,7 +471,7 @@ void tst_QMediaRecorder::testApplicationInative()
encoder.setQuality(QMediaRecorder::VeryHighQuality);
encoder.setOutputLocation(QUrl("test.tmp"));
- QCOMPARE(encoder.outputLocation().toString(), QString("test.tmp"));
+ QCOMPARE(encoder.outputLocation().toString(), QStringLiteral("test.tmp"));
QCOMPARE(encoder.actualLocation(), QUrl());
encoder.record();
@@ -487,7 +485,7 @@ void tst_QMediaRecorder::testApplicationInative()
encoder.stop();
// the actual location is available after record
- QCOMPARE(encoder.actualLocation().toString(), QString("test.tmp"));
+ QCOMPARE(encoder.actualLocation().toString(), QStringLiteral("test.tmp"));
}
QTEST_GUILESS_MAIN(tst_QMediaRecorder)
diff --git a/tests/auto/unit/multimedia/qmediatimerange/CMakeLists.txt b/tests/auto/unit/multimedia/qmediatimerange/CMakeLists.txt
index a414e2635..507eb16fa 100644
--- a/tests/auto/unit/multimedia/qmediatimerange/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qmediatimerange/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmediatimerange.pro.
#####################################################################
@@ -7,7 +10,7 @@
qt_internal_add_test(tst_qmediatimerange
SOURCES
tst_qmediatimerange.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/unit/multimedia/qmediatimerange/tst_qmediatimerange.cpp b/tests/auto/unit/multimedia/qmediatimerange/tst_qmediatimerange.cpp
index acccdea08..750b72108 100644
--- a/tests/auto/unit/multimedia/qmediatimerange/tst_qmediatimerange.cpp
+++ b/tests/auto/unit/multimedia/qmediatimerange/tst_qmediatimerange.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/qdebug.h>
@@ -170,7 +143,7 @@ void tst_QMediaTimeRange::testGetters()
// isEmpty + isContinuous + intervals + start + end
QVERIFY(!x.isEmpty());
QVERIFY(!x.isContinuous());
- QVERIFY(x.intervals().count() == 2);
+ QVERIFY(x.intervals().size() == 2);
QVERIFY(x.intervals()[0].start() == 10);
QVERIFY(x.intervals()[0].end() == 20);
QVERIFY(x.intervals()[1].start() == 30);
@@ -316,7 +289,7 @@ void tst_QMediaTimeRange::testAddInterval()
QVERIFY(!x.isEmpty());
QVERIFY(!x.isContinuous());
- QVERIFY(x.intervals().count() == 2);
+ QVERIFY(x.intervals().size() == 2);
QVERIFY(x.intervals()[0].start() == 10);
QVERIFY(x.intervals()[0].end() == 40);
QVERIFY(x.intervals()[1].start() == 50);
@@ -466,7 +439,7 @@ void tst_QMediaTimeRange::testRemoveInterval()
QVERIFY(!x.isEmpty());
QVERIFY(!x.isContinuous());
- QVERIFY(x.intervals().count() == 2);
+ QVERIFY(x.intervals().size() == 2);
QVERIFY(x.intervals()[0].start() == 10);
QVERIFY(x.intervals()[0].end() == 19);
QVERIFY(x.intervals()[1].start() == 41);
@@ -514,7 +487,7 @@ void tst_QMediaTimeRange::testRemoveInterval()
QVERIFY(!x.isEmpty());
QVERIFY(!x.isContinuous());
- QVERIFY(x.intervals().count() == 2);
+ QVERIFY(x.intervals().size() == 2);
QVERIFY(x.intervals()[0].start() == 10);
QVERIFY(x.intervals()[0].end() == 14);
QVERIFY(x.intervals()[1].start() == 36);
@@ -536,7 +509,7 @@ void tst_QMediaTimeRange::testRemoveInterval()
QVERIFY(!x.isEmpty());
QVERIFY(!x.isContinuous());
- QVERIFY(x.intervals().count() == 2);
+ QVERIFY(x.intervals().size() == 2);
QVERIFY(x.intervals()[0].start() == 10);
QVERIFY(x.intervals()[0].end() == 19);
QVERIFY(x.intervals()[1].start() == 41);
@@ -597,7 +570,7 @@ void tst_QMediaTimeRange::testRemoveTimeRange()
QVERIFY(!b.isEmpty());
QVERIFY(!b.isContinuous());
- QVERIFY(b.intervals().count() == 2);
+ QVERIFY(b.intervals().size() == 2);
QVERIFY(b.intervals()[0].start() == 10);
QVERIFY(b.intervals()[0].end() == 19);
QVERIFY(b.intervals()[1].start() == 51);
@@ -618,7 +591,7 @@ void tst_QMediaTimeRange::testRemoveTimeRange()
QVERIFY(!b.isEmpty());
QVERIFY(!b.isContinuous());
- QVERIFY(b.intervals().count() == 2);
+ QVERIFY(b.intervals().size() == 2);
QVERIFY(b.intervals()[0].start() == 10);
QVERIFY(b.intervals()[0].end() == 19);
QVERIFY(b.intervals()[1].start() == 91);
diff --git a/tests/auto/unit/multimedia/qmultimediautils/CMakeLists.txt b/tests/auto/unit/multimedia/qmultimediautils/CMakeLists.txt
new file mode 100644
index 000000000..2dd805271
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmultimediautils/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_qmultimediautils
+ SOURCES
+ tst_qmultimediautils.cpp
+ LIBRARIES
+ Qt::MultimediaPrivate
+)
diff --git a/tests/auto/unit/multimedia/qmultimediautils/tst_qmultimediautils.cpp b/tests/auto/unit/multimedia/qmultimediautils/tst_qmultimediautils.cpp
new file mode 100644
index 000000000..8ed54ac64
--- /dev/null
+++ b/tests/auto/unit/multimedia/qmultimediautils/tst_qmultimediautils.cpp
@@ -0,0 +1,184 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QDebug>
+#include <private/qmultimediautils_p.h>
+#include <qvideoframeformat.h>
+
+class tst_QMultimediaUtils : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void fraction_of_0();
+ void fraction_of_negative_1_5();
+ void fraction_of_1_5();
+ void fraction_of_30();
+ void fraction_of_29_97();
+ void fraction_of_lower_boundary();
+ void fraction_of_upper_boundary();
+
+ void qRotatedFrameSize_returnsSizeAccordinglyToRotation();
+
+ void qMediaFromUserInput_addsFilePrefix_whenCalledWithLocalFile();
+
+ void qGetRequiredSwapChainFormat_returnsSdr_whenMaxLuminanceIsBelowSdrThreshold_data();
+ void qGetRequiredSwapChainFormat_returnsSdr_whenMaxLuminanceIsBelowSdrThreshold();
+ void qGetRequiredSwapChainFormat_returnsHdr_whenMaxLuminanceIsBelowHdrThreshold_data();
+ void qGetRequiredSwapChainFormat_returnsHdr_whenMaxLuminanceIsBelowHdrThreshold();
+
+ void qShouldUpdateSwapChainFormat_returnsFalse_whenSwapChainIsNullPointer();
+};
+
+void tst_QMultimediaUtils::fraction_of_0()
+{
+ auto [n, d] = qRealToFraction(0.);
+ QCOMPARE(n, 0);
+ QCOMPARE(d, 1);
+}
+
+void tst_QMultimediaUtils::fraction_of_negative_1_5()
+{
+ auto [n, d] = qRealToFraction(-1.5);
+ QCOMPARE(double(n) / double(d), -1.5);
+ QCOMPARE(n, -3);
+ QCOMPARE(d, 2);
+}
+
+void tst_QMultimediaUtils::fraction_of_1_5()
+{
+ auto [n, d] = qRealToFraction(1.5);
+ QCOMPARE(double(n) / double(d), 1.5);
+ QCOMPARE(n, 3);
+ QCOMPARE(d, 2);
+}
+
+void tst_QMultimediaUtils::fraction_of_30()
+{
+ auto [n, d] = qRealToFraction(30.);
+ QCOMPARE(double(n) / double(d), 30.);
+ QCOMPARE(d, 1);
+}
+
+void tst_QMultimediaUtils::fraction_of_29_97()
+{
+ auto [n, d] = qRealToFraction(29.97);
+ QCOMPARE(double(n) / double(d), 29.97);
+}
+
+void tst_QMultimediaUtils::fraction_of_lower_boundary()
+{
+ double f = 0.000001;
+ auto [n, d] = qRealToFraction(f);
+ QVERIFY(double(n) / double(d) < f);
+ QVERIFY(double(n) / double(d) >= 0.);
+}
+
+void tst_QMultimediaUtils::fraction_of_upper_boundary()
+{
+ double f = 0.999999;
+ auto [n, d] = qRealToFraction(f);
+ QVERIFY(double(n) / double(d) <= 1.);
+ QVERIFY(double(n) / double(d) > f);
+}
+
+void tst_QMultimediaUtils::qRotatedFrameSize_returnsSizeAccordinglyToRotation()
+{
+ QCOMPARE(qRotatedFrameSize({ 10, 22 }, 0), QSize(10, 22));
+ QCOMPARE(qRotatedFrameSize({ 10, 23 }, -180), QSize(10, 23));
+ QCOMPARE(qRotatedFrameSize({ 10, 24 }, 180), QSize(10, 24));
+ QCOMPARE(qRotatedFrameSize({ 10, 25 }, 360), QSize(10, 25));
+ QCOMPARE(qRotatedFrameSize({ 11, 26 }, 540), QSize(11, 26));
+
+ QCOMPARE(qRotatedFrameSize({ 10, 22 }, -90), QSize(22, 10));
+ QCOMPARE(qRotatedFrameSize({ 10, 23 }, 90), QSize(23, 10));
+ QCOMPARE(qRotatedFrameSize({ 10, 24 }, 270), QSize(24, 10));
+ QCOMPARE(qRotatedFrameSize({ 10, 25 }, 450), QSize(25, 10));
+
+ QCOMPARE(qRotatedFrameSize({ 10, 22 }, QtVideo::Rotation::None), QSize(10, 22));
+ QCOMPARE(qRotatedFrameSize({ 10, 22 }, QtVideo::Rotation::Clockwise180), QSize(10, 22));
+
+ QCOMPARE(qRotatedFrameSize({ 11, 22 }, QtVideo::Rotation::Clockwise90), QSize(22, 11));
+ QCOMPARE(qRotatedFrameSize({ 11, 22 }, QtVideo::Rotation::Clockwise270), QSize(22, 11));
+}
+
+void tst_QMultimediaUtils::qMediaFromUserInput_addsFilePrefix_whenCalledWithLocalFile()
+{
+ using namespace Qt::Literals;
+
+ QCOMPARE(qMediaFromUserInput(QUrl(u"/foo/bar/baz"_s)), QUrl(u"file:///foo/bar/baz"_s));
+ QCOMPARE(qMediaFromUserInput(QUrl::fromLocalFile(u"C:/foo/bar/baz"_s)),
+ QUrl(u"file:///C:/foo/bar/baz"_s));
+ QCOMPARE(qMediaFromUserInput(QUrl(u"file:///foo/bar/baz"_s)), QUrl(u"file:///foo/bar/baz"_s));
+ QCOMPARE(qMediaFromUserInput(QUrl(u"http://foo/bar/baz"_s)), QUrl(u"http://foo/bar/baz"_s));
+
+ QCOMPARE(qMediaFromUserInput(QUrl(u"foo/bar/baz"_s)),
+ QUrl::fromLocalFile(QDir::currentPath() + u"/foo/bar/baz"_s));
+}
+
+void tst_QMultimediaUtils::
+ qGetRequiredSwapChainFormat_returnsSdr_whenMaxLuminanceIsBelowSdrThreshold_data()
+{
+ QTest::addColumn<float>("maxLuminance");
+
+ QTest::newRow("0") << 0.0f;
+ QTest::newRow("80") << 80.0f;
+ QTest::newRow("100") << 100.0f;
+}
+
+void tst_QMultimediaUtils::
+ qGetRequiredSwapChainFormat_returnsSdr_whenMaxLuminanceIsBelowSdrThreshold()
+{
+ // Arrange
+ QFETCH(float, maxLuminance);
+
+ QVideoFrameFormat format;
+ format.setMaxLuminance(maxLuminance);
+
+ // Act
+ QRhiSwapChain::Format requiredSwapChainFormat = qGetRequiredSwapChainFormat(format);
+
+ // Assert
+ QCOMPARE(requiredSwapChainFormat, QRhiSwapChain::Format::SDR);
+}
+
+void tst_QMultimediaUtils::
+ qGetRequiredSwapChainFormat_returnsHdr_whenMaxLuminanceIsBelowHdrThreshold_data()
+{
+ QTest::addColumn<float>("maxLuminance");
+
+ QTest::newRow("101") << 101.0f;
+ QTest::newRow("300") << 300.0f;
+ QTest::newRow("1600") << 1600.0f;
+}
+
+void tst_QMultimediaUtils::
+ qGetRequiredSwapChainFormat_returnsHdr_whenMaxLuminanceIsBelowHdrThreshold()
+{
+ // Arrange
+ QVideoFrameFormat format;
+ format.setMaxLuminance(300.0f);
+
+ // Act
+ QRhiSwapChain::Format requiredSwapChainFormat = qGetRequiredSwapChainFormat(format);
+
+ // Assert
+ QCOMPARE(requiredSwapChainFormat, QRhiSwapChain::Format::HDRExtendedSrgbLinear);
+}
+
+void tst_QMultimediaUtils::qShouldUpdateSwapChainFormat_returnsFalse_whenSwapChainIsNullPointer()
+{
+ // Arrange
+ QRhiSwapChain *swapChain = nullptr;
+ QRhiSwapChain::Format requiredSwapChainFormat = QRhiSwapChain::Format::SDR;
+
+ // Act
+ bool shouldUpdate = qShouldUpdateSwapChainFormat(swapChain, requiredSwapChainFormat);
+
+ // Assert
+ QCOMPARE(shouldUpdate, false);
+}
+
+QTEST_MAIN(tst_QMultimediaUtils)
+#include "tst_qmultimediautils.moc"
diff --git a/tests/auto/unit/multimedia/qsamplecache/CMakeLists.txt b/tests/auto/unit/multimedia/qsamplecache/CMakeLists.txt
index 279bd9800..2f58c1f6e 100644
--- a/tests/auto/unit/multimedia/qsamplecache/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qsamplecache/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qsamplecache.pro.
#####################################################################
@@ -13,7 +16,7 @@ list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_qsamplecache
SOURCES
tst_qsamplecache.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
TESTDATA ${test_data}
diff --git a/tests/auto/unit/multimedia/qsamplecache/tst_qsamplecache.cpp b/tests/auto/unit/multimedia/qsamplecache/tst_qsamplecache.cpp
index 5baea0278..311d05a5a 100644
--- a/tests/auto/unit/multimedia/qsamplecache/tst_qsamplecache.cpp
+++ b/tests/auto/unit/multimedia/qsamplecache/tst_qsamplecache.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <private/qsamplecache_p.h>
diff --git a/tests/auto/unit/multimedia/qscreencapture/CMakeLists.txt b/tests/auto/unit/multimedia/qscreencapture/CMakeLists.txt
new file mode 100644
index 000000000..f5b152034
--- /dev/null
+++ b/tests/auto/unit/multimedia/qscreencapture/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qscreencapture Test:
+#####################################################################
+
+qt_internal_add_test(tst_qscreencapture
+ SOURCES
+ tst_qscreencapture.cpp
+ INCLUDE_DIRECTORIES
+ ../../mockbackend
+ LIBRARIES
+ # Remove: L${CMAKE_CURRENT_SOURCE_DIR}
+ Qt::Gui
+ Qt::MultimediaPrivate
+ MockMultimediaPlugin
+)
diff --git a/tests/auto/unit/multimedia/qscreencapture/tst_qscreencapture.cpp b/tests/auto/unit/multimedia/qscreencapture/tst_qscreencapture.cpp
new file mode 100644
index 000000000..ad574ddf6
--- /dev/null
+++ b/tests/auto/unit/multimedia/qscreencapture/tst_qscreencapture.cpp
@@ -0,0 +1,64 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// TESTED_COMPONENT=src/multimedia
+
+#include <QtTest/QtTest>
+#include <QDebug>
+
+#include "qmockintegration.h"
+#include "qscreencapture.h"
+#include "qmocksurfacecapture.h"
+#include "qatomic.h"
+
+QT_USE_NAMESPACE
+
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
+class tst_QScreenCapture : public QObject
+{
+ Q_OBJECT
+
+private:
+ // Use custom waiting instead of QSignalSpy since the spy tries copying not sharable object
+ // QVideoFrame to QVariant and gets an assert
+ bool waitForFrame(QPlatformSurfaceCapture &psc)
+ {
+ QAtomicInteger<bool> newFrameReceived = false;
+ QObject o;
+ auto connection = connect(&psc, &QPlatformSurfaceCapture::newVideoFrame, &o,
+ [&newFrameReceived]() { newFrameReceived = true; });
+
+ return QTest::qWaitFor([&newFrameReceived]() { return newFrameReceived; });
+ }
+
+private slots:
+ void destructionOfActiveCapture();
+
+};
+
+void tst_QScreenCapture::destructionOfActiveCapture()
+{
+ // Run a few times in order to catch random UB on deletion
+ for (int i = 0; i < 10; ++i) {
+ auto sc = std::make_unique<QScreenCapture>();
+ QPointer<QPlatformSurfaceCapture> psc = QMockIntegration::instance()->lastScreenCapture();
+ QVERIFY(psc);
+
+ sc->setActive(true);
+
+ QVERIFY(waitForFrame(*psc));
+
+ QSignalSpy spy(sc.get(), &QScreenCapture::activeChanged);
+
+ psc->setParent(nullptr);
+ sc.reset();
+
+ QVERIFY2(spy.empty(), "No signals from QScreenCapture are expected on deletion");
+ QVERIFY2(!psc, "Platform screen capture must be deleted whether or not it has a parent");
+ }
+}
+
+QTEST_MAIN(tst_QScreenCapture)
+
+#include "tst_qscreencapture.moc"
diff --git a/tests/auto/unit/multimedia/qvideobuffers/CMakeLists.txt b/tests/auto/unit/multimedia/qvideobuffers/CMakeLists.txt
new file mode 100644
index 000000000..765d02c96
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideobuffers/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_qvideobuffers
+ SOURCES
+ tst_qvideobuffers.cpp
+ LIBRARIES
+ Qt::Multimedia
+ Qt::MultimediaPrivate
+)
diff --git a/tests/auto/unit/multimedia/qvideobuffers/tst_qvideobuffers.cpp b/tests/auto/unit/multimedia/qvideobuffers/tst_qvideobuffers.cpp
new file mode 100644
index 000000000..97f5e3b62
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideobuffers/tst_qvideobuffers.cpp
@@ -0,0 +1,296 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+
+#include <private/qmemoryvideobuffer_p.h>
+#include <private/qimagevideobuffer_p.h>
+#include "qvideoframeformat.h"
+
+using BufferPtr = std::shared_ptr<QAbstractVideoBuffer>;
+using MapModes = std::vector<QVideoFrame::MapMode>;
+
+static const MapModes validMapModes = { QVideoFrame::ReadOnly, QVideoFrame::WriteOnly, QVideoFrame::ReadWrite };
+
+class tst_QVideoBuffers : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QVideoBuffers() {}
+public slots:
+ void initTestCase();
+
+private slots:
+ void map_changesMappedStateAndReturnsProperMappings_whenBufferIsNotMapped_data();
+ void map_changesMappedStateAndReturnsProperMappings_whenBufferIsNotMapped();
+
+ void mapWithNotMappedMode_doesNothing_data();
+ void mapWithNotMappedMode_doesNothing();
+
+ void map_doesNothing_whenBufferIsMapped_data();
+ void map_doesNothing_whenBufferIsMapped();
+
+ void mapMemoryBufferWithReadOnly_doesntDetachArray();
+
+ void mapMemoryBufferWithWriteModes_detachsArray_data();
+ void mapMemoryBufferWithWriteModes_detachsArray();
+
+ void underlyingByteArray_returnsCorrectValueForPlanes();
+
+ void unmap_resetsMappedState_whenBufferIsMapped_data();
+ void unmap_resetsMappedState_whenBufferIsMapped();
+
+ void imageBuffer_fixesInputImage_data();
+ void imageBuffer_fixesInputImage();
+
+private:
+ QString mapModeToString(QVideoFrame::MapMode mapMode) const
+ {
+ switch (mapMode) {
+ case QVideoFrame::NotMapped:
+ return QLatin1String("NotMapped");
+ case QVideoFrame::ReadOnly:
+ return QLatin1String("ReadOnly");
+ case QVideoFrame::WriteOnly:
+ return QLatin1String("WriteOnly");
+ case QVideoFrame::ReadWrite:
+ return QLatin1String("ReadWrite");
+ default:
+ return QLatin1String("Unknown");
+ }
+ }
+
+ void generateImageAndMemoryBuffersWithAllModes(const MapModes& modes = validMapModes) const
+ {
+ QTest::addColumn<BufferPtr>("buffer");
+ QTest::addColumn<QVideoFrame::MapMode>("mapMode");
+
+ for (auto mode : modes) {
+ QTest::newRow(QStringLiteral("ImageBuffer, %1").arg(mapModeToString(mode)).toUtf8().constData())
+ << createImageBuffer() << mode;
+ QTest::newRow(QStringLiteral("MemoryBuffer, %1").arg(mapModeToString(mode)).toUtf8().constData())
+ << createMemoryBuffer() << mode;
+ }
+ }
+
+ BufferPtr createImageBuffer() const
+ {
+ return std::make_shared<QImageVideoBuffer>(m_image);
+ }
+
+ BufferPtr createMemoryBuffer() const
+ {
+ return std::make_shared<QMemoryVideoBuffer>(m_byteArray, m_byteArray.size() / m_image.height());
+ }
+
+ QImage m_image = { QSize(5, 4), QImage::Format_RGBA8888 };
+ QByteArray m_byteArray;
+};
+
+
+void tst_QVideoBuffers::initTestCase()
+{
+ m_image.fill(Qt::gray);
+ m_image.setPixelColor(0, 0, Qt::green);
+ m_image.setPixelColor(m_image.width() - 1, 0, Qt::blue);
+ m_image.setPixelColor(0, m_image.height() - 1, Qt::red);
+
+ m_byteArray.assign(m_image.constBits(), m_image.constBits() + m_image.sizeInBytes());
+}
+
+void tst_QVideoBuffers::map_changesMappedStateAndReturnsProperMappings_whenBufferIsNotMapped_data()
+{
+ generateImageAndMemoryBuffersWithAllModes();
+}
+
+void tst_QVideoBuffers::map_changesMappedStateAndReturnsProperMappings_whenBufferIsNotMapped()
+{
+ QFETCH(BufferPtr, buffer);
+ QFETCH(QVideoFrame::MapMode, mapMode);
+
+ auto mappedData = buffer->map(mapMode);
+
+ QCOMPARE(buffer->mapMode(), mapMode);
+
+ QCOMPARE(mappedData.nPlanes, 1);
+ QVERIFY(mappedData.data[0]);
+ QCOMPARE(mappedData.size[0], 80);
+ QCOMPARE(mappedData.bytesPerLine[0], 20);
+
+ const auto data = reinterpret_cast<const char*>(mappedData.data[0]);
+ QCOMPARE(QByteArray(data, mappedData.size[0]), m_byteArray);
+}
+
+void tst_QVideoBuffers::mapWithNotMappedMode_doesNothing_data()
+{
+ generateImageAndMemoryBuffersWithAllModes();
+}
+
+void tst_QVideoBuffers::mapWithNotMappedMode_doesNothing()
+{
+ QFETCH(BufferPtr, buffer);
+ QFETCH(QVideoFrame::MapMode, mapMode);
+
+ buffer->map(mapMode);
+
+ buffer->map(QVideoFrame::NotMapped);
+
+ QCOMPARE(buffer->mapMode(), mapMode);
+}
+
+void tst_QVideoBuffers::map_doesNothing_whenBufferIsMapped_data()
+{
+ generateImageAndMemoryBuffersWithAllModes();
+}
+
+void tst_QVideoBuffers::map_doesNothing_whenBufferIsMapped()
+{
+ QFETCH(BufferPtr, buffer);
+ QFETCH(QVideoFrame::MapMode, mapMode);
+
+ buffer->map(mapMode);
+ auto mappedData = buffer->map(QVideoFrame::ReadOnly);
+ QCOMPARE(mappedData.nPlanes, 0);
+ QCOMPARE(buffer->mapMode(), mapMode);
+}
+
+void tst_QVideoBuffers::mapMemoryBufferWithReadOnly_doesntDetachArray()
+{
+ auto buffer = createMemoryBuffer();
+ auto underlyingArray = buffer->underlyingByteArray(0);
+
+ auto mappedData = buffer->map(QVideoFrame::ReadOnly);
+ QCOMPARE(mappedData.nPlanes, 1);
+ QCOMPARE(mappedData.data[0], reinterpret_cast<const uchar *>(underlyingArray.constData()));
+ QCOMPARE(mappedData.data[0], reinterpret_cast<const uchar *>(m_byteArray.constData()));
+}
+
+void tst_QVideoBuffers::mapMemoryBufferWithWriteModes_detachsArray_data()
+{
+ QTest::addColumn<QVideoFrame::MapMode>("mapMode");
+
+ QTest::newRow(mapModeToString(QVideoFrame::WriteOnly).toUtf8().constData()) << QVideoFrame::WriteOnly;
+ QTest::newRow(mapModeToString(QVideoFrame::WriteOnly).toUtf8().constData()) << QVideoFrame::WriteOnly;
+}
+
+void tst_QVideoBuffers::mapMemoryBufferWithWriteModes_detachsArray()
+{
+ QFETCH(QVideoFrame::MapMode, mapMode);
+
+ auto buffer = createMemoryBuffer();
+ auto underlyingArray = buffer->underlyingByteArray(0);
+
+ auto mappedData = buffer->map(mapMode);
+ QCOMPARE(mappedData.nPlanes, 1);
+ QCOMPARE_NE(mappedData.data[0], reinterpret_cast<const uchar *>(underlyingArray.constData()));
+}
+
+void tst_QVideoBuffers::underlyingByteArray_returnsCorrectValueForPlanes()
+{
+ auto buffer = createMemoryBuffer();
+
+ QCOMPARE(buffer->underlyingByteArray(0).constData(), m_byteArray.constData());
+
+ QVERIFY(buffer->underlyingByteArray(-1).isNull());
+ QVERIFY(buffer->underlyingByteArray(1).isNull());
+ QVERIFY(buffer->underlyingByteArray(2).isNull());
+}
+
+void tst_QVideoBuffers::unmap_resetsMappedState_whenBufferIsMapped_data()
+{
+ generateImageAndMemoryBuffersWithAllModes();
+}
+
+void tst_QVideoBuffers::unmap_resetsMappedState_whenBufferIsMapped()
+{
+ QFETCH(BufferPtr, buffer);
+ QFETCH(QVideoFrame::MapMode, mapMode);
+
+ buffer->map(mapMode);
+
+ buffer->unmap();
+
+ QCOMPARE(buffer->mapMode(), QVideoFrame::NotMapped);
+
+ // Check buffer is valid and it's possible to map again
+ auto mappedData = buffer->map(QVideoFrame::ReadOnly);
+ QCOMPARE(mappedData.nPlanes, 1);
+ QCOMPARE(buffer->mapMode(), QVideoFrame::ReadOnly);
+
+ const auto data = reinterpret_cast<const char*>(mappedData.data[0]);
+ QCOMPARE(QByteArray(data, mappedData.size[0]), m_byteArray);
+}
+
+void tst_QVideoBuffers::imageBuffer_fixesInputImage_data()
+{
+ QTest::addColumn<QImage::Format>("inputImageFormat");
+ QTest::addColumn<QImage::Format>("underlyingImageFormat");
+
+ QTest::newRow("Format_RGB32 => Format_RGB32") << QImage::Format_RGB32 << QImage::Format_RGB32;
+ QTest::newRow("Format_ARGB32 => Format_ARGB32")
+ << QImage::Format_ARGB32 << QImage::Format_ARGB32;
+ QTest::newRow("Format_ARGB32_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_ARGB32_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_RGBA8888 => Format_RGBA8888")
+ << QImage::Format_RGBA8888 << QImage::Format_RGBA8888;
+ QTest::newRow("Format_RGBA8888_Premultiplied => Format_RGBA8888_Premultiplied")
+ << QImage::Format_RGBA8888_Premultiplied << QImage::Format_RGBA8888_Premultiplied;
+ QTest::newRow("Format_RGBX8888 => Format_RGBX8888")
+ << QImage::Format_RGBX8888 << QImage::Format_RGBX8888;
+ QTest::newRow("Format_Grayscale8 => Format_Grayscale8")
+ << QImage::Format_Grayscale8 << QImage::Format_Grayscale8;
+ QTest::newRow("Format_Grayscale16 => Format_Grayscale16")
+ << QImage::Format_Grayscale16 << QImage::Format_Grayscale16;
+
+ QTest::newRow("Format_ARGB8565_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_ARGB8565_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_ARGB6666_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_ARGB6666_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_ARGB8555_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_ARGB8555_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_ARGB4444_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_ARGB4444_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_A2BGR30_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_A2BGR30_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_A2RGB30_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_A2RGB30_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_RGBA64_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_RGBA64_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_RGBA16FPx4_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_RGBA16FPx4_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_RGBA32FPx4_Premultiplied => Format_ARGB32_Premultiplied")
+ << QImage::Format_RGBA32FPx4_Premultiplied << QImage::Format_ARGB32_Premultiplied;
+
+ QTest::newRow("Format_Alpha8 => Format_ARGB32")
+ << QImage::Format_Alpha8 << QImage::Format_ARGB32;
+ QTest::newRow("Format_RGBA64 => Format_ARGB32")
+ << QImage::Format_RGBA64 << QImage::Format_ARGB32;
+ QTest::newRow("Format_RGBA16FPx4 => Format_ARGB32")
+ << QImage::Format_RGBA16FPx4 << QImage::Format_ARGB32;
+ QTest::newRow("Format_RGBA32FPx4 => Format_ARGB32")
+ << QImage::Format_RGBA32FPx4 << QImage::Format_ARGB32;
+}
+
+void tst_QVideoBuffers::imageBuffer_fixesInputImage()
+{
+ QFETCH(QImage::Format, inputImageFormat);
+ QFETCH(QImage::Format, underlyingImageFormat);
+
+ m_image.convertTo(inputImageFormat);
+ QImageVideoBuffer buffer(m_image);
+
+ auto underlyingImage = buffer.underlyingImage();
+
+ QCOMPARE(underlyingImage.format(), underlyingImageFormat);
+ QCOMPARE_NE(QVideoFrameFormat::pixelFormatFromImageFormat(underlyingImage.format()),
+ QVideoFrameFormat::Format_Invalid);
+ QCOMPARE(m_image.convertedTo(underlyingImageFormat), underlyingImage);
+
+ if (inputImageFormat == underlyingImageFormat)
+ QCOMPARE(m_image.constBits(), underlyingImage.constBits());
+}
+
+QTEST_APPLESS_MAIN(tst_QVideoBuffers);
+
+#include "tst_qvideobuffers.moc"
diff --git a/tests/auto/unit/multimedia/qvideoframe/CMakeLists.txt b/tests/auto/unit/multimedia/qvideoframe/CMakeLists.txt
index a46bbcc9b..29b8a413e 100644
--- a/tests/auto/unit/multimedia/qvideoframe/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qvideoframe/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qvideoframe.pro.
#####################################################################
@@ -7,7 +10,7 @@
qt_internal_add_test(tst_qvideoframe
SOURCES
tst_qvideoframe.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp b/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp
index cb94c102e..bc73eeb44 100644
--- a/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp
+++ b/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp
@@ -1,37 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <qvideoframe.h>
#include <qvideoframeformat.h>
+#include "QtTest/qtestcase.h"
#include "private/qmemoryvideobuffer_p.h"
#include <QtGui/QImage>
#include <QtCore/QPointer>
@@ -44,6 +18,113 @@
<< QString(QLatin1String(#x));
+// Image used for testing conversion from QImage to QVideoFrame
+QImage createTestImage(QImage::Format format)
+{
+ // +---+---+---+
+ // | r | g | b |
+ // | b | r | g |
+ // +---+---+---+
+ QImage image{ { 3, 2 }, QImage::Format_ARGB32 };
+ image.setPixelColor(0, 0, QColor(Qt::red));
+ image.setPixelColor(1, 0, QColor(Qt::green));
+ image.setPixelColor(2, 0, QColor(Qt::blue));
+ image.setPixelColor(0, 1, QColor(Qt::blue));
+ image.setPixelColor(1, 1, QColor(Qt::red));
+ image.setPixelColor(2, 1, QColor(Qt::green));
+ return image.convertToFormat(format);
+}
+
+// clang-format off
+
+// Convert a QVideoFrame pixel value from raw format to QRgb
+// Only works with little-endian byte ordering
+QRgb swizzle(uint value, QVideoFrameFormat::PixelFormat format)
+{
+ switch (format) {
+ case QVideoFrameFormat::Format_ARGB8888:
+ case QVideoFrameFormat::Format_ARGB8888_Premultiplied:
+ case QVideoFrameFormat::Format_XRGB8888:
+ Q_ASSERT(false); // not implemented
+ return 0;
+ case QVideoFrameFormat::Format_BGRA8888:
+ case QVideoFrameFormat::Format_BGRA8888_Premultiplied:
+ case QVideoFrameFormat::Format_BGRX8888:
+ return value;
+ case QVideoFrameFormat::Format_ABGR8888:
+ case QVideoFrameFormat::Format_XBGR8888:
+ Q_ASSERT(false); // not implemented
+ return 0;
+ case QVideoFrameFormat::Format_RGBA8888:
+ case QVideoFrameFormat::Format_RGBX8888:
+ return (((value >> 24) & 0xff) << 24) // a -> a
+ | ((value & 0xff) << 16) // b -> r
+ | (((value >> 8) & 0xff) << 8) // g -> g
+ | ((value >> 16) & 0xff); // r -> b
+ default:
+ qWarning() << "Unsupported format";
+ return 0;
+ }
+}
+
+std::vector<QRgb> swizzle(const std::vector<uint> &pixels, QVideoFrameFormat::PixelFormat format)
+{
+ std::vector<QRgb> rgba(pixels.size());
+ std::transform(pixels.begin(), pixels.end(), rgba.begin(),
+ [format](uint value) {
+ return swizzle(value, format);
+ });
+ return rgba;
+}
+
+// clang-format on
+
+std::optional<std::vector<QRgb>> getPixels(QVideoFrame &frame)
+{
+ if (!frame.map(QVideoFrame::ReadOnly))
+ return std::nullopt;
+
+ const uint *mappedPixels = reinterpret_cast<const uint *>(frame.bits(0));
+ const unsigned long long stride = frame.bytesPerLine(0) / sizeof(QRgb);
+
+ std::vector<uint> pixels;
+ for (int j = 0; j < frame.size().height(); ++j) {
+ for (int i = 0; i < frame.size().width(); ++i) {
+ pixels.push_back(mappedPixels[i + j * stride]);
+ }
+ }
+
+ frame.unmap();
+
+ return swizzle(pixels, frame.pixelFormat());
+}
+
+bool compareEq(QVideoFrame &frame, const QImage &image)
+{
+ if (frame.size() != image.size()) {
+ qDebug() << "Size mismatch";
+ return false;
+ }
+
+ const std::vector<QRgb> expectedPixels = { image.pixel(0, 0), image.pixel(1, 0), image.pixel(2, 0),
+ image.pixel(0, 1), image.pixel(1, 1), image.pixel(2, 1) };
+
+ const std::optional<std::vector<QRgb>> actualPixels = getPixels(frame);
+ if (!actualPixels) {
+ qDebug() << "Failed to read pixels from frame";
+ return false;
+ }
+
+ for (size_t i = 0; i < expectedPixels.size(); ++i) {
+ if (expectedPixels[i] != actualPixels->at(i)) {
+ qDebug() << "Pixel difference at element" << i << ":" << Qt::hex << expectedPixels[i]
+ << "vs" << actualPixels->at(i);
+ return false;
+ }
+ }
+ return true;
+}
+
class tst_QVideoFrame : public QObject
{
Q_OBJECT
@@ -86,6 +167,18 @@ private slots:
void image();
void emptyData();
+
+ void mirrored_takesValue_fromVideoFrameFormat();
+ void rotation_takesValue_fromVideoFrameFormat();
+ void streamFrameRate_takesValue_fromVideoFrameFormat();
+
+ void constructor_createsInvalidFrame_whenCalledWithNullImage();
+ void constructor_createsInvalidFrame_whenCalledWithEmptyImage();
+ void constructor_createsInvalidFrame_whenCalledWithInvalidImageFormat();
+ void constructor_createsFrameWithCorrectFormat_whenCalledWithSupportedImageFormats_data();
+ void constructor_createsFrameWithCorrectFormat_whenCalledWithSupportedImageFormats();
+ void constructor_copiesImageData_whenCalledWithRGBFormats_data();
+ void constructor_copiesImageData_whenCalledWithRGBFormats();
};
class QtTestDummyVideoBuffer : public QObject, public QAbstractVideoBuffer
@@ -169,33 +262,43 @@ void tst_QVideoFrame::create_data()
{
QTest::addColumn<QSize>("size");
QTest::addColumn<QVideoFrameFormat::PixelFormat>("pixelFormat");
- QTest::addColumn<int>("bytes");
QTest::addColumn<int>("bytesPerLine");
QTest::newRow("64x64 ARGB32")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_ARGB8888;
+ << QVideoFrameFormat::Format_ARGB8888
+ << 64*4;
QTest::newRow("32x256 YUV420P")
<< QSize(32, 256)
- << QVideoFrameFormat::Format_YUV420P;
+ << QVideoFrameFormat::Format_YUV420P
+ << 32;
+ QTest::newRow("32x256 UYVY")
+ << QSize(32, 256)
+ << QVideoFrameFormat::Format_UYVY
+ << 32*2;
}
void tst_QVideoFrame::create()
{
QFETCH(QSize, size);
QFETCH(QVideoFrameFormat::PixelFormat, pixelFormat);
+ QFETCH(int, bytesPerLine);
QVideoFrame frame(QVideoFrameFormat(size, pixelFormat));
QVERIFY(frame.isValid());
QCOMPARE(frame.handleType(), QVideoFrame::NoHandle);
- QCOMPARE(frame.textureHandle(0), 0u);
+ QVERIFY(frame.videoBuffer() != nullptr);
+ QCOMPARE(frame.videoBuffer()->textureHandle(nullptr, 0), 0u);
QCOMPARE(frame.pixelFormat(), pixelFormat);
QCOMPARE(frame.size(), size);
QCOMPARE(frame.width(), size.width());
QCOMPARE(frame.height(), size.height());
QCOMPARE(frame.startTime(), qint64(-1));
QCOMPARE(frame.endTime(), qint64(-1));
+ frame.map(QVideoFrame::ReadOnly);
+ QCOMPARE(frame.bytesPerLine(0), bytesPerLine);
+ frame.unmap();
}
void tst_QVideoFrame::createInvalid_data()
@@ -220,7 +323,7 @@ void tst_QVideoFrame::createInvalid()
QVERIFY(!frame.isValid());
QCOMPARE(frame.handleType(), QVideoFrame::NoHandle);
- QCOMPARE(frame.textureHandle(0), 0u);
+ QCOMPARE(frame.videoBuffer(), nullptr);
QCOMPARE(frame.pixelFormat(), pixelFormat);
QCOMPARE(frame.size(), size);
QCOMPARE(frame.width(), size.width());
@@ -606,7 +709,12 @@ void tst_QVideoFrame::map()
void tst_QVideoFrame::mapPlanes_data()
{
QTest::addColumn<QVideoFrame>("frame");
+
+ // Distance between subsequent lines within a color plane in bytes
QTest::addColumn<QList<int> >("strides");
+
+ // Distance from first pixel of first color plane to first pixel
+ // of n'th plane in bytes
QTest::addColumn<QList<int> >("offsets");
static uchar bufferData[1024];
@@ -629,6 +737,10 @@ void tst_QVideoFrame::mapPlanes_data()
<< QVideoFrame(QVideoFrameFormat(QSize(60, 64), QVideoFrameFormat::Format_YUV420P))
<< (QList<int>() << 64 << 32 << 32)
<< (QList<int>() << 4096 << 5120);
+ QTest::newRow("Format_YUV422P")
+ << QVideoFrame(QVideoFrameFormat(QSize(60, 64), QVideoFrameFormat::Format_YUV422P))
+ << (QList<int>() << 64 << 64 / 2 << 64 / 2)
+ << (QList<int>() << 64 * 64 << 64 * 64 + 64 / 2 * 64);
QTest::newRow("Format_YV12")
<< QVideoFrame(QVideoFrameFormat(QSize(60, 64), QVideoFrameFormat::Format_YV12))
<< (QList<int>() << 64 << 32 << 32)
@@ -669,24 +781,24 @@ void tst_QVideoFrame::mapPlanes()
QFETCH(QList<int>, strides);
QFETCH(QList<int>, offsets);
- QCOMPARE(strides.count(), offsets.count() + 1);
+ QCOMPARE(strides.size(), offsets.size() + 1);
QCOMPARE(frame.map(QVideoFrame::ReadOnly), true);
- QCOMPARE(frame.planeCount(), strides.count());
+ QCOMPARE(frame.planeCount(), strides.size());
- QVERIFY(strides.count() > 0);
+ QVERIFY(strides.size() > 0);
QCOMPARE(frame.bytesPerLine(0), strides.at(0));
QVERIFY(frame.bits(0));
- if (strides.count() > 1) {
+ if (strides.size() > 1) {
QCOMPARE(frame.bytesPerLine(1), strides.at(1));
QCOMPARE(int(frame.bits(1) - frame.bits(0)), offsets.at(0));
}
- if (strides.count() > 2) {
+ if (strides.size() > 2) {
QCOMPARE(frame.bytesPerLine(2), strides.at(2));
QCOMPARE(int(frame.bits(2) - frame.bits(0)), offsets.at(1));
}
- if (strides.count() > 3) {
+ if (strides.size() > 3) {
QCOMPARE(frame.bytesPerLine(3), strides.at(3));
QCOMPARE(int(frame.bits(3) - frame.bits(0)), offsets.at(0));
}
@@ -803,6 +915,11 @@ void tst_QVideoFrame::formatConversion_data()
QTest::newRow("QVideoFrameFormat::Format_Jpeg")
<< QImage::Format_Invalid
<< QVideoFrameFormat::Format_Jpeg;
+ QTest::newRow("QVideoFrameFormat::Format_RGBX8888")
+ << QImage::Format_RGBX8888 << QVideoFrameFormat::Format_RGBX8888;
+ QTest::newRow("QImage::Format_RGBA8888_Premultiplied => QVideoFrameFormat::Format_RGBX8888 "
+ "(workaround)")
+ << QImage::Format_RGBA8888_Premultiplied << QVideoFrameFormat::Format_RGBX8888;
}
void tst_QVideoFrame::formatConversion()
@@ -813,6 +930,12 @@ void tst_QVideoFrame::formatConversion()
if (imageFormat != QImage::Format_Invalid)
QCOMPARE(QVideoFrameFormat::pixelFormatFromImageFormat(imageFormat), pixelFormat);
+ if (imageFormat == QImage::Format_RGBA8888_Premultiplied) {
+ qWarning() << "Workaround: convert QImage::Format_RGBA8888_Premultiplied to "
+ "QVideoFrameFormat::Format_RGBX8888; to be removed in 6.8";
+ return;
+ }
+
if (pixelFormat != QVideoFrameFormat::Format_Invalid)
QCOMPARE(QVideoFrameFormat::imageFormatFromPixelFormat(pixelFormat), imageFormat);
}
@@ -915,85 +1038,69 @@ void tst_QVideoFrame::image_data()
{
QTest::addColumn<QSize>("size");
QTest::addColumn<QVideoFrameFormat::PixelFormat>("pixelFormat");
- QTest::addColumn<QImage::Format>("imageFormat");
QTest::newRow("64x64 ARGB32")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_ARGB8888
- << QImage::Format_ARGB32_Premultiplied;
+ << QVideoFrameFormat::Format_ARGB8888;
QTest::newRow("64x64 ARGB32_Premultiplied")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_ARGB8888_Premultiplied
- << QImage::Format_ARGB32_Premultiplied;
+ << QVideoFrameFormat::Format_ARGB8888_Premultiplied;
QTest::newRow("64x64 RGB32")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_XRGB8888
- << QImage::Format_RGB32;
+ << QVideoFrameFormat::Format_XRGB8888;
QTest::newRow("64x64 BGRA32")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_BGRA8888
- << QImage::Format_ARGB32_Premultiplied;
+ << QVideoFrameFormat::Format_BGRA8888;
QTest::newRow("64x64 BGRA32_Premultiplied")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_BGRA8888_Premultiplied
- << QImage::Format_ARGB32_Premultiplied;
+ << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
QTest::newRow("64x64 BGR32")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_XBGR8888
- << QImage::Format_RGB32;
+ << QVideoFrameFormat::Format_XBGR8888;
QTest::newRow("64x64 AYUV")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_AYUV
- << QImage::Format_ARGB32_Premultiplied;
+ << QVideoFrameFormat::Format_AYUV;
QTest::newRow("64x64 YUV420P")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_YUV420P
- << QImage::Format_RGB32;
+ << QVideoFrameFormat::Format_YUV420P;
QTest::newRow("64x64 YV12")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_YV12
- << QImage::Format_RGB32;
+ << QVideoFrameFormat::Format_YV12;
QTest::newRow("64x64 UYVY")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_UYVY
- << QImage::Format_RGB32;
+ << QVideoFrameFormat::Format_UYVY;
QTest::newRow("64x64 YUYV")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_YUYV
- << QImage::Format_RGB32;
+ << QVideoFrameFormat::Format_YUYV;
QTest::newRow("64x64 NV12")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_NV12
- << QImage::Format_RGB32;
+ << QVideoFrameFormat::Format_NV12;
QTest::newRow("64x64 NV21")
<< QSize(64, 64)
- << QVideoFrameFormat::Format_NV21
- << QImage::Format_RGB32;
+ << QVideoFrameFormat::Format_NV21;
}
void tst_QVideoFrame::image()
{
QFETCH(QSize, size);
QFETCH(QVideoFrameFormat::PixelFormat, pixelFormat);
- QFETCH(QImage::Format, imageFormat);
QVideoFrame frame(QVideoFrameFormat(size, pixelFormat));
QImage img = frame.toImage();
QVERIFY(!img.isNull());
- QCOMPARE(img.format(), imageFormat);
QCOMPARE(img.size(), size);
}
@@ -1005,6 +1112,211 @@ void tst_QVideoFrame::emptyData()
QVERIFY(!f.map(QVideoFrame::ReadOnly));
}
+void tst_QVideoFrame::mirrored_takesValue_fromVideoFrameFormat()
+{
+ QVideoFrameFormat format(QSize(10, 20), QVideoFrameFormat::Format_ARGB8888);
+ format.setMirrored(true);
+
+ QVideoFrame frame(format);
+ QVERIFY(frame.mirrored());
+
+ frame.setMirrored(false);
+ frame.setRotation(QtVideo::Rotation::Clockwise180);
+ QVERIFY(!frame.mirrored());
+ QVERIFY(!frame.surfaceFormat().isMirrored());
+}
+
+void tst_QVideoFrame::rotation_takesValue_fromVideoFrameFormat()
+{
+ QVideoFrameFormat format(QSize(10, 20), QVideoFrameFormat::Format_ARGB8888);
+ format.setRotation(QtVideo::Rotation::Clockwise270);
+
+ QVideoFrame frame(format);
+ QCOMPARE(frame.rotation(), QtVideo::Rotation::Clockwise270);
+
+ frame.setRotation(QtVideo::Rotation::Clockwise180);
+
+ QCOMPARE(frame.rotation(), QtVideo::Rotation::Clockwise180);
+ QCOMPARE(frame.surfaceFormat().rotation(), QtVideo::Rotation::Clockwise180);
+}
+
+void tst_QVideoFrame::streamFrameRate_takesValue_fromVideoFrameFormat()
+{
+ QVideoFrameFormat format(QSize(10, 20), QVideoFrameFormat::Format_ARGB8888);
+ format.setStreamFrameRate(20.);
+
+ QVideoFrame frame(format);
+ QCOMPARE(frame.streamFrameRate(), 20.);
+
+ frame.setStreamFrameRate(25.);
+
+ QCOMPARE(frame.streamFrameRate(), 25.);
+ QCOMPARE(frame.surfaceFormat().streamFrameRate(), 25.);
+}
+
+void tst_QVideoFrame::constructor_createsInvalidFrame_whenCalledWithNullImage()
+{
+ const QVideoFrame frame{ QImage{} };
+ QVERIFY(!frame.isValid());
+}
+
+void tst_QVideoFrame::constructor_createsInvalidFrame_whenCalledWithEmptyImage()
+{
+ {
+ const QImage image{ QSize{}, QImage::Format_RGB32 };
+ const QVideoFrame frame{ image };
+
+ QVERIFY(!frame.isValid());
+ }
+
+ {
+ const QImage image{ { 0, 0 }, QImage::Format_RGB32 };
+ const QVideoFrame frame{ image };
+
+ QVERIFY(!frame.isValid());
+ }
+
+ {
+ const QImage image{ { 1, 0 }, QImage::Format_RGB32 };
+ const QVideoFrame frame{ image };
+
+ QVERIFY(!frame.isValid());
+ }
+
+ {
+ const QImage image{ { 0, 1 }, QImage::Format_RGB32 };
+ const QVideoFrame frame{ image };
+
+ QVERIFY(!frame.isValid());
+ }
+}
+
+void tst_QVideoFrame::constructor_createsInvalidFrame_whenCalledWithInvalidImageFormat()
+{
+ const QImage image{ { 1, 1 }, QImage::Format_Invalid};
+ const QVideoFrame frame{ image };
+
+ QVERIFY(!frame.isValid());
+}
+
+// clang-format off
+void tst_QVideoFrame::constructor_createsFrameWithCorrectFormat_whenCalledWithSupportedImageFormats_data()
+{
+ QTest::addColumn<QImage::Format>("imageFormat");
+ QTest::addColumn<QVideoFrameFormat::PixelFormat>("expectedFrameFormat");
+
+ // Formats that do not require conversion
+ QTest::newRow("Format_RGB32") << QImage::Format_RGB32 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_ARGB32") << QImage::Format_ARGB32 << QVideoFrameFormat::Format_BGRA8888;
+ QTest::newRow("Format_ARGB32_Premultiplied") << QImage::Format_ARGB32_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_RGBA8888") << QImage::Format_RGBA8888 << QVideoFrameFormat::Format_RGBA8888;
+ QTest::newRow("Format_RGBA8888_Premultiplied") << QImage::Format_RGBA8888_Premultiplied << QVideoFrameFormat::Format_RGBX8888;
+ QTest::newRow("Format_RGBX8888") << QImage::Format_RGBX8888 << QVideoFrameFormat::Format_RGBX8888;
+ QTest::newRow("Format_Grayscale8") << QImage::Format_Grayscale8 << QVideoFrameFormat::Format_Y8;
+ QTest::newRow("Format_Grayscale16") << QImage::Format_Grayscale16 << QVideoFrameFormat::Format_Y16;
+
+ // Formats that require conversion of input image
+ QTest::newRow("Format_Mono") << QImage::Format_Mono << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_MonoLSB") << QImage::Format_MonoLSB << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_Indexed8") << QImage::Format_Indexed8 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_RGB16") << QImage::Format_RGB16 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_ARGB8565_Premultiplied") << QImage::Format_ARGB8565_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_RGB666") << QImage::Format_RGB666 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_ARGB6666_Premultiplied") << QImage::Format_ARGB6666_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_RGB555") << QImage::Format_RGB555 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_ARGB8555_Premultiplied") << QImage::Format_ARGB8555_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_RGB888") << QImage::Format_RGB888 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_RGB444") << QImage::Format_RGB444 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_ARGB4444_Premultiplied") << QImage::Format_ARGB4444_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_BGR30") << QImage::Format_BGR30 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_A2BGR30_Premultiplied") << QImage::Format_A2BGR30_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_RGB30") << QImage::Format_RGB30 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_A2RGB30_Premultiplied") << QImage::Format_A2RGB30_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_Alpha8") << QImage::Format_Alpha8 << QVideoFrameFormat::Format_BGRA8888;
+ QTest::newRow("Format_RGBX64") << QImage::Format_RGBX64 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_RGBA64") << QImage::Format_RGBA64 << QVideoFrameFormat::Format_BGRA8888;
+ QTest::newRow("Format_RGBA64_Premultiplied") << QImage::Format_RGBA64_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_BGR888") << QImage::Format_BGR888 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_RGBX16FPx4") << QImage::Format_RGBX16FPx4 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_RGBA16FPx4") << QImage::Format_RGBA16FPx4 << QVideoFrameFormat::Format_BGRA8888;
+ QTest::newRow("Format_RGBA16FPx4_Premultiplied") << QImage::Format_RGBA16FPx4_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+ QTest::newRow("Format_RGBX32FPx4") << QImage::Format_RGBX32FPx4 << QVideoFrameFormat::Format_BGRX8888;
+ QTest::newRow("Format_RGBA32FPx4") << QImage::Format_RGBA32FPx4 << QVideoFrameFormat::Format_BGRA8888;
+ QTest::newRow("Format_RGBA32FPx4_Premultiplied") << QImage::Format_RGBA32FPx4_Premultiplied << QVideoFrameFormat::Format_BGRA8888_Premultiplied;
+}
+// clang-format on
+
+void tst_QVideoFrame::constructor_createsFrameWithCorrectFormat_whenCalledWithSupportedImageFormats()
+{
+ QFETCH(const QImage::Format, imageFormat);
+ QFETCH(QVideoFrameFormat::PixelFormat, expectedFrameFormat);
+
+ const QImage image{ { 1, 1 }, imageFormat };
+ const QVideoFrame frame{ image };
+
+ QVERIFY(frame.isValid());
+ QCOMPARE_EQ(frame.pixelFormat(), expectedFrameFormat);
+}
+
+// clang-format off
+void tst_QVideoFrame::constructor_copiesImageData_whenCalledWithRGBFormats_data()
+{
+ QTest::addColumn<QImage::Format>("imageFormat");
+
+ // Formats that do not require image conversion
+ QTest::newRow("Format_RGB32") << QImage::Format_RGB32;
+ QTest::newRow("Format_RGBX8888") << QImage::Format_RGBX8888;
+ QTest::newRow("Format_ARGB32") << QImage::Format_ARGB32;
+ QTest::newRow("Format_ARGB32_Premultiplied") << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("Format_RGBA8888") << QImage::Format_RGBA8888;
+ QTest::newRow("Format_RGBA8888_Premultiplied") << QImage::Format_RGBA8888_Premultiplied;
+
+ // Formats that require image conversion
+ QTest::newRow("Format_Mono") << QImage::Format_Mono;
+ QTest::newRow("Format_MonoLSB") << QImage::Format_MonoLSB;
+ QTest::newRow("Format_Indexed8") << QImage::Format_Indexed8;
+ QTest::newRow("Format_RGB16") << QImage::Format_RGB16;
+ QTest::newRow("Format_ARGB8565_Premultiplied") << QImage::Format_ARGB8565_Premultiplied;
+ QTest::newRow("Format_RGB666") << QImage::Format_RGB666;
+ QTest::newRow("Format_ARGB6666_Premultiplied") << QImage::Format_ARGB6666_Premultiplied;
+ QTest::newRow("Format_RGB555") << QImage::Format_RGB555;
+ QTest::newRow("Format_ARGB8555_Premultiplied") << QImage::Format_ARGB8555_Premultiplied;
+ QTest::newRow("Format_RGB888") << QImage::Format_RGB888;
+ QTest::newRow("Format_RGB444") << QImage::Format_RGB444;
+ QTest::newRow("Format_ARGB4444_Premultiplied") << QImage::Format_ARGB4444_Premultiplied;
+ QTest::newRow("Format_BGR30") << QImage::Format_BGR30;
+ QTest::newRow("Format_A2BGR30_Premultiplied") << QImage::Format_A2BGR30_Premultiplied;
+ QTest::newRow("Format_RGB30") << QImage::Format_RGB30;
+ QTest::newRow("Format_A2RGB30_Premultiplied") << QImage::Format_A2RGB30_Premultiplied;
+ QTest::newRow("Format_Alpha8") << QImage::Format_Alpha8;
+ QTest::newRow("Format_RGBX64") << QImage::Format_RGBX64;
+ QTest::newRow("Format_RGBA64") << QImage::Format_RGBA64;
+ QTest::newRow("Format_RGBA64_Premultiplied") << QImage::Format_RGBA64_Premultiplied;
+ QTest::newRow("Format_BGR888") << QImage::Format_BGR888;
+ QTest::newRow("Format_RGBX16FPx4") << QImage::Format_RGBX16FPx4;
+ QTest::newRow("Format_RGBA16FPx4") << QImage::Format_RGBA16FPx4;
+ QTest::newRow("Format_RGBA16FPx4_Premultiplied") << QImage::Format_RGBA16FPx4_Premultiplied;
+ QTest::newRow("Format_RGBX32FPx4") << QImage::Format_RGBX32FPx4;
+ QTest::newRow("Format_RGBA32FPx4") << QImage::Format_RGBA32FPx4;
+ QTest::newRow("Format_RGBA32FPx4_Premultiplied") << QImage::Format_RGBA32FPx4_Premultiplied;
+}
+
+// clang-format on
+
+void tst_QVideoFrame::constructor_copiesImageData_whenCalledWithRGBFormats()
+{
+ QFETCH(const QImage::Format, imageFormat);
+
+ // Arrange
+ const QImage image{ createTestImage(imageFormat) };
+
+ // Act
+ QVideoFrame frame{ image };
+
+ // Assert
+ QVERIFY(compareEq(frame, image));
+}
+
QTEST_MAIN(tst_QVideoFrame)
#include "tst_qvideoframe.moc"
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/CMakeLists.txt b/tests/auto/unit/multimedia/qvideoframecolormanagement/CMakeLists.txt
new file mode 100644
index 000000000..6a6e6413b
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ testdata/*)
+list(APPEND testdata_resource_files ${test_data_glob})
+
+qt_internal_add_test(tst_qvideoframecolormanagement
+ SOURCES
+ tst_qvideoframecolormanagement.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::MultimediaPrivate
+ TESTDATA ${testdata_resource_files}
+)
+
+qt_internal_add_resource(tst_qvideoframecolormanagement "testdata"
+ PREFIX
+ "/"
+ FILES
+ ${testdata_resource_files}
+)
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg
new file mode 100644
index 000000000..52b0f620b
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_AdobeRgb_Full.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_AdobeRgb_Video.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT2020_Full.png
new file mode 100644
index 000000000..d6d461f5d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT2020_Video.png
new file mode 100644
index 000000000..2a4f7d8a7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT601_Full.png
new file mode 100644
index 000000000..d291f62bb
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT601_Video.png
new file mode 100644
index 000000000..35296fc03
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT709_Full.png
new file mode 100644
index 000000000..64e5eb6dc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT709_Video.png
new file mode 100644
index 000000000..9f6bdd1ea
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_420p_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_AdobeRgb_Full.png
new file mode 100644
index 000000000..3e255af2f
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_AdobeRgb_Video.png
new file mode 100644
index 000000000..3e255af2f
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT2020_Full.png
new file mode 100644
index 000000000..74fd12726
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT2020_Video.png
new file mode 100644
index 000000000..e358d16d8
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT601_Full.png
new file mode 100644
index 000000000..cb1cbbd34
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT601_Video.png
new file mode 100644
index 000000000..6dd95a078
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT709_Full.png
new file mode 100644
index 000000000..3e92f3695
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT709_Video.png
new file mode 100644
index 000000000..e94891e1c
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_422p_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_abgr8888_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_argb8888_premultiplied_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgra8888_premultiplied_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_bgrx8888_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_AdobeRgb_Full.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_AdobeRgb_Video.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT2020_Full.png
new file mode 100644
index 000000000..d6d461f5d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT2020_Video.png
new file mode 100644
index 000000000..2a4f7d8a7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT601_Full.png
new file mode 100644
index 000000000..d291f62bb
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT601_Video.png
new file mode 100644
index 000000000..35296fc03
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT709_Full.png
new file mode 100644
index 000000000..64e5eb6dc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT709_Video.png
new file mode 100644
index 000000000..9f6bdd1ea
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc1_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_AdobeRgb_Full.png
new file mode 100644
index 000000000..90b2b3601
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_AdobeRgb_Video.png
new file mode 100644
index 000000000..90b2b3601
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT2020_Full.png
new file mode 100644
index 000000000..2e78cfc31
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT2020_Video.png
new file mode 100644
index 000000000..d673b7ce5
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT601_Full.png
new file mode 100644
index 000000000..8be30a706
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT601_Video.png
new file mode 100644
index 000000000..1f64ea0f1
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT709_Full.png
new file mode 100644
index 000000000..24fb9065e
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT709_Video.png
new file mode 100644
index 000000000..f737d8602
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc2_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_AdobeRgb_Full.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_AdobeRgb_Video.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT2020_Full.png
new file mode 100644
index 000000000..d6d461f5d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT2020_Video.png
new file mode 100644
index 000000000..2a4f7d8a7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT601_Full.png
new file mode 100644
index 000000000..d291f62bb
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT601_Video.png
new file mode 100644
index 000000000..35296fc03
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT709_Full.png
new file mode 100644
index 000000000..64e5eb6dc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT709_Video.png
new file mode 100644
index 000000000..9f6bdd1ea
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc3_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_AdobeRgb_Full.png
new file mode 100644
index 000000000..6efa73ea2
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_AdobeRgb_Video.png
new file mode 100644
index 000000000..6efa73ea2
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT2020_Full.png
new file mode 100644
index 000000000..8d6a36a1c
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT2020_Video.png
new file mode 100644
index 000000000..dab23bf0d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT601_Full.png
new file mode 100644
index 000000000..36e787cef
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT601_Video.png
new file mode 100644
index 000000000..01e6ab967
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT709_Full.png
new file mode 100644
index 000000000..22beff2e8
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT709_Video.png
new file mode 100644
index 000000000..c2af074b8
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_imc4_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_AdobeRgb_Full.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_AdobeRgb_Video.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT2020_Full.png
new file mode 100644
index 000000000..d6d461f5d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT2020_Video.png
new file mode 100644
index 000000000..2a4f7d8a7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT601_Full.png
new file mode 100644
index 000000000..d291f62bb
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT601_Video.png
new file mode 100644
index 000000000..35296fc03
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT709_Full.png
new file mode 100644
index 000000000..64e5eb6dc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT709_Video.png
new file mode 100644
index 000000000..9f6bdd1ea
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv12_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_AdobeRgb_Full.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_AdobeRgb_Video.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT2020_Full.png
new file mode 100644
index 000000000..d6d461f5d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT2020_Video.png
new file mode 100644
index 000000000..2a4f7d8a7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT601_Full.png
new file mode 100644
index 000000000..d291f62bb
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT601_Video.png
new file mode 100644
index 000000000..35296fc03
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT709_Full.png
new file mode 100644
index 000000000..64e5eb6dc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT709_Video.png
new file mode 100644
index 000000000..9f6bdd1ea
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_nv21_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_AdobeRgb_Full.png
new file mode 100644
index 000000000..71e107b8a
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_AdobeRgb_Video.png
new file mode 100644
index 000000000..71e107b8a
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT2020_Full.png
new file mode 100644
index 000000000..58a7ebc92
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT2020_Video.png
new file mode 100644
index 000000000..d8756caac
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT601_Full.png
new file mode 100644
index 000000000..905568bf9
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT601_Video.png
new file mode 100644
index 000000000..f374df207
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT709_Full.png
new file mode 100644
index 000000000..d2ee0f8e2
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT709_Video.png
new file mode 100644
index 000000000..740de7f79
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p010_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_AdobeRgb_Full.png
new file mode 100644
index 000000000..ad76d393a
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_AdobeRgb_Video.png
new file mode 100644
index 000000000..ad76d393a
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT2020_Full.png
new file mode 100644
index 000000000..a6e47132c
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT2020_Video.png
new file mode 100644
index 000000000..d9760b9c9
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT601_Full.png
new file mode 100644
index 000000000..04ae5e1cd
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT601_Video.png
new file mode 100644
index 000000000..9faa15fad
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT709_Full.png
new file mode 100644
index 000000000..84b04ff9e
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT709_Video.png
new file mode 100644
index 000000000..505752c10
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_p016_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgba8888_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_rgbx8888_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Full.png
new file mode 100644
index 000000000..c5243c441
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Video.png
new file mode 100644
index 000000000..c5243c441
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Full.png
new file mode 100644
index 000000000..0a9874943
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Video.png
new file mode 100644
index 000000000..7318c1e99
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Full.png
new file mode 100644
index 000000000..68789bef5
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Video.png
new file mode 100644
index 000000000..bfd6396ec
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Full.png
new file mode 100644
index 000000000..704c59cf9
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Video.png
new file mode 100644
index 000000000..d9ad9c239
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xbgr8888_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_AdobeRgb_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_AdobeRgb_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT2020_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT2020_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT601_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT601_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT709_Full.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT709_Video.png
new file mode 100644
index 000000000..682e999cc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_xrgb8888_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_AdobeRgb_Full.png
new file mode 100644
index 000000000..b1dc781f2
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_AdobeRgb_Video.png
new file mode 100644
index 000000000..b1dc781f2
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT2020_Full.png
new file mode 100644
index 000000000..619ee36a4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT2020_Video.png
new file mode 100644
index 000000000..881f6be33
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT601_Full.png
new file mode 100644
index 000000000..b1d3111df
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT601_Video.png
new file mode 100644
index 000000000..e4d1ce940
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT709_Full.png
new file mode 100644
index 000000000..b1d3111df
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT709_Video.png
new file mode 100644
index 000000000..df8df3edd
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y16_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_AdobeRgb_Full.png
new file mode 100644
index 000000000..130a3b541
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_AdobeRgb_Video.png
new file mode 100644
index 000000000..130a3b541
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT2020_Full.png
new file mode 100644
index 000000000..21ed2218a
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT2020_Video.png
new file mode 100644
index 000000000..f60f53d02
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT601_Full.png
new file mode 100644
index 000000000..df59b71e7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT601_Video.png
new file mode 100644
index 000000000..dbca71c70
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT709_Full.png
new file mode 100644
index 000000000..df59b71e7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT709_Video.png
new file mode 100644
index 000000000..3479bb890
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_y8_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_AdobeRgb_Full.png
new file mode 100644
index 000000000..20b24da65
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_AdobeRgb_Video.png
new file mode 100644
index 000000000..20b24da65
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT2020_Full.png
new file mode 100644
index 000000000..b96379a0b
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT2020_Video.png
new file mode 100644
index 000000000..c77645b59
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT601_Full.png
new file mode 100644
index 000000000..a1b8b62da
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT601_Video.png
new file mode 100644
index 000000000..7a69f6afa
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT709_Full.png
new file mode 100644
index 000000000..644b083fe
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT709_Video.png
new file mode 100644
index 000000000..d4e9debd7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuv420p10_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Full.png
new file mode 100644
index 000000000..c5243c441
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Video.png
new file mode 100644
index 000000000..c5243c441
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Full.png
new file mode 100644
index 000000000..0a9874943
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Video.png
new file mode 100644
index 000000000..7318c1e99
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Full.png
new file mode 100644
index 000000000..68789bef5
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Video.png
new file mode 100644
index 000000000..bfd6396ec
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Full.png
new file mode 100644
index 000000000..704c59cf9
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Video.png
new file mode 100644
index 000000000..d9ad9c239
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_AdobeRgb_Full.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_AdobeRgb_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_AdobeRgb_Video.png
new file mode 100644
index 000000000..2af7cdaa4
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_AdobeRgb_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT2020_Full.png
new file mode 100644
index 000000000..d6d461f5d
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT2020_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT2020_Video.png
new file mode 100644
index 000000000..2a4f7d8a7
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT2020_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT601_Full.png
new file mode 100644
index 000000000..d291f62bb
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT601_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT601_Video.png
new file mode 100644
index 000000000..35296fc03
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT601_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT709_Full.png
new file mode 100644
index 000000000..64e5eb6dc
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT709_Full.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT709_Video.png
new file mode 100644
index 000000000..9f6bdd1ea
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yv12_BT709_Video.png
Binary files differ
diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/tst_qvideoframecolormanagement.cpp b/tests/auto/unit/multimedia/qvideoframecolormanagement/tst_qvideoframecolormanagement.cpp
new file mode 100644
index 000000000..22b7ddd36
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/tst_qvideoframecolormanagement.cpp
@@ -0,0 +1,448 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+
+#include <qvideoframe.h>
+#include <qvideoframeformat.h>
+#include "private/qmemoryvideobuffer_p.h"
+#include "private/qplatformmediaintegration_p.h"
+#include "private/qimagevideobuffer_p.h"
+#include <QtGui/QColorSpace>
+#include <QtGui/QImage>
+#include <QtCore/QPointer>
+
+QT_USE_NAMESPACE
+
+namespace {
+
+struct TestParams
+{
+ QString fileName;
+ QVideoFrameFormat::PixelFormat pixelFormat;
+ QVideoFrameFormat::ColorSpace colorSpace;
+ QVideoFrameFormat::ColorRange colorRange;
+};
+
+QString toString(QVideoFrameFormat::ColorRange r)
+{
+ switch (r) {
+ case QVideoFrameFormat::ColorRange_Video:
+ return "Video";
+ case QVideoFrameFormat::ColorRange_Full:
+ return "Full";
+ default:
+ Q_ASSERT(false);
+ return "";
+ }
+}
+
+std::vector<QVideoFrameFormat::ColorRange> colorRanges()
+{
+ return {
+ QVideoFrameFormat::ColorRange_Video,
+ QVideoFrameFormat::ColorRange_Full,
+ };
+}
+
+// clang-format off
+
+static const QHash<QVideoFrameFormat::PixelFormat, const char*> s_formats {
+ { QVideoFrameFormat::Format_ARGB8888, "argb8888" },
+ { QVideoFrameFormat::Format_ARGB8888_Premultiplied, "argb8888_premultiplied" },
+ { QVideoFrameFormat::Format_XRGB8888, "xrgb8888" },
+ { QVideoFrameFormat::Format_BGRA8888, "bgra8888" },
+ { QVideoFrameFormat::Format_BGRA8888_Premultiplied, "bgra8888_premultiplied" },
+ { QVideoFrameFormat::Format_BGRX8888, "bgrx8888" },
+ { QVideoFrameFormat::Format_ABGR8888, "abgr8888" },
+ { QVideoFrameFormat::Format_XBGR8888, "xbgr8888" },
+ { QVideoFrameFormat::Format_RGBA8888, "rgba8888" },
+ { QVideoFrameFormat::Format_RGBX8888, "rgbx8888" },
+ { QVideoFrameFormat::Format_NV12, "nv12" },
+ { QVideoFrameFormat::Format_NV21, "nv21" },
+ { QVideoFrameFormat::Format_IMC1, "imc1" },
+ { QVideoFrameFormat::Format_IMC2, "imc2" },
+ { QVideoFrameFormat::Format_IMC3, "imc3" },
+ { QVideoFrameFormat::Format_IMC4, "imc4" },
+ //{ QVideoFrameFormat::Format_AYUV, "ayuv" }, // TODO: Fixme (No corresponding FFmpeg format available)
+ //{ QVideoFrameFormat::Format_AYUV_Premultiplied, "ayuv_premultiplied" }, // TODO: Fixme (No corresponding FFmpeg format available)
+ { QVideoFrameFormat::Format_YV12, "yv12" },
+ { QVideoFrameFormat::Format_YUV420P, "420p" },
+ { QVideoFrameFormat::Format_YUV422P, "422p" },
+ { QVideoFrameFormat::Format_UYVY, "uyvy" },
+ { QVideoFrameFormat::Format_YUYV, "yuyv" },
+ { QVideoFrameFormat::Format_Y8, "y8" },
+ { QVideoFrameFormat::Format_Y16, "y16" },
+ { QVideoFrameFormat::Format_P010, "p010" },
+ { QVideoFrameFormat::Format_P016, "p016" },
+ { QVideoFrameFormat::Format_YUV420P10, "yuv420p10" }
+};
+
+// clang-format on
+
+QString toString(QVideoFrameFormat::PixelFormat f)
+{
+ if (!s_formats.contains(f)) {
+ Q_ASSERT(false);
+ return {};
+ }
+
+ return s_formats.value(f);
+}
+
+QList<QVideoFrameFormat::PixelFormat> pixelFormats()
+{
+ return s_formats.keys();
+}
+
+bool isSupportedPixelFormat(QVideoFrameFormat::PixelFormat pixelFormat)
+{
+#ifdef Q_OS_ANDROID
+ // TODO: QTBUG-125238
+ switch (pixelFormat) {
+ case QVideoFrameFormat::Format_Y16:
+ case QVideoFrameFormat::Format_P010:
+ case QVideoFrameFormat::Format_P016:
+ case QVideoFrameFormat::Format_YUV420P10:
+ return false;
+ default:
+ return true;
+ }
+#else
+ return true;
+#endif
+}
+
+
+QString toString(QVideoFrameFormat::ColorSpace s)
+{
+ switch (s) {
+ case QVideoFrameFormat::ColorSpace_BT601:
+ return "BT601";
+ case QVideoFrameFormat::ColorSpace_BT709:
+ return "BT709";
+ case QVideoFrameFormat::ColorSpace_AdobeRgb:
+ return "AdobeRgb";
+ case QVideoFrameFormat::ColorSpace_BT2020:
+ return "BT2020";
+ default:
+ Q_ASSERT(false);
+ return "";
+ }
+}
+
+std::vector<QVideoFrameFormat::ColorSpace> colorSpaces()
+{
+ return { QVideoFrameFormat::ColorSpace_BT601, QVideoFrameFormat::ColorSpace_BT709,
+ QVideoFrameFormat::ColorSpace_AdobeRgb, QVideoFrameFormat::ColorSpace_BT2020 };
+}
+
+QString name(const TestParams &p)
+{
+ return QStringLiteral("%1_%2_%3_%4")
+ .arg(p.fileName)
+ .arg(toString(p.pixelFormat))
+ .arg(toString(p.colorSpace))
+ .arg(toString(p.colorRange));
+}
+
+QString path(const QTemporaryDir &dir, const TestParams &param, const QString &suffix = ".png")
+{
+ return dir.filePath(name(param) + suffix);
+}
+
+QVideoFrame createTestFrame(const TestParams &params, const QImage &image)
+{
+ QVideoFrameFormat format(image.size(), params.pixelFormat);
+ format.setColorRange(params.colorRange);
+ format.setColorSpace(params.colorSpace);
+ format.setColorTransfer(QVideoFrameFormat::ColorTransfer_Unknown);
+
+ auto buffer = std::make_unique<QImageVideoBuffer>(image);
+ QVideoFrameFormat imageFormat = {
+ image.size(), QVideoFrameFormat::pixelFormatFromImageFormat(image.format())
+ };
+
+ QVideoFrame source{ buffer.release(), imageFormat };
+ return QPlatformMediaIntegration::instance()->convertVideoFrame(source, format);
+}
+
+struct ImageDiffReport
+{
+ int DiffCountAboveThreshold; // Number of channel differences above threshold
+ int MaxDiff; // Maximum difference between two images (max across channels)
+ int PixelCount; // Number of pixels in the image
+ QImage DiffImage; // The difference image (absolute per-channel difference)
+};
+
+double aboveThresholdDiffRatio(const ImageDiffReport &report)
+{
+ return static_cast<double>(report.DiffCountAboveThreshold) / report.PixelCount;
+}
+
+int maxChannelDiff(QRgb lhs, QRgb rhs)
+{
+ // clang-format off
+ return std::max({ std::abs(qRed(lhs) - qRed(rhs)),
+ std::abs(qGreen(lhs) - qGreen(rhs)),
+ std::abs(qBlue(lhs) - qBlue(rhs)) });
+ // clang-format on
+}
+
+int clampedAbsDiff(int lhs, int rhs)
+{
+ return std::clamp(std::abs(lhs - rhs), 0, 255);
+}
+
+QRgb pixelDiff(QRgb lhs, QRgb rhs)
+{
+ return qRgb(clampedAbsDiff(qRed(lhs), qRed(rhs)), clampedAbsDiff(qGreen(lhs), qGreen(rhs)),
+ clampedAbsDiff(qBlue(lhs), qBlue(rhs)));
+}
+
+std::optional<ImageDiffReport> compareImagesRgb32(const QImage &computed, const QImage &baseline,
+ int channelThreshold)
+{
+ Q_ASSERT(baseline.format() == QImage::Format_RGB32);
+
+ if (computed.size() != baseline.size())
+ return {};
+
+ if (computed.format() != baseline.format())
+ return {};
+
+ if (computed.colorSpace() != baseline.colorSpace())
+ return {};
+
+ const QSize size = baseline.size();
+
+ ImageDiffReport report{};
+ report.PixelCount = size.width() * size.height();
+ report.DiffImage = QImage(size, baseline.format());
+
+ // Iterate over all pixels and update report
+ for (int l = 0; l < size.height(); l++) {
+ const QRgb *colorComputed = reinterpret_cast<const QRgb *>(computed.constScanLine(l));
+ const QRgb *colorBaseline = reinterpret_cast<const QRgb *>(baseline.constScanLine(l));
+ QRgb *colorDiff = reinterpret_cast<QRgb *>(report.DiffImage.scanLine(l));
+
+ int w = size.width();
+ while (w--) {
+ *colorDiff = pixelDiff(*colorComputed, *colorBaseline);
+ if (*colorComputed != *colorBaseline) {
+ const int diff = maxChannelDiff(*colorComputed, *colorBaseline);
+
+ if (diff > report.MaxDiff)
+ report.MaxDiff = diff;
+
+ if (diff > channelThreshold)
+ ++report.DiffCountAboveThreshold;
+ }
+
+ ++colorComputed;
+ ++colorBaseline;
+ ++colorDiff;
+ }
+ }
+ return report;
+}
+
+bool copyAllFiles(const QDir &source, const QDir &dest)
+{
+ if (!source.exists() || !dest.exists())
+ return false;
+
+ QDirIterator it(source);
+ while (it.hasNext()) {
+ QFileInfo file{ it.next() };
+ if (file.isFile()) {
+ const QString destination = dest.absolutePath() + "/" + file.fileName();
+ QFile::copy(file.absoluteFilePath(), destination);
+ }
+ }
+
+ return true;
+}
+
+class ReferenceData
+{
+public:
+ ReferenceData()
+ {
+ m_testdataDir = QTest::qExtractTestData("testdata");
+ if (!m_testdataDir)
+ m_testdataDir = QSharedPointer<QTemporaryDir>(new QTemporaryDir);
+ }
+
+ ~ReferenceData()
+ {
+ if (m_testdataDir->autoRemove())
+ return;
+
+ QString resultPath = m_testdataDir->path();
+ if (qEnvironmentVariableIsSet("COIN_CTEST_RESULTSDIR")) {
+ const QDir sourceDir = m_testdataDir->path();
+ const QDir resultsDir{ qEnvironmentVariable("COIN_CTEST_RESULTSDIR") };
+ if (!copyAllFiles(sourceDir, resultsDir)) {
+ qDebug() << "Failed to copy files to COIN_CTEST_RESULTSDIR";
+ } else {
+ resultPath = resultsDir.path();
+ }
+ }
+
+ qDebug() << "Images with differences were found. The output images with differences"
+ << "can be found in" << resultPath << ". Review the images and if the"
+ << "differences are expected, please update the testdata with the new"
+ << "output images";
+ }
+
+ QImage getReference(const TestParams &param) const
+ {
+ const QString referenceName = name(param);
+ const QString referencePath = m_testdataDir->filePath(referenceName + ".png");
+ QImage result;
+ if (result.load(referencePath))
+ return result;
+ return {};
+ }
+
+ void saveNewReference(const QImage &reference, const TestParams &params) const
+ {
+ const QString filename = path(*m_testdataDir, params);
+ if (!reference.save(filename)) {
+ qDebug() << "Failed to save reference file";
+ Q_ASSERT(false);
+ }
+
+ m_testdataDir->setAutoRemove(false);
+ }
+
+ bool saveComputedImage(const TestParams &params, const QImage &image, const QString& suffix) const
+ {
+ if (!image.save(path(*m_testdataDir, params, suffix))) {
+ qDebug() << "Unexpectedly failed to save actual image to file";
+ Q_ASSERT(false);
+ return false;
+ }
+ m_testdataDir->setAutoRemove(false);
+ return true;
+ }
+
+ QImage getTestdata(const QString &name)
+ {
+ const QString filePath = m_testdataDir->filePath(name);
+ QImage image;
+ if (image.load(filePath))
+ return image;
+ return {};
+ }
+
+private:
+ QSharedPointer<QTemporaryDir> m_testdataDir;
+};
+
+std::optional<ImageDiffReport> compareToReference(const TestParams &params, const QImage &actual,
+ const ReferenceData &references,
+ int maxChannelThreshold)
+{
+ const QImage expected = references.getReference(params);
+ if (expected.isNull()) {
+ // Reference image does not exist. Create one. Adding this to
+ // testdata directory is a manual job.
+ references.saveNewReference(actual, params);
+ qDebug() << "Reference image is missing. Please update testdata directory with the missing "
+ "reference image";
+ return {};
+ }
+
+ // Convert to RGB32 to simplify image comparison
+ const QImage computed = actual.convertToFormat(QImage::Format_RGB32);
+ const QImage baseline = expected.convertToFormat(QImage::Format_RGB32);
+
+ std::optional<ImageDiffReport> diffReport = compareImagesRgb32(computed, baseline, maxChannelThreshold);
+ if (!diffReport)
+ return {};
+
+ if (diffReport->MaxDiff > 0) {
+ // Images are not equal, and may require manual inspection
+ if (!references.saveComputedImage(params, computed, "_actual.png"))
+ return {};
+ if (!references.saveComputedImage(params, diffReport->DiffImage, "_diff.png"))
+ return {};
+ }
+
+ return diffReport;
+}
+
+} // namespace
+
+class tst_qvideoframecolormanagement : public QObject
+{
+ Q_OBJECT
+private slots:
+
+ void toImage_savesWithCorrectColors_data()
+ {
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<TestParams>("params");
+ for (const char *file : { "umbrellas.jpg" }) {
+ for (const QVideoFrameFormat::PixelFormat pixelFormat : pixelFormats()) {
+ for (const QVideoFrameFormat::ColorSpace colorSpace : colorSpaces()) {
+ for (const QVideoFrameFormat::ColorRange colorRange : colorRanges()) {
+
+ if (!isSupportedPixelFormat(pixelFormat))
+ continue;
+
+ TestParams param{ file, pixelFormat, colorSpace, colorRange };
+ QTest::addRow("%s", name(param).toLatin1().data()) << file << param;
+ }
+ }
+ }
+ }
+ }
+
+ // This test is a regression test for the QMultimedia display pipeline.
+ // It compares rendered output (as created by toImage) against reference
+ // images stored to file. The reference images were created by the test
+ // itself, and does not verify correctness, just changes to render output.
+ void toImage_savesWithCorrectColors()
+ {
+ QFETCH(const QString, fileName);
+ QFETCH(const TestParams, params);
+
+ const QImage templateImage = m_reference.getTestdata(fileName);
+ QVERIFY(!templateImage.isNull());
+
+ const QVideoFrame frame = createTestFrame(params, templateImage);
+
+ // Act
+ const QImage actual = frame.toImage();
+
+ // Assert
+ constexpr int diffThreshold = 4;
+ std::optional<ImageDiffReport> result =
+ compareToReference(params, actual, m_reference, diffThreshold);
+
+ // Sanity checks
+ QVERIFY(result.has_value());
+ QCOMPARE_GT(result->PixelCount, 0);
+
+ // Verify that images are similar
+ const double ratioAboveThreshold =
+ static_cast<double>(result->DiffCountAboveThreshold) / result->PixelCount;
+
+ // These thresholds are empirically determined to allow tests to pass in CI.
+ // If tests fail, review the difference between the reference and actual
+ // output to determine if it is a platform dependent inaccuracy before
+ // adjusting the limits
+ QCOMPARE_LT(ratioAboveThreshold, 0.01); // Fraction of pixels with larger differences
+ QCOMPARE_LT(result->MaxDiff, 6); // Maximum per-channel difference
+ }
+
+private:
+ ReferenceData m_reference;
+};
+
+QTEST_MAIN(tst_qvideoframecolormanagement)
+
+#include "tst_qvideoframecolormanagement.moc"
diff --git a/tests/auto/unit/multimedia/qvideoframeformat/CMakeLists.txt b/tests/auto/unit/multimedia/qvideoframeformat/CMakeLists.txt
index af14e67b0..0d1b92d55 100644
--- a/tests/auto/unit/multimedia/qvideoframeformat/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qvideoframeformat/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qvideoframeformat.pro.
#####################################################################
@@ -7,7 +10,7 @@
qt_internal_add_test(tst_qvideoframeformat
SOURCES
tst_qvideoframeformat.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
)
diff --git a/tests/auto/unit/multimedia/qvideoframeformat/tst_qvideoframeformat.cpp b/tests/auto/unit/multimedia/qvideoframeformat/tst_qvideoframeformat.cpp
index e2ab89a41..41d54de0d 100644
--- a/tests/auto/unit/multimedia/qvideoframeformat/tst_qvideoframeformat.cpp
+++ b/tests/auto/unit/multimedia/qvideoframeformat/tst_qvideoframeformat.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
@@ -57,6 +30,8 @@ private slots:
void construct();
void frameSize_data();
void frameSize();
+ void planeCount_returnsNumberOfColorPlanesDictatedByPixelFormat_data() const;
+ void planeCount_returnsNumberOfColorPlanesDictatedByPixelFormat() const;
void viewport_data();
void viewport();
void scanLineDirection_data();
@@ -109,7 +84,7 @@ void tst_QVideoFrameFormat::constructNull()
QCOMPARE(format.frameHeight(), -1);
QCOMPARE(format.viewport(), QRect());
QCOMPARE(format.scanLineDirection(), QVideoFrameFormat::TopToBottom);
- QCOMPARE(format.frameRate(), 0.0);
+ QCOMPARE(format.streamFrameRate(), 0.0);
QCOMPARE(format.colorSpace(), QVideoFrameFormat::ColorSpace_Undefined);
}
@@ -157,7 +132,7 @@ void tst_QVideoFrameFormat::construct()
QCOMPARE(format.isValid(), valid);
QCOMPARE(format.viewport(), viewport);
QCOMPARE(format.scanLineDirection(), QVideoFrameFormat::TopToBottom);
- QCOMPARE(format.frameRate(), 0.0);
+ QCOMPARE(format.streamFrameRate(), 0.0);
QCOMPARE(format.colorSpace(), QVideoFrameFormat::ColorSpace_Undefined);
}
@@ -191,6 +166,59 @@ void tst_QVideoFrameFormat::frameSize()
QCOMPARE(format.frameHeight(), newSize.height());
}
+void tst_QVideoFrameFormat::planeCount_returnsNumberOfColorPlanesDictatedByPixelFormat_data() const {
+ QTest::addColumn<QVideoFrameFormat::PixelFormat>("pixelFormat");
+ QTest::addColumn<int>("colorPlanes"); // Number of planes as specified by QVideoFrameFormat::PixelFormat documentation
+
+ QTest::newRow("ARGB8888") << QVideoFrameFormat::Format_ARGB8888 << 1;
+ QTest::newRow("ARGB8888_Premultiplied") << QVideoFrameFormat::Format_ARGB8888_Premultiplied << 1;
+ QTest::newRow("XRGB8888") << QVideoFrameFormat::Format_XRGB8888 << 1;
+ QTest::newRow("BGRA8888") << QVideoFrameFormat::Format_BGRA8888 << 1;
+ QTest::newRow("BGRA8888_Premultiplied") << QVideoFrameFormat::Format_BGRA8888_Premultiplied << 1;
+ QTest::newRow("BGRX8888") << QVideoFrameFormat::Format_BGRX8888 << 1;
+ QTest::newRow("ABGR8888") << QVideoFrameFormat::Format_ABGR8888 << 1;
+ QTest::newRow("XBGR8888") << QVideoFrameFormat::Format_XBGR8888 << 1;
+ QTest::newRow("RGBA8888") << QVideoFrameFormat::Format_RGBA8888 << 1;
+ QTest::newRow("RGBX8888") << QVideoFrameFormat::Format_RGBX8888 << 1;
+
+ QTest::newRow("AUYVY") << QVideoFrameFormat::Format_AYUV << 1;
+ QTest::newRow("AYUV_Premultiplied") << QVideoFrameFormat::Format_AYUV_Premultiplied << 1;
+ QTest::newRow("YUV420P") << QVideoFrameFormat::Format_YUV420P << 3;
+ QTest::newRow("YUV422P") << QVideoFrameFormat::Format_YUV422P << 3;
+ QTest::newRow("YV12") << QVideoFrameFormat::Format_YV12 << 3;
+
+ QTest::newRow("UYVY") << QVideoFrameFormat::Format_UYVY << 1;
+ QTest::newRow("YUYV") << QVideoFrameFormat::Format_YUYV << 1;
+ QTest::newRow("NV12") << QVideoFrameFormat::Format_NV12 << 2;
+ QTest::newRow("NV21") << QVideoFrameFormat::Format_NV21 << 2;
+
+ QTest::newRow("IMC1") << QVideoFrameFormat::Format_IMC1 << 3;
+ QTest::newRow("IMC2") << QVideoFrameFormat::Format_IMC2 << 2;
+ QTest::newRow("IMC3") << QVideoFrameFormat::Format_IMC3 << 3;
+ QTest::newRow("IMC4") << QVideoFrameFormat::Format_IMC4 << 2;
+
+ QTest::newRow("Y8") << QVideoFrameFormat::Format_Y8 << 1;
+ QTest::newRow("Y16") << QVideoFrameFormat::Format_Y16 << 1;
+
+ QTest::newRow("P010") << QVideoFrameFormat::Format_P010 << 2;
+ QTest::newRow("P016") << QVideoFrameFormat::Format_P016 << 2;
+
+ QTest::newRow("SamplerExternalOES") << QVideoFrameFormat::Format_SamplerExternalOES << 1;
+ QTest::newRow("Jpeg") << QVideoFrameFormat::Format_Jpeg << 1;
+ QTest::newRow("SamplerRect") << QVideoFrameFormat::Format_SamplerRect << 1;
+
+ QTest::newRow("YUV420P10") << QVideoFrameFormat::Format_YUV420P10 << 3;
+}
+
+void tst_QVideoFrameFormat::planeCount_returnsNumberOfColorPlanesDictatedByPixelFormat() const {
+ QFETCH(QVideoFrameFormat::PixelFormat, pixelFormat);
+ QFETCH(int, colorPlanes);
+
+ const QVideoFrameFormat frameFormat = QVideoFrameFormat({}, pixelFormat);
+
+ QCOMPARE_EQ(frameFormat.planeCount(), colorPlanes);
+}
+
void tst_QVideoFrameFormat::viewport_data()
{
QTest::addColumn<QSize>("initialSize");
@@ -307,9 +335,9 @@ void tst_QVideoFrameFormat::frameRate()
QVideoFrameFormat format(QSize(64, 64), QVideoFrameFormat::Format_XRGB8888);
- format.setFrameRate(frameRate);
+ format.setStreamFrameRate(frameRate);
- QCOMPARE(format.frameRate(), frameRate);
+ QCOMPARE(format.streamFrameRate(), frameRate);
}
void tst_QVideoFrameFormat::compare()
@@ -367,13 +395,13 @@ void tst_QVideoFrameFormat::compare()
QCOMPARE(format1 == format2, true);
QCOMPARE(format1 != format2, false);
- format1.setFrameRate(7.5);
+ format1.setStreamFrameRate(7.5);
// Not equal frame rate differs.
QCOMPARE(format1 == format2, false);
QCOMPARE(format1 != format2, true);
- format2.setFrameRate(qreal(7.50001));
+ format2.setStreamFrameRate(qreal(7.50001));
// Equal.
QCOMPARE(format1 == format2, true);
@@ -478,7 +506,7 @@ void tst_QVideoFrameFormat::copyAllParameters()
original.setScanLineDirection(QVideoFrameFormat::BottomToTop);
original.setViewport(QRect(0, 0, 1024, 1024));
- original.setFrameRate(qreal(15.0));
+ original.setStreamFrameRate(qreal(15.0));
original.setColorSpace(QVideoFrameFormat::ColorSpace_BT709);
/* Copy the original instance to copy and verify if both the instances
@@ -489,7 +517,7 @@ void tst_QVideoFrameFormat::copyAllParameters()
QCOMPARE(copy.frameSize(), QSize(1024, 768));
QCOMPARE(copy.scanLineDirection(), QVideoFrameFormat::BottomToTop);
QCOMPARE(copy.viewport(), QRect(0, 0, 1024, 1024));
- QCOMPARE(copy.frameRate(), qreal(15.0));
+ QCOMPARE(copy.streamFrameRate(), qreal(15.0));
QCOMPARE(copy.colorSpace(), QVideoFrameFormat::ColorSpace_BT709);
/* Verify if both the instances are eqaul */
@@ -505,7 +533,7 @@ void tst_QVideoFrameFormat::assignAllParameters()
QSize(64, 64), QVideoFrameFormat::Format_AYUV);
copy.setScanLineDirection(QVideoFrameFormat::TopToBottom);
copy.setViewport(QRect(0, 0, 640, 320));
- copy.setFrameRate(qreal(7.5));
+ copy.setStreamFrameRate(qreal(7.5));
copy.setColorSpace(QVideoFrameFormat::ColorSpace_BT601);
/* Create the instance and set all the parameters. */
@@ -513,7 +541,7 @@ void tst_QVideoFrameFormat::assignAllParameters()
QSize(1024, 768), QVideoFrameFormat::Format_ARGB8888);
original.setScanLineDirection(QVideoFrameFormat::BottomToTop);
original.setViewport(QRect(0, 0, 1024, 1024));
- original.setFrameRate(qreal(15.0));
+ original.setStreamFrameRate(qreal(15.0));
original.setColorSpace(QVideoFrameFormat::ColorSpace_BT709);
/* Assign the original instance to copy and verify if both the instancess
@@ -524,7 +552,7 @@ void tst_QVideoFrameFormat::assignAllParameters()
QCOMPARE(copy.frameSize(), QSize(1024, 768));
QCOMPARE(copy.scanLineDirection(), QVideoFrameFormat::BottomToTop);
QCOMPARE(copy.viewport(), QRect(0, 0, 1024, 1024));
- QCOMPARE(copy.frameRate(), qreal(15.0));
+ QCOMPARE(copy.streamFrameRate(), qreal(15.0));
QCOMPARE(copy.colorSpace(), QVideoFrameFormat::ColorSpace_BT709);
/* Verify if both the instances are eqaul */
diff --git a/tests/auto/unit/multimedia/qvideotexturehelper/CMakeLists.txt b/tests/auto/unit/multimedia/qvideotexturehelper/CMakeLists.txt
new file mode 100644
index 000000000..a47b46d5b
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideotexturehelper/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_test(tst_qvideotexturehelper
+ SOURCES
+ tst_qvideotexturehelper.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::MultimediaPrivate
+)
diff --git a/tests/auto/unit/multimedia/qvideotexturehelper/tst_qvideotexturehelper.cpp b/tests/auto/unit/multimedia/qvideotexturehelper/tst_qvideotexturehelper.cpp
new file mode 100644
index 000000000..aa166af54
--- /dev/null
+++ b/tests/auto/unit/multimedia/qvideotexturehelper/tst_qvideotexturehelper.cpp
@@ -0,0 +1,260 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtCore/qbytearray.h>
+#include <QtTest/qtest.h>
+
+#include <private/qvideotexturehelper_p.h>
+#include <qvideoframe.h>
+
+#include "qvideoframeformat.h"
+
+QT_USE_NAMESPACE
+
+struct ColorSpaceCoeff
+{
+ float a;
+ float b;
+ float c;
+};
+
+// Coefficients used in ITU-R BT.709-6 Table 3 - Signal format
+constexpr ColorSpaceCoeff BT709Coefficients = {
+ 0.2126f, 0.7152f, 0.0722f // E_g' = 0.2126 * E_R' + 0.7152 * E_G' + 0.0722 * E_B'
+ //
+ // Note that the other coefficients can be derived from a and c
+ // to re-normalize the values, see ITU-R BT.601-7, section 2.5.2
+ //
+ // E_CB' = (E_B' - E_g') / 1.8556 -> 1.8556 == (1-0.0722) * 2
+ // E_CR' = (E_R' - E_g') / 1.5748 -> 1.5748 == (1-0.2126) * 2
+};
+
+// Coefficients used in ITU-R BT.2020-2 Table 4 - Signal format
+constexpr ColorSpaceCoeff BT2020Coefficients = {
+ 0.2627f, 0.6780f, 0.0593f // Y_c' = (0.2627 R + 0.6780 G + 0.05938 B)'
+ // C_B' = (B' - Y') / 1.8814 -> 1.8814 == 2*(1-0.0593)
+ // C_R' = (R' - Y') / 1.4746 -> 1.4746 == 2*(1-0.2627)
+};
+
+struct ColorSpaceEntry
+{
+ QVideoFrameFormat::ColorSpace colorSpace;
+ QVideoFrameFormat::ColorRange colorRange;
+ ColorSpaceCoeff coefficients;
+};
+
+// clang-format off
+const std::vector<ColorSpaceEntry> colorSpaces = {
+ {
+ QVideoFrameFormat::ColorSpace_BT709,
+ QVideoFrameFormat::ColorRange_Video,
+ BT709Coefficients
+ },
+ {
+ QVideoFrameFormat::ColorSpace_BT709,
+ QVideoFrameFormat::ColorRange_Full,
+ BT709Coefficients
+ },
+ {
+ QVideoFrameFormat::ColorSpace_BT2020,
+ QVideoFrameFormat::ColorRange_Video,
+ BT2020Coefficients
+ },
+ {
+ QVideoFrameFormat::ColorSpace_BT2020,
+ QVideoFrameFormat::ColorRange_Full,
+ BT2020Coefficients
+ }
+};
+
+ColorSpaceCoeff getColorSpaceCoef(QVideoFrameFormat::ColorSpace colorSpace,
+ QVideoFrameFormat::ColorRange range)
+{
+ const auto it = std::find_if(colorSpaces.begin(), colorSpaces.end(),
+ [&](const ColorSpaceEntry &p) {
+ return p.colorSpace == colorSpace && p.colorRange == range;
+ });
+
+ if (it != colorSpaces.end())
+ return it->coefficients;
+
+ Q_ASSERT(false);
+
+ return {};
+}
+
+QMatrix4x4 yuv2rgb(QVideoFrameFormat::ColorSpace colorSpace, QVideoFrameFormat::ColorRange range)
+{
+ constexpr float max8bit = static_cast<float>(255);
+ constexpr float uvOffset = -128.0f/max8bit; // Really -0.5, but carried over from fixed point
+
+ QMatrix4x4 normalizeYUV;
+
+ if (range == QVideoFrameFormat::ColorRange_Video) {
+ // YUV signal is assumed to be in limited range 8 bit representation,
+ // where Y is in range [16..235] and U and V are in range [16..240].
+ // Shaders use floats in [0..1], so we scale the values accordingly.
+ constexpr float yRange = (235 - 16) / max8bit;
+ constexpr float yOffset = -16 / max8bit;
+ constexpr float uvRange = (240 - 16) / max8bit;
+
+ // Second, stretch limited range YUV signals to full range
+ normalizeYUV.scale(1/yRange, 1/uvRange, 1/uvRange);
+
+ // First, pull limited range signals down so that they start on 0
+ normalizeYUV.translate(yOffset, uvOffset, uvOffset);
+ } else {
+ normalizeYUV.translate(0.0f, uvOffset, uvOffset);
+ }
+
+ const auto [a, b, c] = getColorSpaceCoef(colorSpace, range);
+
+ // Re-normalization coefficients that restores the color difference
+ // signals to (-0.5..0.5)
+ const auto d = 2 * (1.0f - c);
+ const auto e = 2 * (1.0f - a);
+
+ // Color matrix from ITU-R BT.709-6 Table 3 - Signal Format
+ // Same as ITU-R BT.2020-2 Table 4 - Signal format
+ const QMatrix4x4 rgb2yuv {
+ a, b, c, 0.0f, // Item 3.2: E_g' = a * E_R' + b * E_G' + c * E_B'
+ -a/d, -b/d, (1-c)/d, 0.0f, // Item 3.3: E_CB' = (E_B' - E_g')/d
+ (1-a)/e, -b/e, -c/e, 0.0f, // Item 3.3: E_CR' = (E_R' - E_g')/e
+ 0.0f, 0.0f, 0.0f, 1.0f
+ };
+
+ const QMatrix4x4 yuv2rgb = rgb2yuv.inverted();
+
+ // Read backwards:
+ // 1. Offset and scale YUV signal to be in range [0..1]
+ // 3. Convert to RGB in range [0..1]
+ return yuv2rgb * normalizeYUV;
+}
+
+// clang-format on
+
+bool fuzzyCompareWithTolerance(const QMatrix4x4 &computed, const QMatrix4x4 &baseline,
+ float tolerance)
+{
+ const float *computedData = computed.data();
+ const float *baselineData = baseline.data();
+ for (int i = 0; i < 16; ++i) {
+ const float c = computedData[i];
+ const float b = baselineData[i];
+
+ bool difference = false;
+ if (qFuzzyIsNull(c) && qFuzzyIsNull(b))
+ continue;
+
+ difference = 2 * (std::abs(c - b) / (c + b)) > tolerance;
+
+ if (difference) {
+ qDebug() << "Mismatch at index" << i << c << "vs" << b;
+ qDebug() << "Computed:";
+ qDebug() << computed;
+ qDebug() << "Baseline:";
+ qDebug() << baseline;
+
+ return false;
+ }
+ }
+ return true;
+}
+
+bool fuzzyCompareWithTolerance(const QVector3D &computed, const QVector3D &baseline,
+ float tolerance)
+{
+ auto fuzzyCompare = [](float c, float b, float tolerance) {
+ if (std::abs(c) < tolerance && std::abs(b) < tolerance)
+ return true;
+
+ return 2 * std::abs(c - b) / (c + b) < tolerance;
+ };
+
+ const bool equal = fuzzyCompare(computed.x(), baseline.x(), tolerance)
+ && fuzzyCompare(computed.y(), baseline.y(), tolerance)
+ && fuzzyCompare(computed.z(), baseline.z(), tolerance);
+
+ if (!equal) {
+ qDebug() << "Vectors are different. Computed:";
+ qDebug() << computed;
+ qDebug() << "Baseline:";
+ qDebug() << baseline;
+ }
+
+ return equal;
+}
+
+QMatrix4x4 getColorMatrix(const QByteArray &uniformDataBytes)
+{
+ const auto uniformData =
+ reinterpret_cast<const QVideoTextureHelper::UniformData *>(uniformDataBytes.data());
+ const auto colorMatrixData = reinterpret_cast<const float *>(uniformData->colorMatrix);
+
+ return QMatrix4x4{ colorMatrixData }.transposed();
+};
+
+class tst_qvideotexturehelper : public QObject
+{
+ Q_OBJECT
+public:
+private slots:
+ void updateUniformData_populatesYUV2RGBColorMatrix_data()
+ {
+ QTest::addColumn<QVideoFrameFormat::ColorSpace>("colorSpace");
+ QTest::addColumn<QVideoFrameFormat::ColorRange>("colorRange");
+
+ QTest::addRow("BT709_full")
+ << QVideoFrameFormat::ColorSpace_BT709 << QVideoFrameFormat::ColorRange_Full;
+
+ QTest::addRow("BT709_video")
+ << QVideoFrameFormat::ColorSpace_BT709 << QVideoFrameFormat::ColorRange_Video;
+
+ QTest::addRow("BT2020_full")
+ << QVideoFrameFormat::ColorSpace_BT2020 << QVideoFrameFormat::ColorRange_Full;
+
+ QTest::addRow("BT2020_video")
+ << QVideoFrameFormat::ColorSpace_BT2020 << QVideoFrameFormat::ColorRange_Video;
+ }
+
+ void updateUniformData_populatesYUV2RGBColorMatrix()
+ {
+ QFETCH(const QVideoFrameFormat::ColorSpace, colorSpace);
+ QFETCH(const QVideoFrameFormat::ColorRange, colorRange);
+
+ // Arrange
+ QVideoFrameFormat format{ {}, QVideoFrameFormat::Format_NV12 };
+ format.setColorSpace(colorSpace);
+ format.setColorRange(colorRange);
+
+ const QMatrix4x4 expected = yuv2rgb(colorSpace, colorRange);
+
+ // Act
+ QByteArray data;
+ QVideoTextureHelper::updateUniformData(&data, format, {}, {}, 0.0);
+ const QMatrix4x4 actual = getColorMatrix(data);
+
+ // Assert
+ QVERIFY(fuzzyCompareWithTolerance(actual, expected, 1e-3f));
+
+ { // Sanity check: Color matrix maps white to white
+ constexpr QVector3D expectedWhiteRgb{ 1.0f, 1.0f, 1.0f };
+ const QVector3D whiteYuv = expected.inverted().map(expectedWhiteRgb);
+
+ const QVector3D actualWhiteRgb = actual.map(whiteYuv);
+ QVERIFY(fuzzyCompareWithTolerance(actualWhiteRgb, expectedWhiteRgb, 5e-4f));
+ }
+
+ { // Sanity check: Color matrix maps black to black
+ constexpr QVector3D expectedBlackRgb{ 0.0f, 0.0f, 0.0f };
+ const QVector3D blackYuv = expected.inverted().map(expectedBlackRgb);
+
+ const QVector3D actualBlackRgb = actual.map(blackYuv);
+ QVERIFY(fuzzyCompareWithTolerance(actualBlackRgb, expectedBlackRgb, 5e-4f));
+ }
+ }
+};
+
+QTEST_MAIN(tst_qvideotexturehelper)
+
+#include "tst_qvideotexturehelper.moc"
diff --git a/tests/auto/unit/multimedia/qwavedecoder/CMakeLists.txt b/tests/auto/unit/multimedia/qwavedecoder/CMakeLists.txt
index 29db70754..4567e95ac 100644
--- a/tests/auto/unit/multimedia/qwavedecoder/CMakeLists.txt
+++ b/tests/auto/unit/multimedia/qwavedecoder/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qwavedecoder.pro.
#####################################################################
@@ -13,7 +16,7 @@ list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_qwavedecoder
SOURCES
tst_qwavedecoder.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
Qt::Network
diff --git a/tests/auto/unit/multimedia/qwavedecoder/data/gendata.sh b/tests/auto/unit/multimedia/qwavedecoder/data/gendata.sh
index c799e6f9f..99a04129b 100755
--- a/tests/auto/unit/multimedia/qwavedecoder/data/gendata.sh
+++ b/tests/auto/unit/multimedia/qwavedecoder/data/gendata.sh
@@ -1,31 +1,6 @@
#!/bin/bash
-#############################################################################
-##
-## Copyright (C) 2016 The Qt Company Ltd.
-## Contact: https://www.qt.io/licensing/
-##
-## This file is the build configuration utility of the Qt Toolkit.
-##
-## $QT_BEGIN_LICENSE:GPL-EXCEPT$
-## Commercial License Usage
-## Licensees holding valid commercial Qt licenses may use this file in
-## accordance with the commercial license agreement provided with the
-## Software or, alternatively, in accordance with the terms contained in
-## a written agreement between you and The Qt Company. For licensing terms
-## and conditions see https://www.qt.io/terms-conditions. For further
-## information use the contact form at https://www.qt.io/contact-us.
-##
-## GNU General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU
-## General Public License version 3 as published by the Free Software
-## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-## included in the packaging of this file. Please review the following
-## information to ensure the GNU General Public License requirements will
-## be met: https://www.gnu.org/licenses/gpl-3.0.html.
-##
-## $QT_END_LICENSE$
-##
-#############################################################################
+# Copyright (C) 2016 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
# Generate some simple test data. Uses "sox".
diff --git a/tests/auto/unit/multimedia/qwavedecoder/data/isawav_1_8_8000_even_bext.wav b/tests/auto/unit/multimedia/qwavedecoder/data/isawav_1_8_8000_even_bext.wav
new file mode 100644
index 000000000..531b0ee58
--- /dev/null
+++ b/tests/auto/unit/multimedia/qwavedecoder/data/isawav_1_8_8000_even_bext.wav
Binary files differ
diff --git a/tests/auto/unit/multimedia/qwavedecoder/data/isawav_1_8_8000_odd_bext.wav b/tests/auto/unit/multimedia/qwavedecoder/data/isawav_1_8_8000_odd_bext.wav
new file mode 100644
index 000000000..467be6312
--- /dev/null
+++ b/tests/auto/unit/multimedia/qwavedecoder/data/isawav_1_8_8000_odd_bext.wav
Binary files differ
diff --git a/tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp b/tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp
index d4fa9e5b8..079f98075 100644
--- a/tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp
+++ b/tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <qwavedecoder.h>
@@ -85,7 +58,7 @@ void tst_QWaveDecoder::cleanupTestCase()
static QString testFilePath(const char *filename)
{
- QString path = QString("data/%1").arg(filename);
+ QString path = QStringLiteral("data/%1").arg(filename);
return QFINDTESTDATA(path);
}
@@ -96,33 +69,35 @@ void tst_QWaveDecoder::file_data()
QTest::addColumn<int>("channels");
QTest::addColumn<int>("samplesize");
QTest::addColumn<int>("samplerate");
- QTest::addColumn<QAudioFormat::Endian>("byteorder");
-
- QTest::newRow("File is empty") << testFilePath("empty.wav") << tst_QWaveDecoder::NotAWav << -1 << -1 << -1 << QAudioFormat::LittleEndian;
- QTest::newRow("File is one byte") << testFilePath("onebyte.wav") << tst_QWaveDecoder::NotAWav << -1 << -1 << -1 << QAudioFormat::LittleEndian;
- QTest::newRow("File is not a wav(text)") << testFilePath("notawav.wav") << tst_QWaveDecoder::NotAWav << -1 << -1 << -1 << QAudioFormat::LittleEndian;
- QTest::newRow("Wav file has no sample data") << testFilePath("nosampledata.wav") << tst_QWaveDecoder::NoSampleData << -1 << -1 << -1 << QAudioFormat::LittleEndian;
- QTest::newRow("corrupt fmt chunk descriptor") << testFilePath("corrupt_fmtdesc_1_16_8000.le.wav") << tst_QWaveDecoder::FormatDescriptor << -1 << -1 << -1 << QAudioFormat::LittleEndian;
- QTest::newRow("corrupt fmt string") << testFilePath("corrupt_fmtstring_1_16_8000.le.wav") << tst_QWaveDecoder::FormatString << -1 << -1 << -1 << QAudioFormat::LittleEndian;
- QTest::newRow("corrupt data chunk descriptor") << testFilePath("corrupt_datadesc_1_16_8000.le.wav") << tst_QWaveDecoder::DataDescriptor << -1 << -1 << -1 << QAudioFormat::LittleEndian;
-
- QTest::newRow("File isawav_1_8_8000.wav") << testFilePath("isawav_1_8_8000.wav") << tst_QWaveDecoder::None << 1 << 8 << 8000 << QAudioFormat::LittleEndian;
- QTest::newRow("File isawav_1_8_44100.wav") << testFilePath("isawav_1_8_44100.wav") << tst_QWaveDecoder::None << 1 << 8 << 44100 << QAudioFormat::LittleEndian;
- QTest::newRow("File isawav_2_8_8000.wav") << testFilePath("isawav_2_8_8000.wav") << tst_QWaveDecoder::None << 2 << 8 << 8000 << QAudioFormat::LittleEndian;
- QTest::newRow("File isawav_2_8_44100.wav") << testFilePath("isawav_2_8_44100.wav") << tst_QWaveDecoder::None << 2 << 8 << 44100 << QAudioFormat::LittleEndian;
-
- QTest::newRow("File isawav_1_16_8000_le.wav") << testFilePath("isawav_1_16_8000_le.wav") << tst_QWaveDecoder::None << 1 << 16 << 8000 << QAudioFormat::LittleEndian;
- QTest::newRow("File isawav_1_16_44100_le.wav") << testFilePath("isawav_1_16_44100_le.wav") << tst_QWaveDecoder::None << 1 << 16 << 44100 << QAudioFormat::LittleEndian;
- QTest::newRow("File isawav_2_16_8000_be.wav") << testFilePath("isawav_2_16_8000_be.wav") << tst_QWaveDecoder::None << 2 << 16 << 8000 << QAudioFormat::BigEndian;
- QTest::newRow("File isawav_2_16_44100_be.wav") << testFilePath("isawav_2_16_44100_be.wav") << tst_QWaveDecoder::None << 2 << 16 << 44100 << QAudioFormat::BigEndian;
- // The next file has extra data in the wave header.
- QTest::newRow("File isawav_1_16_44100_le_2.wav") << testFilePath("isawav_1_16_44100_le_2.wav") << tst_QWaveDecoder::None << 1 << 16 << 44100 << QAudioFormat::LittleEndian;
+ QTest::newRow("File is empty") << testFilePath("empty.wav") << tst_QWaveDecoder::NotAWav << -1 << -1 << -1;
+ QTest::newRow("File is one byte") << testFilePath("onebyte.wav") << tst_QWaveDecoder::NotAWav << -1 << -1 << -1;
+ QTest::newRow("File is not a wav(text)") << testFilePath("notawav.wav") << tst_QWaveDecoder::NotAWav << -1 << -1 << -1;
+ QTest::newRow("Wav file has no sample data") << testFilePath("nosampledata.wav") << tst_QWaveDecoder::NoSampleData << -1 << -1 << -1;
+ QTest::newRow("corrupt fmt chunk descriptor") << testFilePath("corrupt_fmtdesc_1_16_8000.le.wav") << tst_QWaveDecoder::FormatDescriptor << -1 << -1 << -1;
+ QTest::newRow("corrupt fmt string") << testFilePath("corrupt_fmtstring_1_16_8000.le.wav") << tst_QWaveDecoder::FormatString << -1 << -1 << -1;
+ QTest::newRow("corrupt data chunk descriptor") << testFilePath("corrupt_datadesc_1_16_8000.le.wav") << tst_QWaveDecoder::DataDescriptor << -1 << -1 << -1;
+
+ QTest::newRow("File isawav_1_8_8000.wav") << testFilePath("isawav_1_8_8000.wav") << tst_QWaveDecoder::None << 1 << 8 << 8000;
+ QTest::newRow("File isawav_1_8_44100.wav") << testFilePath("isawav_1_8_44100.wav") << tst_QWaveDecoder::None << 1 << 8 << 44100;
+ QTest::newRow("File isawav_2_8_8000.wav") << testFilePath("isawav_2_8_8000.wav") << tst_QWaveDecoder::None << 2 << 8 << 8000;
+ QTest::newRow("File isawav_2_8_44100.wav") << testFilePath("isawav_2_8_44100.wav") << tst_QWaveDecoder::None << 2 << 8 << 44100;
+
+ QTest::newRow("File isawav_1_16_8000_le.wav") << testFilePath("isawav_1_16_8000_le.wav") << tst_QWaveDecoder::None << 1 << 16 << 8000;
+ QTest::newRow("File isawav_1_16_44100_le.wav") << testFilePath("isawav_1_16_44100_le.wav") << tst_QWaveDecoder::None << 1 << 16 << 44100;
+ QTest::newRow("File isawav_2_16_8000_be.wav") << testFilePath("isawav_2_16_8000_be.wav") << tst_QWaveDecoder::None << 2 << 16 << 8000;
+ QTest::newRow("File isawav_2_16_44100_be.wav") << testFilePath("isawav_2_16_44100_be.wav") << tst_QWaveDecoder::None << 2 << 16 << 44100;
+ // The next file has extra data in the wave header.
+ QTest::newRow("File isawav_1_16_44100_le_2.wav") << testFilePath("isawav_1_16_44100_le_2.wav") << tst_QWaveDecoder::None << 1 << 16 << 44100;
+ // The next file has embedded bext chunk with odd payload (QTBUG-122193)
+ QTest::newRow("File isawav_1_8_8000_odd_bext.wav") << testFilePath("isawav_1_8_8000_odd_bext.wav") << tst_QWaveDecoder::None << 1 << 8 << 8000;
+ // The next file has embedded bext chunk with even payload
+ QTest::newRow("File isawav_1_8_8000_even_bext.wav") << testFilePath("isawav_1_8_8000_even_bext.wav") << tst_QWaveDecoder::None << 1 << 8 << 8000;
// 32 bit waves are not supported
- QTest::newRow("File isawav_1_32_8000_le.wav") << testFilePath("isawav_1_32_8000_le.wav") << tst_QWaveDecoder::FormatDescriptor << 1 << 32 << 8000 << QAudioFormat::LittleEndian;
- QTest::newRow("File isawav_1_32_44100_le.wav") << testFilePath("isawav_1_32_44100_le.wav") << tst_QWaveDecoder::FormatDescriptor << 1 << 32 << 44100 << QAudioFormat::LittleEndian;
- QTest::newRow("File isawav_2_32_8000_be.wav") << testFilePath("isawav_2_32_8000_be.wav") << tst_QWaveDecoder::FormatDescriptor << 2 << 32 << 8000 << QAudioFormat::BigEndian;
- QTest::newRow("File isawav_2_32_44100_be.wav") << testFilePath("isawav_2_32_44100_be.wav") << tst_QWaveDecoder::FormatDescriptor << 2 << 32 << 44100 << QAudioFormat::BigEndian;
+ QTest::newRow("File isawav_1_32_8000_le.wav") << testFilePath("isawav_1_32_8000_le.wav") << tst_QWaveDecoder::FormatDescriptor << 1 << 32 << 8000;
+ QTest::newRow("File isawav_1_32_44100_le.wav") << testFilePath("isawav_1_32_44100_le.wav") << tst_QWaveDecoder::FormatDescriptor << 1 << 32 << 44100;
+ QTest::newRow("File isawav_2_32_8000_be.wav") << testFilePath("isawav_2_32_8000_be.wav") << tst_QWaveDecoder::FormatDescriptor << 2 << 32 << 8000;
+ QTest::newRow("File isawav_2_32_44100_be.wav") << testFilePath("isawav_2_32_44100_be.wav") << tst_QWaveDecoder::FormatDescriptor << 2 << 32 << 44100;
}
void tst_QWaveDecoder::file()
@@ -132,7 +107,6 @@ void tst_QWaveDecoder::file()
QFETCH(int, channels);
QFETCH(int, samplesize);
QFETCH(int, samplerate);
- QFETCH(QAudioFormat::Endian, byteorder);
QFile stream;
stream.setFileName(file);
@@ -141,8 +115,10 @@ void tst_QWaveDecoder::file()
QVERIFY(stream.isOpen());
QWaveDecoder waveDecoder(&stream);
- QSignalSpy validFormatSpy(&waveDecoder, SIGNAL(formatKnown()));
- QSignalSpy parsingErrorSpy(&waveDecoder, SIGNAL(parsingError()));
+ QSignalSpy validFormatSpy(&waveDecoder, &QWaveDecoder::formatKnown);
+ QSignalSpy parsingErrorSpy(&waveDecoder, &QWaveDecoder::parsingError);
+
+ QVERIFY(waveDecoder.open(QIODeviceBase::ReadOnly));
if (corruption == NotAWav) {
QSKIP("Not all failures detected correctly yet");
@@ -174,11 +150,8 @@ void tst_QWaveDecoder::file()
QAudioFormat format = waveDecoder.audioFormat();
QVERIFY(format.isValid());
QVERIFY(format.channelCount() == channels);
- QVERIFY(format.sampleSize() == samplesize);
+ QCOMPARE(format.bytesPerSample() * 8, samplesize);
QVERIFY(format.sampleRate() == samplerate);
- if (format.sampleSize() != 8) {
- QVERIFY(format.byteOrder() == byteorder);
- }
}
stream.close();
@@ -191,7 +164,6 @@ void tst_QWaveDecoder::http()
QFETCH(int, channels);
QFETCH(int, samplesize);
QFETCH(int, samplerate);
- QFETCH(QAudioFormat::Endian, byteorder);
QFile stream;
stream.setFileName(file);
@@ -204,8 +176,10 @@ void tst_QWaveDecoder::http()
QNetworkReply *reply = nam.get(QNetworkRequest(QUrl::fromLocalFile(file)));
QWaveDecoder waveDecoder(reply);
- QSignalSpy validFormatSpy(&waveDecoder, SIGNAL(formatKnown()));
- QSignalSpy parsingErrorSpy(&waveDecoder, SIGNAL(parsingError()));
+ QSignalSpy validFormatSpy(&waveDecoder, &QWaveDecoder::formatKnown);
+ QSignalSpy parsingErrorSpy(&waveDecoder, &QWaveDecoder::parsingError);
+
+ QVERIFY(waveDecoder.open(QIODeviceBase::ReadOnly));
if (corruption == NotAWav) {
QSKIP("Not all failures detected correctly yet");
@@ -237,11 +211,8 @@ void tst_QWaveDecoder::http()
QAudioFormat format = waveDecoder.audioFormat();
QVERIFY(format.isValid());
QVERIFY(format.channelCount() == channels);
- QVERIFY(format.sampleSize() == samplesize);
+ QCOMPARE(format.bytesPerSample() * 8, samplesize);
QVERIFY(format.sampleRate() == samplerate);
- if (format.sampleSize() != 8) {
- QVERIFY(format.byteOrder() == byteorder);
- }
}
delete reply;
@@ -256,7 +227,9 @@ void tst_QWaveDecoder::readAllAtOnce()
QVERIFY(stream.isOpen());
QWaveDecoder waveDecoder(&stream);
- QSignalSpy validFormatSpy(&waveDecoder, SIGNAL(formatKnown()));
+ QSignalSpy validFormatSpy(&waveDecoder, &QWaveDecoder::formatKnown);
+
+ QVERIFY(waveDecoder.open(QIODeviceBase::ReadOnly));
QTRY_COMPARE(validFormatSpy.count(), 1);
QVERIFY(waveDecoder.size() > 0);
@@ -282,7 +255,9 @@ void tst_QWaveDecoder::readPerByte()
QVERIFY(stream.isOpen());
QWaveDecoder waveDecoder(&stream);
- QSignalSpy validFormatSpy(&waveDecoder, SIGNAL(formatKnown()));
+ QSignalSpy validFormatSpy(&waveDecoder, &QWaveDecoder::formatKnown);
+
+ QVERIFY(waveDecoder.open(QIODeviceBase::ReadOnly));
QTRY_COMPARE(validFormatSpy.count(), 1);
QVERIFY(waveDecoder.size() > 0);
diff --git a/tests/auto/unit/multimediawidgets/CMakeLists.txt b/tests/auto/unit/multimediawidgets/CMakeLists.txt
index 336488835..87adc0190 100644
--- a/tests/auto/unit/multimediawidgets/CMakeLists.txt
+++ b/tests/auto/unit/multimediawidgets/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from multimediawidgets.pro.
add_subdirectory(qcamerawidgets)
diff --git a/tests/auto/unit/multimediawidgets/qcamerawidgets/CMakeLists.txt b/tests/auto/unit/multimediawidgets/qcamerawidgets/CMakeLists.txt
index 9df51a5b1..486f34690 100644
--- a/tests/auto/unit/multimediawidgets/qcamerawidgets/CMakeLists.txt
+++ b/tests/auto/unit/multimediawidgets/qcamerawidgets/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qcamerawidgets.pro.
#####################################################################
@@ -9,11 +12,11 @@ qt_internal_add_test(tst_qcamerawidgets
tst_qcamerawidgets.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::MultimediaPrivate
Qt::MultimediaWidgetsPrivate
Qt::Widgets
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp b/tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp
index 7db456190..6c31a4b66 100644
--- a/tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp
+++ b/tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QDebug>
@@ -42,10 +15,11 @@
#include <qvideosink.h>
#include "qmockmediacapturesession.h"
-#include "qmockintegration_p.h"
+#include "qmockintegration.h"
QT_USE_NAMESPACE
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
class tst_QCameraWidgets: public QObject
{
@@ -58,9 +32,6 @@ public slots:
private slots:
void testCameraEncodingProperyChange();
void testSetVideoOutput();
-
-private:
- QMockIntegration mockIntegration;
};
void tst_QCameraWidgets::initTestCase()
@@ -80,12 +51,12 @@ void tst_QCameraWidgets::testCameraEncodingProperyChange()
session.setCamera(&camera);
session.setImageCapture(&imageCapture);
- QSignalSpy activeChangedSignal(&camera, SIGNAL(activeChanged(bool)));
+ QSignalSpy activeChangedSignal(&camera, &QCamera::activeChanged);
camera.start();
QCOMPARE(camera.isActive(), true);
- QCOMPARE(activeChangedSignal.count(), 1);
+ QCOMPARE(activeChangedSignal.size(), 1);
}
void tst_QCameraWidgets::testSetVideoOutput()
diff --git a/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/CMakeLists.txt b/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/CMakeLists.txt
index 227eb18ff..599042725 100644
--- a/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/CMakeLists.txt
+++ b/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qgraphicsvideoitem.pro.
#####################################################################
@@ -9,10 +12,10 @@ qt_internal_add_test(tst_qgraphicsvideoitem
tst_qgraphicsvideoitem.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
Qt::MultimediaWidgetsPrivate
Qt::Widgets
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp b/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp
index 583115f99..2a1538edc 100644
--- a/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp
+++ b/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtmultimediaglobal.h>
#include "qgraphicsvideoitem.h"
@@ -41,9 +14,12 @@
#include <QtWidgets/qgraphicsscene.h>
#include <QtWidgets/qgraphicsview.h>
-#include <qmockintegration_p.h>
+#include <qmockintegration.h>
QT_USE_NAMESPACE
+
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
class tst_QGraphicsVideoItem : public QObject
{
Q_OBJECT
@@ -66,9 +42,6 @@ private slots:
void boundingRect();
void paint();
-
-public:
- QMockIntegration integration;
};
class QtTestGraphicsVideoItem : public QGraphicsVideoItem
@@ -241,7 +214,7 @@ void tst_QGraphicsVideoItem::nativeSize()
QCOMPARE(item.nativeSize(), QSizeF());
- QSignalSpy spy(&item, SIGNAL(nativeSizeChanged(QSizeF)));
+ QSignalSpy spy(&item, &QGraphicsVideoItem::nativeSizeChanged);
QVideoFrameFormat format(frameSize, QVideoFrameFormat::Format_ARGB8888);
format.setViewport(viewport);
@@ -250,7 +223,7 @@ void tst_QGraphicsVideoItem::nativeSize()
QCoreApplication::processEvents();
QCOMPARE(item.nativeSize(), nativeSize);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.last().first().toSizeF(), nativeSize);
}
diff --git a/tests/auto/unit/multimediawidgets/qmediaplayerwidgets/CMakeLists.txt b/tests/auto/unit/multimediawidgets/qmediaplayerwidgets/CMakeLists.txt
index b1859fb39..7c54e67b9 100644
--- a/tests/auto/unit/multimediawidgets/qmediaplayerwidgets/CMakeLists.txt
+++ b/tests/auto/unit/multimediawidgets/qmediaplayerwidgets/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmediaplayerwidgets.pro.
#####################################################################
@@ -9,12 +12,12 @@ qt_internal_add_test(tst_qmediaplayerwidgets
tst_qmediaplayerwidgets.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
# Remove: L${CMAKE_CURRENT_SOURCE_DIR}
Qt::Gui
Qt::MultimediaPrivate
Qt::MultimediaWidgetsPrivate
Qt::Network
Qt::Widgets
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimediawidgets/qmediaplayerwidgets/tst_qmediaplayerwidgets.cpp b/tests/auto/unit/multimediawidgets/qmediaplayerwidgets/tst_qmediaplayerwidgets.cpp
index a8fafc86b..e0f23d477 100644
--- a/tests/auto/unit/multimediawidgets/qmediaplayerwidgets/tst_qmediaplayerwidgets.cpp
+++ b/tests/auto/unit/multimediawidgets/qmediaplayerwidgets/tst_qmediaplayerwidgets.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/qdebug.h>
@@ -38,47 +11,22 @@
#include <private/qplatformmediaplayer_p.h>
#include "qvideosink.h"
-#include "qmockintegration_p.h"
+#include "qmockintegration.h"
QT_USE_NAMESPACE
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
class tst_QMediaPlayerWidgets: public QObject
{
Q_OBJECT
-public slots:
- void initTestCase();
- void cleanupTestCase();
- void init();
- void cleanup();
-
private slots:
void testSetVideoOutput();
void testSetVideoOutputNoService();
void testSetVideoOutputNoControl();
-
-private:
- QMockIntegration *mockIntegration;
};
-void tst_QMediaPlayerWidgets::initTestCase()
-{
- mockIntegration = new QMockIntegration;
-}
-
-void tst_QMediaPlayerWidgets::cleanupTestCase()
-{
- delete mockIntegration;
-}
-
-void tst_QMediaPlayerWidgets::init()
-{
-}
-
-void tst_QMediaPlayerWidgets::cleanup()
-{
-}
-
void tst_QMediaPlayerWidgets::testSetVideoOutput()
{
QVideoWidget widget;
@@ -128,9 +76,9 @@ void tst_QMediaPlayerWidgets::testSetVideoOutputNoService()
QGraphicsVideoItem item;
QVideoSink surface;
- mockIntegration->setFlags(QMockIntegration::NoPlayerInterface);
+ QMockIntegration::instance()->setFlags(QMockIntegration::NoPlayerInterface);
QMediaPlayer player;
- mockIntegration->setFlags({});
+ QMockIntegration::instance()->setFlags({});
player.setVideoOutput(&widget);
diff --git a/tests/auto/unit/multimediawidgets/qvideowidget/BLACKLIST b/tests/auto/unit/multimediawidgets/qvideowidget/BLACKLIST
new file mode 100644
index 000000000..2ce9e48fa
--- /dev/null
+++ b/tests/auto/unit/multimediawidgets/qvideowidget/BLACKLIST
@@ -0,0 +1,3 @@
+# QTBUG-110453
+[fullScreen]
+android
diff --git a/tests/auto/unit/multimediawidgets/qvideowidget/CMakeLists.txt b/tests/auto/unit/multimediawidgets/qvideowidget/CMakeLists.txt
index 673cd7ff6..b179b0b3b 100644
--- a/tests/auto/unit/multimediawidgets/qvideowidget/CMakeLists.txt
+++ b/tests/auto/unit/multimediawidgets/qvideowidget/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qvideowidget.pro.
#####################################################################
@@ -9,10 +12,10 @@ qt_internal_add_test(tst_qvideowidget
tst_qvideowidget.cpp
INCLUDE_DIRECTORIES
../../mockbackend
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::MultimediaPrivate
Qt::MultimediaWidgetsPrivate
Qt::Widgets
- QtMultimediaMockBackend
+ MockMultimediaPlugin
)
diff --git a/tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp b/tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp
index 1f43a2be4..3cfe5d18e 100644
--- a/tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp
+++ b/tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp
@@ -1,32 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtmultimediaglobal.h>
#include <QtTest/QtTest>
@@ -40,13 +13,19 @@
#include <QtWidgets/qapplication.h>
-#include <qmockintegration_p.h>
+#include <qmockintegration.h>
#include <qmockvideosink.h>
QT_USE_NAMESPACE
+
+Q_ENABLE_MOCK_MULTIMEDIA_PLUGIN
+
class tst_QVideoWidget : public QObject
{
Q_OBJECT
+public slots:
+ void initTestCase();
+
private slots:
void nullObject();
@@ -82,6 +61,15 @@ public:
}
};
+void tst_QVideoWidget::initTestCase()
+{
+#ifdef Q_OS_MACOS
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").toLower() == "ci")
+ QSKIP("SKIP on macOS CI since metal is not supported, otherwise it often crashes. To be "
+ "fixed.");
+#endif
+}
+
void tst_QVideoWidget::nullObject()
{
QtTestVideoWidget widget;
@@ -117,8 +105,8 @@ void tst_QVideoWidget::show()
void tst_QVideoWidget::aspectRatio()
{
- QMediaPlayer player;
QtTestVideoWidget widget;
+ QMediaPlayer player;
player.setVideoOutput(&widget);
// Test the aspect ratio defaults to keeping the aspect ratio.
@@ -172,11 +160,11 @@ void tst_QVideoWidget::sizeHint()
// QFETCH(QRect, viewport);
QFETCH(QSize, expectedSize);
- QMockIntegration mock;
- QMediaPlayer player;
QtTestVideoWidget widget;
+ QMediaPlayer player;
+
player.setVideoOutput(&widget);
- auto mockSink = mock.lastVideoSink();
+ auto mockSink = QMockIntegration::instance()->lastVideoSink();
widget.show();
QVERIFY(QTest::qWaitForWindowExposed(&widget));
@@ -188,28 +176,28 @@ void tst_QVideoWidget::sizeHint()
void tst_QVideoWidget::fullScreen()
{
- QMediaPlayer player;
QtTestVideoWidget widget;
+ QMediaPlayer player;
player.setVideoOutput(&widget);
widget.showNormal();
QVERIFY(QTest::qWaitForWindowExposed(&widget));
Qt::WindowFlags windowFlags = widget.windowFlags();
- QSignalSpy spy(&widget, SIGNAL(fullScreenChanged(bool)));
+ QSignalSpy spy(&widget, &QVideoWidget::fullScreenChanged);
// Test showing full screen with setFullScreen(true).
widget.setFullScreen(true);
QVERIFY(QTest::qWaitForWindowExposed(&widget));
QCOMPARE(widget.isFullScreen(), true);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(spy.value(0).value(0).toBool(), true);
// Test returning to normal with setFullScreen(false).
widget.setFullScreen(false);
QVERIFY(QTest::qWaitForWindowExposed(&widget));
QCOMPARE(widget.isFullScreen(), false);
- QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.size(), 2);
QCOMPARE(spy.value(1).value(0).toBool(), false);
QCOMPARE(widget.windowFlags(), windowFlags);
@@ -217,35 +205,35 @@ void tst_QVideoWidget::fullScreen()
widget.showFullScreen();
QVERIFY(QTest::qWaitForWindowExposed(&widget));
QCOMPARE(widget.isFullScreen(), true);
- QCOMPARE(spy.count(), 3);
+ QCOMPARE(spy.size(), 3);
QCOMPARE(spy.value(2).value(0).toBool(), true);
// Test returning to normal with showNormal().
widget.showNormal();
QVERIFY(QTest::qWaitForWindowExposed(&widget));
QCOMPARE(widget.isFullScreen(), false);
- QCOMPARE(spy.count(), 4);
+ QCOMPARE(spy.size(), 4);
QCOMPARE(spy.value(3).value(0).toBool(), false);
QCOMPARE(widget.windowFlags(), windowFlags);
// Test setFullScreen(false) and showNormal() do nothing when isFullScreen() == false.
widget.setFullScreen(false);
QCOMPARE(widget.isFullScreen(), false);
- QCOMPARE(spy.count(), 4);
+ QCOMPARE(spy.size(), 4);
widget.showNormal();
QVERIFY(QTest::qWaitForWindowExposed(&widget));
QCOMPARE(widget.isFullScreen(), false);
- QCOMPARE(spy.count(), 4);
+ QCOMPARE(spy.size(), 4);
// Test setFullScreen(true) and showFullScreen() do nothing when isFullScreen() == true.
widget.showFullScreen();
QVERIFY(QTest::qWaitForWindowExposed(&widget));
widget.setFullScreen(true);
QCOMPARE(widget.isFullScreen(), true);
- QCOMPARE(spy.count(), 5);
+ QCOMPARE(spy.size(), 5);
widget.showFullScreen();
QCOMPARE(widget.isFullScreen(), true);
- QCOMPARE(spy.count(), 5);
+ QCOMPARE(spy.size(), 5);
}
static const uchar rgb32ImageData[] =
@@ -256,8 +244,8 @@ static const uchar rgb32ImageData[] =
void tst_QVideoWidget::paint()
{
- QMediaPlayer player;
QtTestVideoWidget widget;
+ QMediaPlayer player;
player.setVideoOutput(&widget);
widget.resize(640,480);
widget.show();
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt
new file mode 100644
index 000000000..08e38e893
--- /dev/null
+++ b/tests/manual/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(audiodecoder)
+add_subdirectory(devices)
+add_subdirectory(minimal-player)
+add_subdirectory(wasm)
+
+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/examples/multimedia/audiodecoder/CMakeLists.txt b/tests/manual/audiodecoder/CMakeLists.txt
index 85b9a45be..758c3a913 100644
--- a/examples/multimedia/audiodecoder/CMakeLists.txt
+++ b/tests/manual/audiodecoder/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
cmake_minimum_required(VERSION 3.16)
project(audiodecoder LANGUAGES CXX)
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/examples/multimedia/audiodecoder/audiodecoder.pro b/tests/manual/audiodecoder/audiodecoder.pro
index 7870d340e..7870d340e 100644
--- a/examples/multimedia/audiodecoder/audiodecoder.pro
+++ b/tests/manual/audiodecoder/audiodecoder.pro
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/examples/multimedia/devices/CMakeLists.txt b/tests/manual/devices/CMakeLists.txt
index 3e19c12ec..cae0c577f 100644
--- a/examples/multimedia/devices/CMakeLists.txt
+++ b/tests/manual/devices/CMakeLists.txt
@@ -1,6 +1,13 @@
+# 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)
@@ -16,7 +23,7 @@ qt_add_executable(devices
)
set_target_properties(devices PROPERTIES
- WIN32_EXECUTABLE TRUE
+ WIN32_EXECUTABLE FALSE
MACOSX_BUNDLE TRUE
)
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/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>
diff --git a/tests/systemtests/audio_playback/sys_audio.qtt b/tests/systemtests/audio_playback/sys_audio.qtt
deleted file mode 100644
index 64629e8fc..000000000
--- a/tests/systemtests/audio_playback/sys_audio.qtt
+++ /dev/null
@@ -1,374 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
-
-testcase = {
-
-/* Notes
-Ensure various mp3, wav, ogg, oga, spx and flac audio files available for playback
-Ensure a valid m3u file referring to locations where indicated media is available on the device
-Ensure the device can ping destinations on the network (if applicable)
-Have an audio stream url available (e.g. http://202.6.74.107:8060/triplej.mp3 for example)
-Ensure access to Internet available to device either via 3G and WiFi
-*/
- initTestCase: function()
- {
- },
-
- play_an_audio_file_data: {
- mp3:[".mp3", "filename", "mpeg audio layer 3"],
- wav:[".wav", "filename", "waveform audio"],
- ogg:[".ogg", "filename", "container format"],
- vorbis:[".oga", "filename", "audio compression format"],
- speex:[".spx", "filename", "speech audio format"],
- flac:[".flac", "filename", "audio file format"]
- },
-
- play_an_audio_file: function(extension, filename, format)
- {
- // Test meta data
- testTitle = "Multimedia - Play Audio File";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Verify that various Audio files can be played.";
- testPreconditions = "Audio files in " + extension + " format are available on device. Launch the native Music player to populate the playlist before opening BGMPTest.";
- testGroups = "BAT, 5.0";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Verify that the playlist has populated the application | |
- | Select the desired "+filename+extension+" and press play | Verify that the " + format + " file is audible on device |
-
- "));
- },
-
- basic_player_controls_for_audio_data: {
- mp3:[".mp3", "filename", "mpeg audio layer 3"],
- wav:[".wav", "filename", "waveform audio"],
- ogg:[".ogg", "filename", "container format"],
- vorbis:[".oga", "filename", "audio compression format"],
- speex:[".spx", "filename", "speech audio format"],
- flac:[".flac", "filename", "audio file format"]
- },
-
- basic_player_controls_for_audio: function(extension, filename, format)
- {
- // Test meta data
- testTitle = "Multimedia - Audio Player Controls";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Verify basic API for controlling audio playback ";
- testPreconditions = "a test "+extension+" file is available on device";
- testGroups = "BAT, 5.0";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select the desired "+filename+extension+" in playlist and press [Play] | Verify that file plays properly from the start |
- | | Verify that the correct track details are displayed in the playlist |
- | Select [Pause] | Verify that file has paused |
- | Select [Play] | Verify that file continues to play properly from where previously paused. |
- | | Verify that the progress bar tracks the progression of the file playback |
- | Select [Next] control | Verify that next file in playlist is playing from start |
- | | Verify that the correct playlist title is highlighted and that the details are correct to reflect the selected audio track |
- | Select [Stop] control | Verify that file has stopped playing |
- | Select [Play] | Verify that file continues to play from start of file. |
- | With file playing, verify that UI volume control slider can change volume up and down | |
- | Select volume at 50% (or thereabout) | |
- | Select [Mute] | Verify that sound is muted while file is playing |
- | | Verify that [Mute] control is displaying the Muted icon |
- | Select [Mute] control again | Verify that sound has reverted back to level selected prior to muting |
- "));
- },
-
- hardware_volume_controls_for_audio_data: {
- mp3:[".mp3", "filename", "mpeg audio layer 3"],
- wav:[".wav", "filename", "waveform audio"],
- ogg:[".ogg", "filename", "container format"],
- vorbis:[".oga", "filename", "audio compression format"],
- speex:[".spx", "filename", "speech audio format"],
- flac:[".flac", "filename", "audio file format"]
- },
-
- hardware_volume_controls_for_audio: function(extension, filename, format)
- {
- // Test meta data
- testTitle = "Multimedia - Audio Player Controls";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Verify hardware volume control of audio playback ";
- testPreconditions = "a test "+extension+" file is available on device";
- testGroups = "BAT, 5.0";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select the desired "+filename+extension+" in playlist and press [Play] | Verify that file plays properly from the start |
- | Press the hardware volume keys to increase the volume to the maximum | Verify that the device now plays at maximum volume |
- | Using hardware volume keys, select volume at about 50% | Verify that the device is now playing at about 50% volume |
- | Using hardware keys, take volume down to 0% | Verify that sound is muted while file is playing |
- | Using hardware keys, increase volume | Verify that sound is once again playing from device |
- "));
- },
-
-
- basic_playlist_controls_for_audio_data: {
- mp3:[".mp3", "filename", "mpeg audio layer 3"],
- wav:[".wav", "filename", "waveform audio"],
- ogg:[".ogg", "filename", "container format"],
- vorbis:[".oga", "filename", "audio compression format"],
- speex:[".spx", "filename", "speech audio format"],
- flac:[".flac", "filename", "audio file format"]
- },
-
- basic_playlist_controls_for_audio: function(extension, filename, format)
- {
- // Test meta data
- testTitle = "Multimedia - Audio Player Controls";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Verify basic API for controlling playlist playback ";
- testPreconditions = "a test "+extension+" file is available on device";
- testGroups = "BAT, 5.0";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select 'Sequential' as the Play Mode | |
- | Select the desired "+filename+extension+" in playlist and press [Play] | Verify that file plays properly from the start |
- | | Verify that the correct track details are displayed in the playlist |
- | Select [Next] | Verify that the next file in the playlist is highlighted |
- | | Verify that the correct audio file is being played from the beginning of the track |
- | | Verify that there are no audible artifacts or undue lag during this change of track |
- | After some time (10-20 sec), select [Prev] icon | Verify that the previous file in the playlist is being played from start |
- | Let file play till end | Verify player continues playing the next file in the playlist window |
- | After some arbitrary time, Select [Stop] | |
- | Select [Next File] control | Verify that next file in playlist is highlighted |
- | | Verify that file is not automatically playing |
- | Select [Prev] control | Verify that previous file in playlist is highlighted |
- | Select file in playlist | Verify that file plays in player. |
- | Select number of files greater than can be displayed without scrolling in to playlist | Verify that list can be scrolled through |
- | Navigate to the last file in the playlist and select [Next] | Verify that no other track is played |
- | Select 'Loop' as the Play Mode | |
- | Select [Next] | Verify that the next song played is the first song in the playlist |
- | While first song is playing select [Prev] | Verify that the last song in the playlist is now playing |
- | Select [current Loop] from Play Mode | Verify that the same song now plays in a loop |
- | | Verify that selecting [Prev] or [Next] has no effect on the tune played |
- | Select [Current Only] in the Play Mode | Verify that only the selected song plays and stops at the end |
- | Select [Shuffle] | Verify that the playlist is now shuffled in a random fashion |
- | Select [Shuffle] again | Verify that the playlist is again shuffled in a random fashion |
- | Select [Reset] | Verify that the playlist is now back to its original order |
- "));
-
- },
-
- seek_controls_for_audio_data: {
- mp3:[".mp3", "filename", "mpeg audio layer 3"],
- wav:[".wav", "filename", "waveform audio"],
- ogg:[".ogg", "filename", "container format"],
- virbis:[".oga", "filename", "audio compression format"],
- speex:[".spx", "filename", "speech audio format"],
- flac:[".flac", "filename", "audio file format"]
- },
-
- seek_controls_for_audio: function(extension, filename, format)
- {
- // Test meta data
- testTitle = "Multimedia - Audio Seek Controls";
- testBinary = "AudioPlayer";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Verify FastForward and Rewind functionality of the API";
- testPreconditions = "a test "+extension+" file is available on device";
- testGroups = "BAT, 5.0";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select [Play] to play the file | |
- | Click on the [Move Forward] control | Verify that file playback has moved forward 5 seconds |
- | Select [Pause] | Verify that the file has paused the playback |
- | Click on the [Move Forward] control | Verify that the file has moved 5 seconds forward, but is still paused |
- | Select [Play] | Verify that file is now playing from new position |
- | Click on the [Move Backwards] control | Verify that file playback has moved back 5 seconds |
- | Select [Pause] | Verify that the file has paused the playback |
- | Click on the [Move Backwards] control | Verify that the file has moved 5 seconds backwards, but is still paused |
- | Select [Play] | Verify that file is now playing from new position |
- "));
- },
-
-
-
- basic_controls_for_streamed_audio_content: function()
- {
-
-//*Note* For this test you need a playlist of streaming media urls. Suggest: http://202.6.74.107:8060/triplej.mp3 ; http://www.abc.net.au/streaming/triplej.asx ; rtsp://media1.abc.net.au/broadcast/triplej.rm ; mms://media3.abc.net.au/triplej ; depending on your platform and backend support.
-
- // Test meta data
- testTitle = "Multimedia - Content Streaming";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Verify playback of streaming audio media";
- testPreconditions = "";
- testGroups = "BAT, 5.0";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | In player application, Select or Tap [Open] | |
- | In [Open File] navigate and highlight test (streaming) playlist file | |
- | Select or Tap on selected file to return to player UI | Verify selected file has populated playlist |
- | Highlight file in playlist by selecting or clicking it | |
- | Select [Play] | Verify that stream is playing on device |
- | Select [Pause] | Verify that stream is paused on device |
- | | Verify that the [Pause] button has changed to a [Play] button |
- | Select [Play] again | Verify that stream is playing on device |
- | Select [Stop] | |
- | Open local mp3 file in to play list and select it to play | Verify that the mp3 plays while the stream name is still present in the playlist |
- | Re-select the stream in the playlist | Verify that the stream starts playing again |
- | Select [Next File] | Verify that the player started playing the next file in the playlist |
- | Select [Previous File] | Verify that the original stream is playing again |
- | During stream playback, have headphones connected to device | Verify that the sound is now playing through the headphones |
- | Invoke a system notification on the device (such as an incoming SMS) | Verify that the device correctly transmits the audible system notification through the devices speakers and then returns to playing the stream through the headphones |
- "));
- },
-
-
- play_sound_effects: function()
- {
-
- // Test meta data
- testTitle = "Multimedia - SoundEffects Playback";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Verify playback of sound effects.";
- testPreconditions = "";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | Verify app launches normally |
- | play soundeffect | Verify that a sound effect is audible |
- | Close application | Verify that the application closes without issues |
- "));
- },
-
- change_volume_of_sound_effects: function()
- {
-
- // Test meta data
- testTitle = "Multimedia - SoundEffects Playback";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Change volume of sound effects.";
- testPreconditions = "";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | Verify app launches normally |
- | Set volume of device to maximum | Verify that sound effect now plays at maximum volume |
- | Set volume of device to about 50% volume | Verify that the sound effect now plays at half volume |
- | Set volume of device to minimum | Verify that there is now no audio from device when sound effect is played |
- | Close application | Verify that the application closes without issues |
- "));
- },
-
-
- play_sound_effects_over_compressed_audio: function()
- {
-
- // Test meta data
- testTitle = "Multimedia - SoundEffects Playback";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimedia/examples/player";
- testGoal = "Verify playback of sound effects over audio track.";
- testPreconditions = "player application to provide background playback";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch player, start playback of playlist | Audio should be heard from the device |
- | Swipe to background player | Audio should still be heard from player |
- | Launch " + testBinary + " | Verify app launches normally |
- | Touch red rectangle | Verify that a sound effect is audible together with the background track |
- | Touch blue rectangle | Verify that a sound effect is audible together with the background track |
- | Touch green rectangle | Verify that a sound effect is audible together with the background track |
- | Touch yellow rectangle | Verify that a sound effect is audible together with the background track |
- | Touch a combination of rectangles in quick succession | Verify that sound effects trigger on each touch and that the background track can still be heard |
- | Close application | Verify that the application closes without issues |
- | | Verify that player is still playing audio tracks |
- "));
- }
-}
diff --git a/tests/systemtests/audio_recording/sys_audio_recording.qtt b/tests/systemtests/audio_recording/sys_audio_recording.qtt
deleted file mode 100644
index 504e0c994..000000000
--- a/tests/systemtests/audio_recording/sys_audio_recording.qtt
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
-
-testcase = {
-
- initTestCase: function()
- {
- },
-
-
- record_an_audio_file: function(extension, filename, format)
- {
- // Test meta data
- testTitle = "Multimedia - Record Audio File";
- testBinary = "player";
- testSource = "$QTDIR/qtmultimediia/examples/audiorecorder";
- testGoal = "Verify that various Audio files can be recorded.";
- testPreconditions = "";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select Default for Input Device, Audio Codec, File Container and Sample rate | |
- | Select Constant Quality for Encoding mode | |
- | Select output file and hit Record | Verify that Audio Level is responding to audio input |
- | Navigate to created audio file and play | Verify that audio is of intended quality |
- | Close application | Verify application closes cleanly |
-
- "));
- }
-}
diff --git a/tests/systemtests/radio/sys_radio.qtt b/tests/systemtests/radio/sys_radio.qtt
deleted file mode 100644
index 81e911c2d..000000000
--- a/tests/systemtests/radio/sys_radio.qtt
+++ /dev/null
@@ -1,286 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
-
-testcase = {
-
-/* Notes
-Ensure radio reception is available in testing area
-Most tier 1 platforms require a radio module
-A device specific headset is required to be connected to device for radio testing
-*/
-
- initTestCase: function()
- {
- },
-
- play_a_radio_transmission: function()
- {
- // Test meta data
- testTitle = "Multimedia - Play Radio Transmission";
- testBinary = "radio";
- testSource ="qt5/qtmultimedia/examples/radio"
- testGoal = "Verify that radio stations can be played.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Device must have a suppported radio module.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | | If not first time running application on image, verify that you are presented with a list of previously scanned radio stations |
- | Select a radio station from the list | Verify that the station is properly selected |
- | Select the power icon next to the station name | Verify that audio from the radio station is now heard through the headphones |
- | Deselect the power icon next to the station name | Verify that audio from the radio station is no longer heard through the headphones |
- | Exit the radio application | Verify that the radio application closes cleanly |
- "));
- },
-
- change_a_radio_station: function()
- {
- // Test meta data
- testTitle = "Multimedia - Radio: Change radio station";
- testBinary = "radio";
- testSource ="qt5/qtmultimedia/examples/radio"
- testGoal = "Verify that radio station can be changed from station playlist.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Wired headphones act as an antenna to the device and must be connected.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select a radio station from the list | Verify that the station is heard through the headphones |
- | If in the visualization view, select the down icon to return to the station selection view | Verify that you are presented with the list of stations |
- | Select a different station | Verify that you are now listening to a different radio station |
- | Deselect the power icon next to the station name | Verify that audio from the radio station is no longer heard through the headphones |
- | Exit the radio application | Verify that the radio application closes cleanly |
- "));
- },
-
- manually_tune_radio_station: function()
- {
- // Test meta data
- testTitle = "Multimedia - Manually tune radio station";
- testBinary = "radio";
- testSource ="qt5/qtmultimedia/examples/radio"
- testGoal = "Verify that user can manually change radio station.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Wired headphones act as an antenna to the device and must be connected.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select a radio station from the list | Verify that the station is heard through the headphones |
- | Select the up icon to be presented to the visualization view | |
- | Select the Plus icon on the menu bar | Verify that you are presented with the frequency dial |
- | Note the current selected frequency | |
- | Select the dot indicator on the frequency dial, and move finger to right | Verify that the frequency on the indicator is now rising and that the audio has gone off the previous station |
- | Keep moving selector until a radio station is heard | Verify that the frequency of the selected station is higher than the originally selected station |
- | | Verify that the station is playing properly |
- | Select to save the selected radio station frequency | Verify that the station is saved in the radio station playlist |
- | Exit the radio application | Verify that the radio application closes cleanly |
- "));
- },
-
- scan_up_and_down: function()
- {
- // Test meta data
- testTitle = "Multimedia - Scan up and down the frequency";
- testBinary = "radio";
- testSource ="qt5/qtmultimedia/examples/radio"
- testGoal = "Verify that radio can scan up and down the FM frequency.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Wired headphones act as an antenna to the device and must be connected.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select a radio station from the list | Verify that the station is heard through the headphones |
- | Select the up icon to be presented to the visualization view | |
- | Select the Plus icon on the menu bar | Verify that you are presented with the frequency dial |
- | Note the current selected frequency | |
- | Select the Scan Up Frequency control | Verify that application has located a new station and that the frequency displayed is of the new station |
- | | Verify that the station is playing properly |
- | Select to save the selected radio station frequency | Verify that the station is saved in the radio station playlist |
- | Select the Scan Down Frequency control | Verify that application has located the original station and that the frequency displayed is of the original station |
- | | Verify that the station is playing properly |
- | Select to save the selected radio station frequency | Verify that the station is saved in the radio station playlist |
- | Exit the radio application | Verify that the radio application closes cleanly |
- "));
- },
-
- scan_for_multiple_radio_stations: function()
- {
- // Test meta data
- testTitle = "Multimedia - Radio: Scan for multiple radio station";
- testBinary = "radio";
- testSource ="qt5/qtmultimedia/examples/radio"
- testGoal = "Verify that radio can scan for multiple stations at once.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Wired headphones act as an antenna to the device and must be connected.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | On the menu, select Find new station | In the Radio Dialog Scanning view, observe the number of stations found |
- | Exit the radio application | Verify that the radio application closes cleanly |
- "));
- },
-
- save_radio_station_list: function()
- {
- // Test meta data
- testTitle = "Multimedia - Radio: Save station playlist";
- testBinary = "radio";
- testSource ="qt5/qtmultimedia/examples/radio"
- testGoal = "Verify that radio can save station playlist.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Wired headphones act as an antenna to the device and must be connected.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select to scan for multiple stations | Verify that multiple stations have been found |
- | Select to save station playlist | |
- | Close application | Verify that the application closes |
- | Reopen " + testBinary + " | Verify that the app launches normally |
- | Go to station playlist | Verify that the stations from the previous search are listed |
- | Select various stations from playlist | Verify that radio station play properly |
- | Close application | Verify that radio application closes properly |
- "));
- },
-
-
- radio_interrupted_by_system_notification: function()
- {
- // Test meta data
- testTitle = "Multimedia - Radio interrupted by System Notification";
- testBinary = "radio";
- testSource ="$QTDIR/qtmultimediakit/examples/Radio"
- testGoal = "Verify that radio can recover audio playback from various system notifications.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Wired headphones act as an antenna to the device and must be connected.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Locate a strong radio station signal by scanning up/down | |
- | While on a strong and audible radio signal, evoke a system notification on device (eg: alarm notification) | Verify that the radio has muted, and after the system notification has been delivered, the radio reverts to outputting the audio signal back through the headphones |
- | While on a strong and audible radio signal, call the device | Verify that the radio has been muted, and stays muted during the duration of the call, and resumes once the call has ended |
- | Close application | Verify that radio application closes properly |
- "));
- },
-
- radio_volume_control: function()
- {
- // Test meta data
- testTitle = "Multimedia - Radio Volume Control";
- testBinary = "radio";
- testGoal = "Verify that the radio volume can be controlled via volume controls.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Wired headphones act as an antenna to the device and must be connected.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Locate a strong radio station signal by scanning up or down the frequency | |
- | While on a strong and audible radio signal, move volume slider to the right | Verify that the audio volume of the radio station is amplified |
- | Move volume slider to the left | Verify that the audio volume is lowered |
- | Move the volume slider all the way to the left | Verify that the sound is now completely muted |
- | Return the volume to a value above 50% and click the Mute/unmute command | Verify that the audio changes from being muted to unmuted and back |
- "));
- },
-
- display_radio_metadata: function()
- {
- // Test meta data
- testTitle = "Multimedia - Display Radio Meta data";
- testBinary = "radio";
- testGoal = "Verify that the radio API can display properly formatted meta data.";
- testPreconditions = "1. Radio reception should be available in the test area.<br>2. Wired headphones act as an antenna to the device and must be connected.";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Locate a strong radio station signal by scanning up or down the frequency | Verify that the Frequency field is populated with the correct frequency for the given radio station |
- | | Verify that the Volume field is displaying the proper volume % |
- | Click on the Mute/Unmute command | Verify that Muted field changes from 'false' to 'true' and back |
- | | If applicable, confirm that the Station Name field is properly displayed |
- | | If applicable, confirm that the Station Id field is properly displayed |
- | | If applicable, confirm that the Program Type field is properly displayed |
- | | If applicable, confirm that the Radio Text field is properly displayed |
- "));
- },
-}
diff --git a/tests/systemtests/still_camera/sys_camera.qtt b/tests/systemtests/still_camera/sys_camera.qtt
deleted file mode 100644
index e2d885f44..000000000
--- a/tests/systemtests/still_camera/sys_camera.qtt
+++ /dev/null
@@ -1,162 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
-
-testcase = {
-
-/* Notes
-*/
- initTestCase: function()
- {
- },
-
- take_a_photo: function()
- {
- // Test meta data
- testTitle = "Multimedia - Still Camera";
- testBinary = "camera";
- testSource = "qt5/qtmultimedia/examples/camera"
- testGoal = "Verify that a photo can be taken with the camera.";
- testPreconditions = "None";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testTitle + " | App launches normally |
- | If device has lens cover, make sure it is open | |
- | Select Image tab to put camera in still mode | Verify the intended scene is reproduced in the preview pane/viewfinder |
- | Select Capture Photo | Verify that image is shown on screen for preview |
- | | Verify that a new file is in the Images folder and accessible with the image viewer |
- | Select given image | Verify that it is a true representation of the intended screen |
- | | Verify the image has no unintended artifacts and noise and is properly formatted |
- "));
- },
-
- change_camera_settings: function()
- {
- // Test meta data
- testTitle = "Multimedia - Still Camera, Change Settings";
- testBinary = "camera";
- testSource = "qt5/qtmultimedia/examples/camera"
- testGoal = "Verify that the camera can change its Still Camera settings successfully.";
- testPreconditions = None";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | If device has lens cover, make sure it is open | |
- | Select the primary camera device from Menu/Devices | Verify the viewfinder is displaying the scene from the main camera device |
- | Select Capture Photo | Verify that a photo was taken and that a preview image is shown on screen |
- | Select the secondary camera device from Menu/Devices | Verify the viewfinder is displaying the scene from the secondary camera device |
- | Select Capture Photo | Verify that a photo was taken from the secondary camera device and that a preview image is shown on screen |
- | Change Exposure Compensation on the slider in the main UI | Verify that the change in value affects the picture quality in expected fashion |
- | Select Settings/File/Settings | Verify that you have an option to change Image Resolution, Image Format and Quality |
- | Change Image Resolution to another supported resolution | Verify that you can take stills with various resolutions |
- | Select Flash always on from the settings menu | Verify that when photo is taken, the flash is always triggered |
- | Select No Flash on from the menu | Verify that despite the lack of natural light, the flash will not trigger when taking photos |
- | Change Image Format to another supported format | Verify that you can take stills with various Image Formats |
- | Select Settings/Device/Secondary Device | Verify that you can see the view from the Front Facing Camera on the viewfinder |
- | | Verify that you can take still images from the Front Facing Camera |
- | Select Settings/File/Exit | Verify that the application has now closed |
-
- "));
- },
-
- take_a_video: function()
- {
- // Test meta data
- testTitle = "Multimedia - Video Camera, Take Video";
- testBinary = "camera";
- testSource = "qt5/qtmultimedia/examples/camera"
- testGoal = "Verify that the camera can capture and save an audio/video file.";
- testPreconditions = "None";
- testGroups = "BAT, 1.2";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select Video icon to put camera in video mode | Verify the intended scene is reproduced in the preview/viewfinder |
- | Select Record Video | Verify that video is shown on screen for preview |
- | Select Stop | Verify that the recording has now stopped |
- | | Verify that the video file is available in the camera preview pane |
- | Select given video | Verify that it is a true representation of the intended screen |
- | | Verify the image has no unintended artifacts and noise and is properly formatted |
- "));
- },
-
- change_video_settings: function()
- {
- // Test meta data
- testApplication = "Multimedia - Video Camera, Change Video Settings";
- testBinary = "camera";
- testSource = "qt5/qtmultimedia/examples/camera"
- testGoal = "Verify that the camera can change its Video settings successfully.";
- testPreconditions = "None";
- testGroups = "BAT";
-
- // Test steps
- prompt(twiki("---+++ " + testTitle + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | If device has lens cover, make sure it is open | |
- | Select Video tab to put camera in video mode | Verify the intended scene is reproduced in the preview/viewfinder |
- | In the Menu Bar, select Settings/Devices | Verify that you can select Primary and Secondary Camera on Device |
- | Select Secondary Camera | Verify that devise is now showing video stream from Front Facing camera |
- | Select Settings/File/Settings | Verify that you now have the option to select Audio Codecs, Resolution Sample Rate, Framerate, Quality, Video Codecs and Container Format |
- | Change Audio Codecs to another supported Codecs | Verify that camera can record |
- | Change Resolution to another supported Resolution | Verify that camera can record |
- | Change Sample Rate to another supported Sample Rate | Verify that camera can record |
- | Change Framerate to another supported Framerate | Verify that camera can record |
- | Change Quality | Verify that camera can record |
- | Change Video Codecs to another supported Video Codecs | Verify that camera can record |
- | Change Container Format | Verify that camera can record |
- | Change Quality | Verify that camera can record |
- "));
- },
-}
diff --git a/tests/systemtests/video_playback/sys_video.qtt b/tests/systemtests/video_playback/sys_video.qtt
deleted file mode 100644
index 0cf375937..000000000
--- a/tests/systemtests/video_playback/sys_video.qtt
+++ /dev/null
@@ -1,180 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Mobility Components.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//TESTED_COMPONENT=src/multimedia
-
-testcase = {
-
-/* Notes
-Ensure 2 test .mp4 files are available on device
-Ensure the device can ping destinations on the network (if applicable)
-Have a video stream url available (e.g. http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4)
-Ensure access to Internet available to device
-Ensure an active SIM card is available on the device. As some video streaming tests are carrier specific, it is recommended in Australia to have access to Testra, Optus and Vodafone SIM's.
-*/
- initTestCase: function()
- {
- },
-
- play_a_video_file_data: {
- mp4:[".mp4", "filename", "mpeg video layer 4"]
- },
-
- play_a_video_file: function(extension, filename, format)
- {
- // Test meta data
- testApplication = "Multimedia - Play Video File";
- testBinary = "player";
- testGoal = "Verify that local, networked and carrier streamed video files can be played. ";
- testPreconditions = "1. A test "+extension+" file is available on device.<br>2. Internet access is enabled on the device.<br>3. Operator SIM cards are available to the device";
- testGroups = "BAT";
-
- // Test steps
- prompt(twiki("---+++ " + testApplication + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select [Open] | |
- | In [Open File] select the "+filename+extension+" file to return to player UI | |
- | Select [Play] to play the file | Verify selected file has populated playlist |
- | Highlight file in playlist by clicking on it | |
- | Select [Play] | Verify that .MP4 file is playing on device |
- "));
- },
-
- basic_controls_for_video_data: {
- mp4:[".mp4", "filename", "mpeg video layer 4"]
- },
-
- basic_controls_for_video: function(extension, filename, format)
- {
- // Test meta data
- testApplication = "Multimedia - Video Controls";
- testBinary = "player";
- testGoal = "Verify the API for basic video playback functionality";
- testPreconditions = "a test "+extension+" file is available on device.";
- testGroups = "BAT";
-
- // Test steps
- prompt(twiki("---+++ " + testApplication + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | Select [Open] | |
- | In [Open File] select the "+filename+extension+" file to return to player UI | |
- | Select [Play] to play the file | |
- | Select [Pause] | Verify that file has paused |
- | | Verify that the [Pause] icon has changed to a [Play] icon |
- | Select [Play] | Verify that the [Play] icon has changed to a [Pause] icon |
- | | Verify that file continues to play from where previously paused. |
- | Select [Next File] | Verify that next file in playlist is playing from start |
- | After some time (10-20 sec) Select [Beginning of File] control | Verify that current file is being played from start |
- | Let arbitrary time pass, then Select [Beginning of File] control twice in quick succession.| Verify that previous file in playlist is playing from start |
- | Select [Stop] | Verify that file has stopped playing |
- | Select [Play] | Verify that file continues to play from start of file. |
- | Let file play till end | Verify player stops at end of file. |
- | Start playing file. | |
- | After arbitrary time, Select [Stop] | |
- | Select [Next File] | Verify that next file in playlist is highlighted |
- | | Verify that file is not automatically playing |
- | Select [Beginning of File] | Verify that previous file in playlist is highlighted |
- | Select file in playlist | Verify that file plays in player. |
- | Select number of files greater than can be displayed without scrolling in to playlist | Verify that list can be scrolled through |
- | With file playing, verify that hardware keys can change volume up and down | |
- | With file playing, verify that UI volume control slider can change volume up and down | |
- | Select volume at 50% (or thereabout) | |
- | Select [Mute] control | Verify that sound is muted while file is playing |
- | | Verify that [Mute] control is displaying the Muted icon |
- | Select [Mute] control again | Verify that sound has reverted back to level selected prior to muting |
- | Drag positional slider to right, then release | Verify that player continues to play file at later part of file |
- | Drag positional slider to left, then release | Verify that player continues to play file at earlier part of file |
- | Start playing file | |
- | Select [FullScreen] control | Verify that video is now playing in Fullscreen mode |
- | When at end of current file | Verify that next file in playlist is being played |
- | When at last file in playlist and at end of file | Verify that fullscreen mode is exited and user returned to player interface and that [FullScreen] is no longer selected. |
- "));
- },
-
- seek_controls_for_video: function()
- {
- // Test meta data
- testApplication = "Multimedia - Video Seek";
- testBinary = "player";
- testGoal = "Verify the API for Fast Forward/Rewind functionality.";
- testPreconditions = "Video files are available for testing on the device.";
- testGroups = "BAT";
-
- // Test steps
- prompt(twiki("---+++ " + testApplication + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | While playing a video file, Select or Tap on Forward control | Verify that playback has moved five seconds forward |
- | Select or Tap Back control | Verify that playback has moved 5 seconds back |
- "));
- },
-
- basic_controls_for_streamed_content: function()
- {
-
-//*Note* For this test you need a playlist of streaming media urls. Suggest: http://202.6.74.107:8060/triplej.mp3 ; http://www.abc.net.au/streaming/triplej.asx ; rtsp://media1.abc.net.au/broadcast/triplej.rm ; mms://media3.abc.net.au/triplej ; depending on your platform and backend support. A stream that I have found that works is: http://chaos.troll.no/~tavestbo/webkit/mediaelement/transformers640.ogg
-
- // Test meta data
- testApplication = "Multimedia - Content Streaming";
- testBinary = "Video player";
- testGoal = "Verify various forms of video streaming.";
- testPreconditions = "For now you need to eddit the .qml file and hardcode the following URI: http://chaos.troll.no/~tavestbo/webkit/mediaelement/transformers640.ogg";
- testGroups = "BAT";
-
- // Test steps
- prompt(twiki("---+++ " + testApplication + "<br><br>
- *Goal:* " + testGoal + "<br>
- *Pre-Requisites:* " + testPreconditions + "<br>
- *Tested Binary:* " + testBinary + "<br>
-
- | *Step* | *Verification* |
- | Launch " + testBinary + " | App launches normally |
- | In Video player application, Select or Tap Play | Verify that the video stream plays in the application |
- | Select [Pause] | Verify that stream is paused on device |
- | | Verify that the [Pause] button has changed to a [Play] button |
- | Select [Play] again | Verify that stream is playing on device |
- | Select [Stop] | |
- | During stream playback, have headphones connected to device | Verify that the sound is now playing through the headphones |
- | Invoke a system notification on the device (such as an incoming SMS) | Verify that the device correctly transmits the audible system notification through the devices speakers and then returns to playing the stream through the headphones |
- "));
- },
-}
diff --git a/util/adt_generate_qt.m b/util/adt_generate_qt.m
index 45b3dcd03..453732273 100644
--- a/util/adt_generate_qt.m
+++ b/util/adt_generate_qt.m
@@ -1,31 +1,6 @@
%{
-****************************************************************************
-**
** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is the build configuration utility of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************
+** SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
%}
% Create mapping matrixes to convert ambisonic to different speaker layouts supported in Qt Multimedia
@@ -43,47 +18,11 @@ function adt_generate_qt()
radius = 2;
[outfile,msg] = fopen("qambisonicdecoderdata_p.h",'w');
- fprintf(outfile, "/****************************************************************************\n");
- fprintf(outfile, "**\n");
- fprintf(outfile, "** Copyright (C) 2016 The Qt Company Ltd.\n");
- fprintf(outfile, "** Contact: https://www.qt.io/licensing/\n");
- fprintf(outfile, "**\n");
- fprintf(outfile, "** This file is part of the Qt Toolkit.\n");
- fprintf(outfile, "**\n");
- fprintf(outfile, "** $QT_BEGIN_LICENSE:LGPL$\n");
- fprintf(outfile, "** Commercial License Usage\n");
- fprintf(outfile, "** Licensees holding valid commercial Qt licenses may use this file in\n");
- fprintf(outfile, "** accordance with the commercial license agreement provided with the\n");
- fprintf(outfile, "** Software or, alternatively, in accordance with the terms contained in\n");
- fprintf(outfile, "** a written agreement between you and The Qt Company. For licensing terms\n");
- fprintf(outfile, "** and conditions see https://www.qt.io/terms-conditions. For further\n");
- fprintf(outfile, "** information use the contact form at https://www.qt.io/contact-us.\n");
- fprintf(outfile, "**\n");
- fprintf(outfile, "** GNU Lesser General Public License Usage\n");
- fprintf(outfile, "** Alternatively, this file may be used under the terms of the GNU Lesser\n");
- fprintf(outfile, "** General Public License version 3 as published by the Free Software\n");
- fprintf(outfile, "** Foundation and appearing in the file LICENSE.LGPL3 included in the\n");
- fprintf(outfile, "** packaging of this file. Please review the following information to\n");
- fprintf(outfile, "** ensure the GNU Lesser General Public License version 3 requirements\n");
- fprintf(outfile, "** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.\n");
- fprintf(outfile, "**\n");
- fprintf(outfile, "** GNU General Public License Usage\n");
- fprintf(outfile, "** Alternatively, this file may be used under the terms of the GNU\n");
- fprintf(outfile, "** General Public License version 2.0 or (at your option) the GNU General\n");
- fprintf(outfile, "** Public license version 3 or any later version approved by the KDE Free\n");
- fprintf(outfile, "** Qt Foundation. The licenses are as published by the Free Software\n");
- fprintf(outfile, "** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3\n");
- fprintf(outfile, "** included in the packaging of this file. Please review the following\n");
- fprintf(outfile, "** information to ensure the GNU General Public License requirements will\n");
- fprintf(outfile, "** be met: https://www.gnu.org/licenses/gpl-2.0.html and\n");
- fprintf(outfile, "** https://www.gnu.org/licenses/gpl-3.0.html.\n");
- fprintf(outfile, "**\n");
- fprintf(outfile, "** $QT_END_LICENSE$\n");
- fprintf(outfile, "**\n");
- fprintf(outfile, "****************************************************************************/\n");
+ fprintf(outfile, "// Copyright (C) 2016 The Qt Company Ltd.\n");
+ fprintf(outfile, "//SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only\n");
fprintf(outfile, "#ifndef QAMBISONICDECODERDATA_P_H\n");
fprintf(outfile, "#define QAMBISONICDECODERDATA_P_H\n\n");
- fprintf(outfile, "#include <qtmultimediaglobal_p.h>\n\n");
+ fprintf(outfile, "#include <qtspatialaudioglobal_p.h>\n\n");
fprintf(outfile, "// W A R N I N G\n");
fprintf(outfile, "// -------------\n");
fprintf(outfile, "//\n");
@@ -98,34 +37,6 @@ function adt_generate_qt()
fprintf(outfile, "\n\n");
fprintf(outfile, "QT_BEGIN_NAMESPACE\n\n");
- % cover top/bottom and back for mono and stereo
- imag_speakers = [0,0,radius; 0,0,-radius; 0,radius,0; 0,-radius,0; -radius,0,0];
-
- % Mono, one speaker up front
- S = ambi_spkr_array(...
- ... % array name
- 'mono', ...
- ... % coordinate codes, unit codes
- ... % Azimuth, Elevation, Radius; Degrees, Degrees, Meters
- 'AER', 'DDM', ...
- ... % speaker name, [azimuth, elevation, radius]
- 'C', [ 0, 0, radius] ...
- );
- createDecoders(S, imag_speakers, outfile);
-
- % Stereo, assume -30 and 30 degree speakers
- S = ambi_spkr_array(...
- 'stereo', ...
- 'AER', 'DDM', ...
- 'L', [ 30, 0, radius], ...
- 'R', [ -30, 0, radius] ...
- );
- createDecoders(S, imag_speakers, outfile);
-
- S.lfeRow = 3;
- S.name = "2dot1";
- createDecoders(S, imag_speakers, outfile);
-
% cover top/bottom for surround
imag_speakers = [0,0,radius; 0,0,-radius];
@@ -197,6 +108,26 @@ function writeLFERow(outfile, m, suffix)
fprintf(outfile, "// LFE\n");
end
+function [n, m] = getnm(l)
+% Computes spherical harmonic degree and order from Ambisonic Channel Number.
+ n = floor(sqrt(l));
+ m = l-n.^2-n;
+end
+
+function channels = normalizeSN3D(channels)
+ for i = 1:columns(channels)
+ [n, m] = getnm(i-1);
+ if (m == 0)
+ factor = 1;
+ else
+ factor = sqrt(2 * factorial(n - abs(m)) / (factorial(n + abs(m))));
+ endif
+ % There's an adjustment factor of 1.5 here, to match the volumes generated by the matrices
+ % here with what we get from a simple stereo decoding. The factor is heuristically determined
+ channels(i) *= factor*1.5;
+ endfor
+end
+
function writeMatrix(outfile, level, S, M, suffix)
m = trimMatrix(M);
hasLFE = isfield(S, "lfeRow");
@@ -208,7 +139,8 @@ function writeMatrix(outfile, level, S, M, suffix)
fprintf(outfile, "// Decoder matrix for %s, ambisonic level %d\n", S.name, level);
fprintf(outfile, "static constexpr float decoderMatrix_%s_%d_%s[%d*%d] = {\n", S.name, level, suffix, r, c);
for i = 1:rows(S.id)
- fprintf(outfile, "%ff, ", m(i, :));
+ channels = normalizeSN3D(m(i, :))
+ fprintf(outfile, "%ff, ", channels);
fprintf(outfile, "// %s\n", S.id(i, 1){1});
if (hasLFE && S.lfeRow == i + 1)
writeLFERow(outfile, m, suffix);
@@ -218,7 +150,8 @@ function writeMatrix(outfile, level, S, M, suffix)
end
function createOneDecoder(S, imag_speakers, outfile, level)
- [D,S,M,C] = ambi_run_allrad(S, level, imag_speakers, [S.name '_' int2str(level)], false, "amb", 1, 3);
+ ambi_order = ambi_channel_definitions_convention(level, 'ambix2011')
+ [D,S,M,C] = ambi_run_allrad(S, ambi_order, imag_speakers, [S.name '_' int2str(level)], false, "amb", 1, 3);
writeMatrix(outfile, level, S, M.lf, "lf");
m = ambi_apply_gamma(M.hf, D.hf_gains, C);
writeMatrix(outfile, level, S, m, "hf");
diff --git a/util/macos_test_audio_config/CMakeLists.txt b/util/macos_test_audio_config/CMakeLists.txt
new file mode 100644
index 000000000..d99987865
--- /dev/null
+++ b/util/macos_test_audio_config/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.5)
+
+project(test_audio_config LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+add_executable(test_audio_config main.cpp)
+
+target_link_libraries(test_audio_config "-framework Coreaudio" "-framework CoreFoundation")
diff --git a/util/macos_test_audio_config/README b/util/macos_test_audio_config/README
new file mode 100644
index 000000000..560d84100
--- /dev/null
+++ b/util/macos_test_audio_config/README
@@ -0,0 +1,25 @@
+This utility tests stability of audio configuration on macOS, and prints it.
+It reads audio config via AudioObjectGetPropertyData many times, checks if it changes, and dumps results.
+It might be useful to check the configuration if specific audio devices are installed on your PC
+and Qt multimedia handles them incorrectly or writes warnings to the console.
+It's a good idea to attach a text file with the utility's output when creating a bug for audio on macOS.
+In order to test an unstable config, set some sufficient testing time and just attach/detach a device.
+
+
+Build:
+
+mkdir build
+cd build
+cmake ..
+make
+
+
+Run:
+
+./test_audio_config
+Running without parameters performs some default testing. Use --help to see customization details.
+
+
+Lazy build, run, and dump the result to file test_audio_config_res.txt:
+
+./compile_and_run.sh
diff --git a/util/macos_test_audio_config/compile_and_run.sh b/util/macos_test_audio_config/compile_and_run.sh
new file mode 100755
index 000000000..8d9afcf43
--- /dev/null
+++ b/util/macos_test_audio_config/compile_and_run.sh
@@ -0,0 +1,19 @@
+#! /bin/sh
+# Copyright (C) 2019 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+BUILD_DIR="__build__"
+OUTPUT_FILE="test_audio_config_res.txt"
+
+mkdir $BUILD_DIR
+cd $BUILD_DIR
+cmake ..
+make -j
+cd ..
+
+rm -f $OUTPUT_FILE
+./$BUILD_DIR/test_audio_config "$@" >> $OUTPUT_FILE
+cat $OUTPUT_FILE
+echo "\nThe result has been written to file $OUTPUT_FILE\n"
+
+rm -rf $BUILD_DIR
diff --git a/util/macos_test_audio_config/main.cpp b/util/macos_test_audio_config/main.cpp
new file mode 100644
index 000000000..4b1fc1702
--- /dev/null
+++ b/util/macos_test_audio_config/main.cpp
@@ -0,0 +1,498 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <iostream>
+#include <vector>
+#include <optional>
+#include <unordered_map>
+#include <sstream>
+#include <thread>
+#include <chrono>
+#include <algorithm>
+
+#include <CoreAudio/AudioHardware.h>
+
+static constexpr std::uint32_t ElementMaster =
+ kAudioObjectPropertyElementMaster; // TODO: use kAudioObjectPropertyElementMain for macOS
+ // versions >= 12.0
+static constexpr std::uint32_t LogOffsetSize = 2;
+
+static constexpr std::chrono::milliseconds DefaultTestingTime(1000);
+static constexpr std::chrono::milliseconds DefaultTestingInterval(0);
+
+struct Flags
+{
+ std::uint32_t value = 0;
+};
+
+struct Code
+{
+ std::uint32_t code = 0;
+};
+
+template<typename T>
+struct Span
+{
+ const T *data = nullptr;
+ size_t size = 0;
+};
+
+struct LogOffset
+{
+ std::uint32_t value = 0;
+
+ LogOffset operator+(std::uint32_t v) const { return LogOffset{ value + v }; }
+};
+
+namespace std {
+ostream &operator<<(ostream &os, const Flags &flags)
+{
+ os << '[';
+ std::uint32_t index = 0;
+ bool first = true;
+ for (auto value = flags.value; value; value >>= 1, ++index) {
+ if ((value & 1) != 0) {
+ if (!first)
+ os << ',';
+ os << index;
+ first = false;
+ }
+ }
+ os << ']';
+
+ return os;
+}
+
+ostream &operator<<(ostream &os, const Code &code)
+{
+ os << '{' << code.code << '|';
+
+ const char *desc = reinterpret_cast<const char *>(&code.code);
+
+ std::copy_n(std::make_reverse_iterator(desc + sizeof(code.code)), sizeof(code.code),
+ std::ostream_iterator<char>(os));
+
+ os << '}';
+
+ return os;
+}
+
+ostream &operator<<(ostream &os, const LogOffset &offset)
+{
+ std::fill_n(std::ostream_iterator<char>(os), offset.value * LogOffsetSize, ' ');
+
+ return os;
+}
+
+template<typename T>
+ostream &operator<<(ostream &os, const Span<T> &span)
+{
+ os << '[';
+ if (span.size)
+ os << span.data[0];
+
+ for (size_t i = 1; i < span.size; ++i)
+ os << ',' << span.data[i];
+
+ os << ']';
+
+ return os;
+}
+
+ostream &operator<<(ostream &os, const CFStringRef &str)
+{
+ const auto originalBuffer = CFStringGetCStringPtr(str, kCFStringEncodingUTF8);
+ if (originalBuffer) {
+ os << originalBuffer;
+ } else {
+ const auto lengthInUtf16 = CFStringGetLength(str);
+ const auto maxLengthInUtf8 =
+ CFStringGetMaximumSizeForEncoding(lengthInUtf16, kCFStringEncodingUTF8) + 1;
+ std::vector<char> localBuffer(maxLengthInUtf8);
+
+ if (CFStringGetCString(str, localBuffer.data(), maxLengthInUtf8, maxLengthInUtf8))
+ os << localBuffer.data();
+ else
+ os << "{empty}";
+ }
+
+ return os;
+}
+
+} // namespace std
+
+template<typename T = char>
+static std::optional<std::vector<T>>
+getAudioData(std::ostream &os, const LogOffset &offset, AudioObjectID inObjectID,
+ const AudioObjectPropertyAddress &inAddress, size_t minDataSize = 0)
+{
+ static_assert(std::is_trivial_v<T>, "Trivial type is expected");
+
+ UInt32 propSize = 0;
+ const auto res = AudioObjectGetPropertyDataSize(inObjectID, &inAddress, 0, nullptr, &propSize);
+
+ if (res == noErr) {
+ if (propSize / sizeof(T) < minDataSize) {
+ os << offset << "Data size is too low: actual " << propSize << "B vs expected " << minDataSize * sizeof(T) << "B\n";
+ return {};
+ }
+
+ std::vector<T> data(propSize / sizeof(T));
+
+ if (data.size() * sizeof(T) != propSize)
+ os << offset << "Probably, wrong data size: " << propSize << ", Element size: " << sizeof(T) << '\n';
+
+ const auto res = AudioObjectGetPropertyData(inObjectID, &inAddress, 0, nullptr, &propSize,
+ data.data());
+
+ if (res == noErr)
+ return { std::move(data) };
+ else
+ os << offset << "AudioObjectGetPropertyData failed, Err: "
+ << Code{ static_cast<std::uint32_t>(res) } << '\n';
+ } else {
+ os << offset << "AudioObjectGetPropertyDataSize failed, Err: "
+ << Code{ static_cast<std::uint32_t>(res) } << '\n';
+ }
+
+ return {};
+}
+
+template<typename T = char>
+static std::optional<T> getAudioObject(std::ostream &os, const LogOffset &offset,
+ AudioObjectID inObjectID,
+ const AudioObjectPropertyAddress &inAddress)
+{
+ if (auto data = getAudioData<T>(os, offset, inObjectID, inAddress, 1)) {
+ if (data->size() > 1)
+ os << offset << "Warn: unexpected data size: " << data->size() << '\n';
+
+ return { data->front() };
+ }
+
+ return {};
+}
+
+static void dumpFormats(std::ostream &os, const LogOffset &offset, AudioDeviceID id,
+ std::uint32_t scope)
+{
+ os << offset << "Formats:\n";
+
+ const AudioObjectPropertyAddress audioDevicePropertyStreamsAddress{ kAudioDevicePropertyStreams,
+ scope, ElementMaster };
+
+ if (auto streamIDs = getAudioData<AudioStreamID>(os, offset + 1, id,
+ audioDevicePropertyStreamsAddress)) {
+ for (auto &streamID : *streamIDs) {
+ os << offset + 1 << "Stream id: " << streamID << '\n';
+
+ auto dumpCurrentFormat = [&](std::uint32_t selector) {
+ const AudioObjectPropertyAddress propertyAddress{ selector, scope, ElementMaster };
+
+ if (auto format = getAudioObject<AudioStreamBasicDescription>(
+ os, offset + 3, streamID, propertyAddress)) {
+
+ os << offset + 3 << "mFormatID: " << format->mFormatID << '\n';
+ os << offset + 3 << "mSampleRate: " << format->mSampleRate << '\n';
+ os << offset + 3 << "mFormatFlags: " << Flags{ format->mFormatFlags } << '\n';
+ }
+ };
+
+ auto dumpAvailableFormats = [&](std::uint32_t selector) {
+ const AudioObjectPropertyAddress propertyAddress{ selector, scope, ElementMaster };
+
+ if (auto descriptions = getAudioData<AudioStreamRangedDescription>(
+ os, offset + 3, streamID, propertyAddress)) {
+
+ size_t index = 0;
+ for (const AudioStreamRangedDescription &desc : *descriptions) {
+
+ const auto &format = desc.mFormat;
+ os << offset + 3 << "mFormatID: " << format.mFormatID << " (Index " << index
+ << ")\n";
+
+ os << offset + 4 << "mSampleRateRange: [" << desc.mSampleRateRange.mMinimum
+ << "; " << desc.mSampleRateRange.mMaximum << "]\n"
+ << offset + 4 << "mSampleRate: " << format.mSampleRate << '\n'
+ << offset + 4 << "mFormatFlags: " << Flags{ format.mFormatFlags } << '\n'
+ << offset + 4 << "mBytesPerPacket: " << format.mBytesPerPacket << '\n'
+ << offset + 4 << "mFramesPerPacket: " << format.mFramesPerPacket << '\n'
+ << offset + 4 << "mBytesPerFrame: " << format.mBytesPerFrame << '\n'
+ << offset + 4 << "mChannelsPerFrame: " << format.mChannelsPerFrame << '\n'
+ << offset + 4 << "mBitsPerChannel: " << format.mBitsPerChannel << '\n';
+
+ ++index;
+ }
+ }
+ };
+
+ os << offset + 2 << "Preffered physical format "
+ << Code{ kAudioStreamPropertyPhysicalFormat } << ":\n";
+ dumpCurrentFormat(kAudioStreamPropertyPhysicalFormat);
+
+ os << offset + 2 << "Available physical formats "
+ << Code{ kAudioStreamPropertyAvailablePhysicalFormats } << ":\n";
+ dumpAvailableFormats(kAudioStreamPropertyAvailablePhysicalFormats);
+
+ os << offset + 2 << "Preffered virtual format "
+ << Code{ kAudioStreamPropertyVirtualFormat } << ":\n";
+ dumpCurrentFormat(kAudioStreamPropertyVirtualFormat);
+
+ os << offset + 2 << "Available virtual formats "
+ << Code{ kAudioStreamPropertyAvailableVirtualFormats } << ":\n";
+ dumpAvailableFormats(kAudioStreamPropertyAvailableVirtualFormats);
+ }
+ }
+}
+
+static void dumpChannelsLayout(std::ostream &os, const LogOffset &offset, AudioDeviceID id,
+ std::uint32_t scope)
+{
+ os << offset << "Channels Layout " << Code{ kAudioDevicePropertyPreferredChannelLayout }
+ << ":\n";
+
+ const AudioObjectPropertyAddress audioDeviceChannelLayoutPropertyAddress{
+ kAudioDevicePropertyPreferredChannelLayout, scope, ElementMaster
+ };
+
+ if (auto data = getAudioData(os, offset + 1, id, audioDeviceChannelLayoutPropertyAddress,
+ sizeof(AudioChannelLayout))) {
+ const AudioChannelLayout &layout = *reinterpret_cast<AudioChannelLayout *>(data->data());
+
+ os << offset + 1 << "mChannelLayoutTag: " << layout.mChannelLayoutTag << "\n"
+ << offset + 1 << "mChannelBitmap: " << Flags{ layout.mChannelBitmap } << "\n"
+ << offset + 1 << "mNumberChannelDescriptions: " << layout.mNumberChannelDescriptions << "\n"
+ << offset + 1 << "ChannelDescriptions:\n";
+
+ for (UInt32 i = 0; i < layout.mNumberChannelDescriptions; ++i) {
+ const auto &desc = layout.mChannelDescriptions[i];
+ os << offset + 2 << "Channel " << i << ":\n";
+
+ os << offset + 3 << "mChannelLabel: " << desc.mChannelLabel;
+ if (desc.mChannelLabel == 0xFFFFFFFF)
+ os << " (unknown)";
+ os << '\n';
+
+ os << offset + 3 << "mChannelFlags: " << Flags{ desc.mChannelFlags } << '\n'
+ << offset + 3 << "mCoordinates: " << Span<Float32>{ desc.mCoordinates, 3 } << '\n';
+ }
+ }
+}
+
+static void dumpBasicDescription(std::ostream &os, const LogOffset &offset, AudioDeviceID id,
+ std::uint32_t scope)
+{
+ os << offset << "Basic Description " << Code{ kAudioDevicePropertyStreamFormat } << ":\n";
+
+ const AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress{
+ kAudioDevicePropertyStreamFormat, scope, ElementMaster
+ };
+
+ if (auto basicDescr = getAudioObject<AudioStreamBasicDescription>(
+ os, offset + 1, id, audioDeviceStreamFormatPropertyAddress)) {
+ os << offset + 1 << "mSampleRate: " << basicDescr->mSampleRate << "\n"
+ << offset + 1 << "mFormatID: " << basicDescr->mFormatID << "\n"
+ << offset + 1 << "mFormatFlags: " << Flags{ basicDescr->mFormatFlags } << "\n"
+ << offset + 1 << "mBytesPerPacket: " << basicDescr->mBytesPerPacket << "\n"
+ << offset + 1 << "mFramesPerPacket: " << basicDescr->mFramesPerPacket << "\n"
+ << offset + 1 << "mBytesPerFrame: " << basicDescr->mBytesPerFrame << "\n"
+ << offset + 1 << "mChannelsPerFrame: " << basicDescr->mChannelsPerFrame << "\n"
+ << offset + 1 << "mBitsPerChannel: " << basicDescr->mBitsPerChannel << "\n";
+ }
+}
+
+static void dumpGeneralDeviceInfo(std::ostream &os, const LogOffset &offset, AudioDeviceID id,
+ std::uint32_t scope)
+{
+ os << offset << "General Device Info:\n";
+
+ auto dumpString = [&](const char *name, std::uint32_t selector) {
+ os << offset + 1 << name << " " << Code{ selector } << ":\n";
+ const AudioObjectPropertyAddress propertyAddress{ selector, scope, ElementMaster };
+
+ if (auto str = getAudioObject<CFStringRef>(os, offset + 2, id, propertyAddress)) {
+ os << offset + 2 << *str << '\n';
+ CFRelease(*str);
+ }
+ };
+
+ auto dumpBool = [&](const char *name, std::uint32_t selector) {
+ os << offset + 1 << name << ' ' << Code{ selector } << ":\n";
+ const AudioObjectPropertyAddress propertyAddress{ selector, scope, ElementMaster };
+
+ if (auto value = getAudioObject<UInt32>(os, offset + 2, id, propertyAddress))
+ os << offset + 2 << std::boolalpha << static_cast<bool>(*value) << '\n';
+ };
+
+ dumpString("Name", kAudioObjectPropertyName);
+ dumpString("Manufacturer", kAudioObjectPropertyManufacturer);
+ dumpString("Element name", kAudioObjectPropertyElementName);
+ dumpString("Model name", kAudioObjectPropertyModelName);
+
+ dumpBool("Device is alive", kAudioDevicePropertyDeviceIsAlive);
+ dumpBool("Device is running", kAudioDevicePropertyDeviceIsRunning);
+ dumpBool("Can be default device", kAudioDevicePropertyDeviceCanBeDefaultDevice);
+ dumpBool("Can be default system device", kAudioDevicePropertyDeviceCanBeDefaultSystemDevice);
+ dumpBool("Device property is hidden", kAudioDevicePropertyIsHidden);
+
+ {
+ const AudioObjectPropertyAddress propertyAddress{
+ kAudioDevicePropertyPreferredChannelsForStereo, scope, ElementMaster
+ };
+
+ os << offset + 1 << "Preffered channels for stereo "
+ << Code{ kAudioDevicePropertyPreferredChannelsForStereo } << ":\n";
+ if (auto data = getAudioData<UInt32>(os, offset + 2, id, propertyAddress, 2))
+ os << offset + 2 << Span<UInt32>{ data->data(), 2 } << '\n';
+ }
+}
+
+static void dumpAvailableAudioDevices(std::ostream &os, const LogOffset &offset,
+ std::uint32_t scope)
+{
+ os << offset << "Dump devices " << Code{ kAudioHardwarePropertyDevices }
+ << ", scope: " << Code{ scope } << "\n";
+ const AudioObjectPropertyAddress audioDevicesPropertyAddress{ kAudioHardwarePropertyDevices,
+ scope, ElementMaster };
+
+ if (auto devices = getAudioData<AudioDeviceID>(os, offset + 1, kAudioObjectSystemObject,
+ audioDevicesPropertyAddress)) {
+ size_t index = 0;
+ for (auto id : *devices) {
+ os << offset + 2 << "ID: " << id << " (Index " << index << ")\n";
+
+ dumpGeneralDeviceInfo(os, offset + 3, id, scope);
+
+ dumpBasicDescription(os, offset + 3, id, scope);
+
+ dumpChannelsLayout(os, offset + 3, id, scope);
+
+ dumpFormats(os, offset + 3, id, scope);
+
+ ++index;
+ }
+ }
+}
+
+static void dumpAvailableAudioDevices(std::ostream &os, const LogOffset &offset)
+{
+ for (auto scope : { kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput }) {
+ dumpAvailableAudioDevices(os, offset, scope);
+ os << offset << "\n";
+ }
+}
+
+static void testStability(const std::chrono::milliseconds &time,
+ const std::chrono::milliseconds &interval)
+{
+ std::cout << "Start testing audio config...\n" << std::endl;
+
+ std::unordered_map<std::string, std::uint32_t> results;
+ std::uint32_t counter = 0;
+
+ const auto end = std::chrono::system_clock::now() + time;
+
+ while (true) {
+ std::ostringstream stream;
+
+ dumpAvailableAudioDevices(stream, {});
+
+ ++counter;
+ ++results[stream.str()];
+
+ if (std::chrono::system_clock::now() + interval >= end)
+ break;
+
+ std::this_thread::sleep_for(interval);
+ }
+
+ std::cout << "Audio config has been tested for " << time.count() << "ms\n" <<
+ "Set --time %your time% in order to change the testing time.\n" <<
+ "The config has been taken " << counter << " times\n" <<
+ "------------------------------------------------------------\n";
+
+ std::cout << std::endl;
+
+ using Result = decltype(results)::value_type;
+ std::vector<std::reference_wrapper<Result>> resultRefs(results.begin(), results.end());
+ std::sort(resultRefs.begin(), resultRefs.end(),
+ [](const Result &a, const Result &b) { return a.second < b.second; });
+
+ for (size_t i = 0; i < resultRefs.size(); ++i) {
+ const Result &res = resultRefs[i];
+ std::cout << "Result N" << i + 1 << "; Occurs: " << res.second << '/' << counter << '\n'
+ << res.first;
+ }
+
+ if (results.size() == 1)
+ std::cout << "The config seems to be stable (" << counter << " times)\n";
+ else
+ std::cout << "The config is unstable: " << results.size() << " different results\n";
+
+ std::cout << "\nTesting done!" << std::endl;
+}
+
+static void printHelp()
+{
+ // clang-format off
+ std::cout << "This utility tests stability of audio configuration on macOS, and prints it.\n"
+ "It reads audio config via AudioObjectGetPropertyData many times, checks if it changes and dumps results.\n"
+ "It might be useful to check the configuration if specific audio devices are installed on your PC\n"
+ "and Qt multimedia handles them incorrectly or writes warnings to the console.\n"
+ "It's a good idea to attach a text file with the utility's output when creating a bug for audio on macOS.\n"
+ "In order to test an unstable config, set some sufficient testing time and just attach/detach a device.\n"
+ "Options:\n"
+ " --time Common time of testing in ms. Defaults to " << DefaultTestingTime.count() << ".\n"
+ " Set --time 0 for simple duming of the audio config.\n"
+ " --interval Interval in ms between reading of audio config. Defaults to " << DefaultTestingInterval.count() << ".\n"
+ " --help Show help.";
+ // clang-format on
+
+ std::cout << std::endl;
+}
+
+int main(int argc, char *argv[])
+{
+ std::chrono::milliseconds testingTime = DefaultTestingTime;
+ std::chrono::milliseconds testingInterval = DefaultTestingInterval;
+
+ for (int i = 1; i < argc; ++i) {
+ auto getIntValue = [&]() -> std::optional<int> {
+ if (i + 1 < argc) {
+ char *end = argv[i + 1];
+ const auto val = strtol(argv[i + 1], &end, 10);
+ ++i;
+ if (*end == '\0')
+ return { val };
+ else
+ std::cout << "Cannot read value " << argv[i + 1] << std::endl;
+ } else {
+ std::cout << "Cannot read value" << std::endl;
+ }
+
+ return {};
+ };
+
+ if (strcmp(argv[i], "--time") == 0) {
+ if (auto time = getIntValue())
+ testingTime = std::chrono::milliseconds(*time);
+ else
+ return 1;
+ } else if (strcmp(argv[i], "--interval") == 0) {
+ if (auto interval = getIntValue())
+ testingInterval = std::chrono::milliseconds(*interval);
+ else
+ return 1;
+ } else if (strcmp(argv[i], "--help") == 0) {
+ printHelp();
+ return 0;
+ } else {
+ std::cout << "Wrong option " << argv[i] << std::endl;
+ return 1;
+ }
+ }
+
+ testStability(testingTime, testingInterval);
+
+ return 0;
+}